Unsolved Using AddCustomGui for primitive controls

Hello;
using AddCustomGui for complex controls like Quicktab or Linkbox seems easy enough, as you get a specific class derived from BaseCustomGui. However, in the description for GeDialog::AddCustomGui there are numerous "Symbol ID"s listed that do not have a specific class but result in a BaseCustomGui being returned: CUSTOMGUI_REAL, CUSTOMGUI_STATICTEXT, CUSTOMGUI_TIME, etc.

Mostly they seem to correspond with certain primitives (which already have their own dedicated Add functions, e.g. CUSTOMGUI_REALSLIDER and AddEditSlider), but sometimes there is no separate Add function, as e.g. for CUSTOMGUI_PROGRESSBAR or CUSTOMGUI_MATRIX.

I have no idea how to work with these.

For complex controls, I just create the GUI:

self.customcontrol = self.AddCustomGui(ID_CUSTOM, c4d.CUSTOMGUI_QUICKTAB, 
name="A quick tab", 
flags = c4d.BFH_SCALEFIT | c4d.BFV_FIT, minw=120, minh=30, 
customdata=bc)

and work from there with the returned class. No issues here.

If I try the same for a primitive control like CUSTOMGUI_REALSLIDER, no GUI appears in the dialog at all (the return value is BaseCustomGui, not None, so the call did not fail at least). I cannot find any documentation on parameters to be set through the passed BaseContainer, if that's the issue.

You may say "use AddEditSlider" but there is no AddTime or AddProgressBar so I would expect AddCustomGui to work.

I seem to overlook something important. What do these "Symbol ID"s document, and how do I use them?

Hi,

you have function to retrieve the custom datatype corresponding to the custom gui you are adding. But not all gadgets works the same.

for CUSTOMGUI_PROGRESSBAR , you must send a message. DateTime you can either use the returned gadget or use FindCustomGui to update its data. CUSTOMGUI_QUICKTAB you can define the tabs in the CreateLayout function etc etc.

as FindCustomData says:

The method returns a BaseCustomGui when there is no class available for the custom GUI.
This allows to interact with any custom GUI, calling for instance BaseCustomGui.GetData()/SetData().

You can find this manual about the DateTime control/datatype.

Some gadgets could be restricted to Description and looks bad or not work in a GeDialog.

I wrote a plugin to test different cases and show how to use some gadgets.


import c4d
import collections
import os
from datetime import datetime

# Be sure to use a unique ID obtained from www.plugincafe.com
PLUGIN_ID = 9025250



class MemoryViewerDialog(c4d.gui.GeDialog):

    def CreateLayout(self):
        """
        This Method is called automatically when Cinema 4D Create the Layout (display) of the Dialog.
        """
        self.GroupBegin(0, c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, 1)
        self.dateTimeControl = self.AddCustomGui(1000, c4d.DATETIME_GUI, name="date time", 
        flags = c4d.BFH_SCALEFIT | c4d.BFV_FIT, minw=120, minh=30)

        quickTabGadgetBC = c4d.BaseContainer()
        quickTabGadgetBC.SetBool(c4d.QUICKTAB_SHOWSINGLE, True)
        quickTabGadgetBC.SetBool(c4d.QUICKTAB_NOMULTISELECT, False)
        self.quickTabGadget = self.AddCustomGui(1001, c4d.CUSTOMGUI_QUICKTAB, name="quick tab", 
        flags = c4d.BFH_SCALEFIT | c4d.BFV_FIT, minw=120, minh=30, customdata = quickTabGadgetBC)
        self.quickTabGadget.AppendString(0, "First Tab", False)
        self.quickTabGadget.AppendString(0, "Second Tab Selected", True)

        self.AddButton(2000, c4d.BFH_SCALEFIT, 0, 0, "click me")
        self.progressBarGadget = self.AddCustomGui(1002, c4d.CUSTOMGUI_PROGRESSBAR, name="", 
        flags = c4d.BFH_SCALEFIT | c4d.BFV_FIT, minw=120, minh=30)
        self.GroupEnd()

        return True

    def Command(self, id, msg):
        print (id)
        if id == 2000:
            timeDataType = self.FindCustomGui(1000, c4d.DATETIME_GUI)
            print("this is timedata", timeDataType, self.dateTimeControl )
            # Parse the time string
            dt = datetime.strptime('16.07.2011 03:37:12',"%d.%m.%Y %H:%M:%S")
            dtd = c4d.DateTimeData()
            # Fills the Data object with the DateTime object
            dtd.SetDateTime(dt)
            timeDataType.SetDateTime(dtd, True, True)

            progressMessage = c4d.BaseContainer(c4d.BFM_SETSTATUSBAR)
            progressMessage.SetData(c4d.BFM_STATUSBAR_TXT , "this is displayed in the status bar")
            self.SendMessage(1002, progressMessage)
            
            
        return True
   

class MemoryViewerCommandData(c4d.plugins.CommandData):

    dialog = None

    def Execute(self, doc):
        """
        Called when the user Execute the command (CallCommand or a clicks on the Command from the plugin menu)
        :param doc: the current active document
        :type doc: c4d.documents.BaseDocument
        :return: True if the command success
        """
        # Creates the dialog if its not already exists
        if self.dialog is None:
            self.dialog = MemoryViewerDialog()

        # Opens the dialog
        return self.dialog.Open(dlgtype=c4d.DLG_TYPE_ASYNC, pluginid=PLUGIN_ID, defaulth=400, defaultw=400)

    def RestoreLayout(self, sec_ref):
        """
        Used to restore an asynchronous dialog that has been placed in the users layout.
        :param sec_ref: The data that needs to be passed to the dlg (almost no use of it).
        :type sec_ref: PyCObject
        :return: True if the restore success
        """
        # Creates the dialog if its not already exists
        if self.dialog is None:
            self.dialog = MemoryViewerDialog()

        # Restores the layout
        return self.dialog.Restore(pluginid=PLUGIN_ID, secret=sec_ref)


if __name__ == "__main__":
    # Retrieves the icon path
    directory, _ = os.path.split(__file__)
    fn = os.path.join(directory, "res", "mviewer.tif")

    # Creates a BaseBitmap


    # Registers the Command plugin
    c4d.plugins.RegisterCommandPlugin(id=PLUGIN_ID,
                                      str="Py-MemoryViewer 2",
                                      help="Show the current mem usage of Cinema 4D.",
                                      info=0,
                                      dat=MemoryViewerCommandData(),
                                      icon=None)

Cheers,
Manuel

MAXON SDK Specialist

MAXON Registered Developer

@manuel Thanks for the notes, there seems to be a lot that requires additional explanation beyond the API description. I may ask more specific questions later...

Regarding the DateTime, I got a weird error when I try to show the extended GUI: the clock and calendar fields get duplicated:

20221124__05__Screenshot_DateTime_04.jpg

This happens upon calling

self.dateTimeControl.SetLayoutMode(c4d.LAYOUTMODE_MAXIMIZED)

or if you like the full script,

import c4d
from c4d import gui
from datetime import datetime

ID_BUTTON_CLOSE = 1001
ID_DATETIME = 1002

class CustomDialog(c4d.gui.GeDialog):

    def __init__(self):
        self.customcontrol = None

    def CreateLayout(self):
        self.SetTitle("Custom Control Test")
        self.GroupBorderSpace(10, 10, 10, 10)

        if self.GroupBegin(id=0, flags=c4d.BFH_SCALEFIT, cols=1, title="", groupflags=0):

            bc = c4d.BaseContainer()
            bc[c4d.DATETIME_TIME_CONTROL] = True
            bc[c4d.DATETIME_DATE_CONTROL] = True

            self.dateTimeControl = self.AddCustomGui(ID_DATETIME, c4d.DATETIME_GUI,
                    name="DateTime",
                    flags = c4d.BFH_SCALEFIT | c4d.BFV_FIT,
                    minw=10, minh=10, customdata=bc)
            self.dateTimeControl.SetLayoutMode(c4d.LAYOUTMODE_MAXIMIZED)
            dt = datetime.strptime('16.07.2011 03:37:12',"%d.%m.%Y %H:%M:%S")
            dtd = c4d.DateTimeData()
            dtd.SetDateTime(dt)
            self.dateTimeControl.SetDateTime(dtd)
            self.GroupEnd()

        if self.GroupBegin(id=0, flags=c4d.BFH_CENTER, cols=2, title="", groupflags=0):
            self.GroupBorderSpace(0, 20, 0, 0)
            self.AddButton(ID_BUTTON_CLOSE, c4d.BFH_SCALEFIT, name="Close")
            self.GroupEnd()

        return True

    def Command(self, messageId, bc):
        if messageId == ID_BUTTON_CLOSE:
            self.Close()
        return True

def main():

    dlg = CustomDialog()
    dlg.Open(dlgtype=c4d.DLG_TYPE_MODAL_RESIZEABLE)
    dt = dlg.dateTimeControl.GetDateTime().GetDateTime()
    print (dt.year, dt.month, dt.day)
    print (dt.hour, dt.minute, dt.second)

if __name__=='__main__':
    main()

(and no, it doesn't work in a CommandData plugin either...)
I am still on R23.1 and have no current access to more recent versions, so this may actually be an error that has already been corrected, and it's no longer a valid question...