Add Commands to ShowPopupDialog



  • Hi,

    Is there a way I can add commands to ShowPopupDialog?
    In the documentation, it offers commands but built-in ones such as c4d.Ocube or c4d.IDM_COPY.

    I want my own functions such as menu.InsData(some_function(), 'some_function'). But when I do so, it gives me an error.

    It requires that the first parameter should be an integer(i.e. ID). Is there a way I can convert a function to an integer? Or am I approaching this entirely wrong?

    Thank you for looking at my problem



  • Hi @bentraje this is not possible out of the box, you have to register a CommandData and the Execute method of this CommandData will be executed.

    If you don't want to go to this way and keep using only a script, you can assign a unique ID for each menu entry, check for the clicked ID (contained in the return value of ShowPopupDialog) and then call the correct function.

    Cheers,
    Maxime.



  • @m_adam

    Thanks for the response.

    RE: you have to register a CommandData and the Execute method of this CommandData will be executed.
    By register, I was thinking of the line c4d.plugins.RegisterCommandPlugin which is included for every single .pyp file. Does this mean I have to create several .pyp files for each separate command then map them in a universal .pyp file?

    RE: check for the clicked ID (contained in the return value of ShowPopupDialog) and then call the correct function.

    I have a working code below.
    It works when you click on the Menu 1 it prints You selected Menu 1. in the console as a test.

    My concern is I have it under the Message() and not under the Command() since it does not seem to recognize the ShowPopupDialog ids (or sub-ids).

    Is this okay?

    import c4d
    
    class TestDialog(c4d.gui.GeDialog):
        def __init__(self):
            self._showPopup = False, 0, 0
    
        def CreateLayout(self):
            self.GroupBegin(10002, c4d.BFH_SCALEFIT| c4d.BFH_SCALEFIT,  initw=100, inith=100)
            self.AddButton(id=20002, flags=c4d.BFH_CENTER, initw=50, inith=50, name="Button Me")
            self.GroupEnd()
            return True
        
        def IsPositionOnGadget(self, gadgetId, x, y):
            # Be sure that the windows is opened,
            # in our case since we call it in BFM_INTERACTSTART it's ok
            buttonData = self.GetItemDim(gadgetId)
            
            if not buttonData["x"] < x < buttonData["x"] + buttonData["w"]:
                return False
    
            if not buttonData["y"] < y < buttonData["y"] + buttonData["h"]:
                return False
    
            return True
    
        def IsPositionInGuiArea(self, x, y):
            # Check click is within the GeDialog
            if not 0 < x < self._x:
                return False
    
            if not 0 < y < self._y:
                return False
    
            return True
    
        def Message(self, msg, result):
            if msg.GetId() == c4d.BFM_ADJUSTSIZE:
              self._x = msg[3] # Retrieve Y size of the GeDialog
              self._y = msg[4] # Retrieve Y size of the GeDialog
    
            # We are on the main thread here
            elif msg.GetId() == c4d.BFM_INTERACTSTART:
                c4d.StopAllThreads()
    
                state = c4d.BaseContainer()
                self.GetInputState(c4d.BFM_INPUT_MOUSE, c4d.BFM_INPUT_MOUSERIGHT, state)
    
                if state.GetInt32(c4d.BFM_INPUT_VALUE) == True:
    
                    x = state.GetInt32(c4d.BFM_INPUT_X)
                    y = state.GetInt32(c4d.BFM_INPUT_Y)
                    g2l  = self.Global2Local() 
                    x += g2l['x']  
                    y += g2l['y']
                    
                    if self.IsPositionOnGadget(gadgetId=20002, x=x, y=y):
                        IDM_MENU1 = c4d.FIRST_POPUP_ID
                        IDM_MENU2 = c4d.FIRST_POPUP_ID + 1
                        IDM_MENU3 = c4d.FIRST_POPUP_ID + 2
    
                        menu = c4d.BaseContainer()
                        menu.InsData(IDM_MENU1, 'Menu 1')
                        menu.InsData(IDM_MENU2, 'Menu 2')
                        menu.InsData(IDM_MENU3, 'Menu 3')
    
                        l2s = self.Local2Screen()
                        #print str(x+l2s['x']) + " :: " + str(y+l2s['y'])
                        self.KillEvents()
                        self.res = c4d.gui.ShowPopupDialog(cd=self, bc=menu, x=x+l2s['x'], y=y+l2s['y'])
                        
                        if self.res == 900000:
                            print "You selected Menu 1"
                        elif self.res == 900001:
                            print "You selected Menu 2"
                        elif self.res == 900002:
                            print "You selected Menu 3"
                        else:
                            return
                        return True
    
            return c4d.gui.GeDialog.Message(self, msg, result)
        
        def Command(self, id, msg):
            
        
            return True
        
    if __name__ == '__main__':
        global dlg
        dlg = TestDialog()
        dlg.Open(dlgtype=c4d.DLG_TYPE_ASYNC)
    


  • @bentraje
    Hope this will help you out its a small demo I did.
    Quick video on how it look inside Cinema 4d. Here
    Download Demo Test : GitHub Link
    Code:

    # Imports
    import os
    import sys
    import c4d
    from c4d import plugins, gui, bitmaps, documents, storage, utils
    from c4d.gui import GeDialog as WindowDialog
    from c4d.plugins import CommandData, TagData, ObjectData
    
    iPath = os.path.join(os.path.dirname(__file__), 'res', "yourIcon.png")
    
    def CustomCommandPopupMenu():
        """" Popup Commands Ids Only Menu """
        menu = c4d.BaseContainer()
        menu.InsData(0, '') # Append separator
        menu.InsData(PLUG_2['ID'], "CMD")           # eg: menu.InsData(00000000, "CMD")
        menu.InsData(0, '') # Append separator
        menu.InsData(PLUG_3['ID'], "CMD")
        result = gui.ShowPopupDialog(cd=None, bc=menu, x=c4d.MOUSEPOS, y=c4d.MOUSEPOS)
        return True    
    
    class CMDTool(CommandData):
    
        def __init__(self, CMD):
            super(CMDTool, self).__init__()
            self.CMDTool = CMD
    
        def Init(self, op):
            return True
        def Message(self, type, data):
            return True
      
        def Execute(self, doc):
    
            if self.CMDTool == "PCT":
                CustomCommandPopupMenu()
    
            if self.CMDTool == "PCT1":
                gui.MessageDialog("PlugCafeTool2")
    
            if self.CMDTool == "PCT2":
                gui.MessageDialog("PlugCafeTool2")
    
        def RestoreLayout(self, sec_ref):
            return True
        def ExecuteOptionID(self, doc, plugid, subid):
            gui.MessageDialog("PlugCafeTool2 Options")
            return True
    
    # ----------------------------------------------------
    #               Plugin Registration
    # ----------------------------------------------------
    #  // Plugin Flags Tpyes  //
    class PluginFlags:
        """ Register Info Plugin Flags Tpyes """
        # Plugin General Flags :
        HidePlugin = c4d.PLUGINFLAG_HIDE
        HideTool = c4d.PLUGINFLAG_HIDEPLUGINMENU
        RefreshPlugin = c4d.PLUGINFLAG_REFRESHALWAYS
        SmallNodePlugin = c4d.PLUGINFLAG_SMALLNODE
        # Command Plugin Flags:
        OptionGear = c4d.PLUGINFLAG_COMMAND_OPTION_DIALOG   # A info flag / Command has additional options. The user can access them through a small gadget.
        # Tag Plugin Flags :
        TagVis = c4d.TAG_VISIBLE                            # The tag can be seen in the object manager.
        TagMul = c4d.TAG_MULTIPLE                           # Multiple copies of the tag allowed on a single object.
        TagHier = c4d.TAG_HIERARCHICAL                      # The tag works hierarchical, so that sub-objects inherit its properties (e.g. the material tag).
        TagExp = c4d.TAG_EXPRESSION                         # The tag is an expression.
        TagTem = c4d.TAG_TEMPORARY                          # Private.
        # Object Plugin Flags:
        oMod = c4d.OBJECT_MODIFIER                          # Modifier object. Deforms the surrounding object. (E.g. bend.)
        oHier = c4d.OBJECT_HIERARCHYMODIFIER                # Hierarchical modifier. Deforms the surrounding objects together with other instances in a hierarchy chain. Only the top-most instance of the plugin in a chain is called. (E.g. bones.)Hierarchical modifier. Deforms the surrounding objects together with other instances in a hierarchy chain. Only the top-most instance of the plugin in a chain is called. (E.g. bones.)
        oGen = c4d.OBJECT_GENERATOR                         # Generator object. Produces a polygonal or spline representation on its own. (E.g. primitive cube.)
        oInput = c4d.OBJECT_INPUT                           # Used in combination with OBJECT_GENERATOR. Specifies that the generator uses builds a polygon or spline, using its subobjects as input. (E.g. Sweep Subdivision Surface, Boolean.)
        oPart = c4d.OBJECT_PARTICLEMODIFIER                 # Particle modifier.
        oSpline = c4d.OBJECT_ISSPLINE                       # The object is a spline.
        oCamera = c4d.OBJECT_CAMERADEPENDENT                # Camera dependent.
        oPointObj = c4d.OBJECT_POINTOBJECT                  # Point Object.
        oPolyObj = c4d.OBJECT_POLYGONOBJECT                 # Polygon object.
    PF = PluginFlags()
    # // Register Plugin Add-on Tool Tpyes to Cinema 4D //
    def RegisterCommandData(id, str_name, infoflags, iconName, helpInfo, dataClass):
        """ A CommandData Tool Plugin Register """
        DESCRIPTIONS = "" #ToolInfo_Description(helpInfo)
        plugin_Icon = c4d.bitmaps.BaseBitmap()
        plugin_Icon.InitWith(iconName)
        result = plugins.RegisterCommandPlugin(id=id,                       # Plugin register ID.
                                            str=str_name,                   # This is for the Plugin Name to show in the Plugins lic4d.storage.
                                            info=infoflags,                 # If you want a option button once you have a ExecuteOptionID in Data Class, 
                                                                            # then put in Flags info=c4d.PLUGINFLAG_COMMAND_OPTION_DIALOG|c4d.PLUGINFLAG_COMMAND_HOTKEY,
                                            icon=plugin_Icon,               # Plugin Icon Image.
                                            help=DESCRIPTIONS,              # The plugin help info is on what the plugin does.
                                            dat=dataClass)                  # The plugin data class.
        return True
    # // Register Tools // 
    PLUG_1 = {'ID':1050002, 'Icon':iPath, 'Name':"CommandTools Menu",'flags':0, 'Data':CMDTool("PCT"), 'Info':""}
    PLUG_2 = {'ID':1051421, 'Icon':iPath, 'Name':"PlugCafeTool-1", 'flags':PF.HideTool, 'Data':CMDTool("PCT1"), 'Info':""}
    PLUG_3 = {'ID':1054336, 'Icon':iPath, 'Name':"PlugCafeTool-2", 'flags':PF.HideTool|PF.OptionGear, 'Data':CMDTool("PCT2"), 'Info':""}
    
    if __name__ == '__main__':
        dir, file = os.path.split(__file__)
        RegisterCommandData(PLUG_1["ID"], PLUG_1["Name"], PLUG_1["flags"], PLUG_1["Icon"], PLUG_1["Info"], PLUG_1["Data"])
        RegisterCommandData(PLUG_2["ID"], PLUG_2["Name"], PLUG_2["flags"], PLUG_2["Icon"], PLUG_2["Info"], PLUG_2["Data"])
        RegisterCommandData(PLUG_3["ID"], PLUG_3["Name"], PLUG_3["flags"], PLUG_3["Icon"], PLUG_3["Info"], PLUG_3["Data"])
    

    cheers & good luck!
    Ap Ashton



  • Hi @Ashton_FCS_PluginDev

    Thanks for the demo. It works as advertised.
    It certainly answers one of the two options presented by @m_adam on how to approach it.

    1. you have to register a CommandData
    2. you can assign a unique ID for each menu entry, check for the clicked ID

    Hope you'll excuse me if I keep the thread open. I'm interested on @m_adam 's response on my execution of #2 option under the Message() (see above).

    Thanks again for the response. Have a great day ahead!



  • As @Ashton_FCS_PluginDev demonstrates, you can call multiple RegisterXX in your pyp file.
    So yes you have to register a CommandData for each in the first situation.

    You don't have to react about it on the Message function since the whole script/Cinema 4D will be blocked until you clicked/selected answers and the clicked ID will be the return value of ShowPopupDialog but as explained and demonstrated in the ShowPopupDialog documentation.

    Cheers,
    Maxime.



  • @m_adam Thanks for further clarification.

    @Ashton_FCS_PluginDev 's code works but I guess I'll just use the result of the ShowPopupDialog to trigger commands, as I added in the previous code.


Log in to reply