Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush Python API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    How to Open a Dialog Plugin from a Tag Plugin?

    Cinema 4D SDK
    sdk python
    4
    6
    1.1k
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • ?
      A Former User
      last edited by A Former User

      Hello,
      I have seen tag plugins that, when they are double-clicked, open a dialog.

      Using the py-look_at_camera_r13 example from the SDK, I came up with the code below. I'm trying to open the Dialog with the Execute method of the plugin class which is clearly the wrong place for it.

      import c4d, os
      from c4d import plugins, utils, bitmaps, gui, documents
      
      TAG_ID = 1234567
      PLUGIN_ID = 2345678
      
      global dlg
      
      class MyDlg(gui.GeDialog):
          def CreateLayout(self):
              self.SetTitle("MyDlg")
              self.GroupBegin(1, c4d.BFH_CENTER, 2, 1, None, 640)
              self.GroupBorderSpace(10, 20, 10, 20)
              self.AddButton(2, c4d.BFH_FIT, name="Yes", initw=100)
              self.AddButton(3, c4d.BFH_FIT, name="No", initw=100)
              self.GroupEnd()
              return True
      
          def Command(self, id, msg):
              return True
      
      class MyTagPlugin(c4d.plugins.TagData):
          def Init(self, node):
              pd = c4d.PriorityData()
              if pd is None:
                  raise MemoryError("Failed to create a priority data.")
              pd.SetPriorityValue(c4d.PRIORITYVALUE_CAMERADEPENDENT, True)
              node[c4d.EXPRESSION_PRIORITY] = pd
              return True
      
          def Execute(self, tag, doc, op, bt, priority, flags):
              global dlg
              if dlg == None:
                  dlg = MyDlg()
                  dlg.Open(dlgtype=c4d.DLG_TYPE_ASYNC, xpos=-1, ypos=-1, pluginid=PLUGIN_ID, defaultw=540, defaulth=640)
              return c4d.EXECUTIONRESULT_OK
      
      if __name__ == "__main__":
          # Retrieves the icon path
          directory, _ = os.path.split(__file__)
          fn = os.path.join(directory, "res", "icon.png")
      
          # Creates a BaseBitmap
          bmp = c4d.bitmaps.BaseBitmap()
          if bmp is None:
              raise MemoryError("Failed to create a BaseBitmap.")
      
          # Init the BaseBitmap with the icon
          if bmp.InitWith(fn)[0] != c4d.IMAGERESULT_OK:
              raise MemoryError("Failed to initialize the BaseBitmap.")
      
          c4d.plugins.RegisterTagPlugin(id=TAG_ID ,
                                        str="MyTagPlugin",
                                        info=c4d.TAG_EXPRESSION | c4d.TAG_VISIBLE,
                                        g=MyTagPlugin,
                                        description="Tmytagplugin",
                                        icon=bmp)
      
      

      This code throws the following error:

      RuntimeError: illegal operation, invalid cross-thread call
      

      I'm guessing it's because it's a gui call off of the main thread.

      It seems the Tag plugins launch a Command Plugin somehow. I have a few questions about this:

      • How would I launch the Command Plugin once when the tag is double-clicked and not every time Init() or Execute() are called by the tag or if one is already open?
      • How could I get the Dialog Plugin to update its UI when one of the Python tag plugins is selected?

      Thank you.

      1 Reply Last reply Reply Quote 0
      • ferdinandF
        ferdinand
        last edited by ferdinand

        Hi,

        you answered your own question (almost) correctly: GUI functionalities are only allowed to be invoked from the main thread and c4d.TagData.Excute() is not being executed from the main thread. Some thoughts / solutions for your problem:

        1. Generally speaking your desired functionality is rather exotic. May I ask for practical examples on where you have seen this behavior? Breaking with interface paradigms of an application is only rarely a good idea.
        2. The general place to handle "events" in Cinema are the various message functions and methods. NodeData.Message() is also usually invoked from the main thread, but you can double check with c4d.threading.GeIsMainThread(), to make sure that it is safe to invoke any GUI calls.
        3. There is the mouse and keyboard input related message ID BFM_INPUT, but this message is not being sent to NodeData.Message(), since Cinema does not propagate messages through the whole GUI graph, like one might be used to from event based systems.
        4. So your only option is to piggyback on a message ID, that is being sent in a context 'close enough'. c4d.MSG_EDIT could be such a candidate, as it is being invoked when you double click on a node.

        Cheers
        zipit

        MAXON SDK Specialist
        developers.maxon.net

        1 Reply Last reply Reply Quote 0
        • M
          mp5gosu
          last edited by

          Hello. In addition to @zipit's excellent answer, you might consider also implementing a Message Plugin and react to CoreMessages, sent via c4d.SpecialEventAdd()

          @zipit:

          May I ask for practical examples on where you have seen this behavior?

          There are actually some tags with GUI interaction. The MographCache Tag for example.

          1 Reply Last reply Reply Quote 0
          • ?
            A Former User
            last edited by A Former User

            @zipit Thank you for the reply. A practical example would be storing data about a set of objects (as in a character pose) in a tag. The Command plugin would have the UI used for organizing the multiple tags' data (adding/deleting poses to the tag, renaming poses, etc.). Here's

            . I'd be very happy to hear ideas for other paradigms.

            Here's what I have now in my Tag:

                def Message(self, op, type, data):
                    if type == c4d.MSG_EDIT:
                        try:
                            global dlg
                            dlg.Open(dlgtype=c4d.DLG_TYPE_ASYNC, xpos=-1, ypos=-1, pluginid=COMMANDPLUGIN_ID, defaultw=540, defaulth=640)
                        except NameError:
                            global myDlg
                            dlg = MyDlg()
                            dlg.Open(dlgtype=c4d.DLG_TYPE_ASYNC, xpos=-1, ypos=-1, pluginid=COMMANDPLUGIN_ID, defaultw=540, defaulth=640)
                    return True
            

            This method only opens the dialog once. If I close and try to reopen it, however, nothing happens. Opening it this way creates double menu bars:
            Double_Menu_Bar.png

            Also, I'm still unsure of how to check if the Command Plugin UI is already open from the Tag Plugin?

            @mp5gosu Thank you. I'll check out the Message Plugin.

            ferdinandF 1 Reply Last reply Reply Quote 0
            • ferdinandF
              ferdinand @A Former User
              last edited by ferdinand

              @blastframe said in How to Open a Dialog Plugin from a Tag Plugin?:

              @zipit Thank you for the reply. A practical example would be storing data about a set of objects (as in a character pose) in a tag.

              With an example I actually meant where you have seen this before in 'the wild', but @mp5gosu already did provide such an example. I however would remain on my point that this is a rather exotic feature and I would still suggest contemplating if this is really what you want. Weird GUIs are really good way to alienate users. I am not saying this is a bad idea, I am just saying: Really make sure that this is not a bad idea.

              I am not quite sure, if I have missed the CommandData stuff before or if this is new information. I would lean more into the direction of @mp5gosu suggestion now.

              1. Your bug might be caused by the fact, that you have your dialog dangling around as a global variable, which might lead to the dislog not terminating properly. I am not sure though.
              2. I originally thought the dialog was attached to your node. If you just want to open the dialog of a CommandData plugin, either execute the plugin itself via c4d.CallCommand() or send a message to your CommandData plugin (similar to @mp5gosu suggestion):
              # In the tag
              def Message(self, node, type, data):
                  if type == c4d.MSG_EDIT:
                      c4d.GePluginMessage(ID_OPEN_YOUR_DIALOG, data=some_data)
                  [...]
              
              # In the command data:
              def Message(self, type, data):
                  if type == ID_OPEN_YOUR_DIALOG:
                      self._open_my_dialog(data)
                  [...]
              
              1. You could also use core messages like @mp5gosu suggested, if you want to do more complex stuff.

              edit: Jeah, there it is, CommandData, in your first posting, mea culpa 😉

              Cheers
              zipit

              MAXON SDK Specialist
              developers.maxon.net

              1 Reply Last reply Reply Quote 0
              • S
                s_bach
                last edited by

                Hello,

                the important question is, if you want to open a modal or asynchronous dialog.

                An asynchronous dialog must be hosted by a CommandData plugin (GeDialog Manual) (e.g. py-memory_viewer).

                As mentioned above, a double-click on a tag can be detected by looking for the MSG_EDIT message (NodeData::Message() Manual).

                So, when you detect MSG_EDIT, you simply have to execute the command that hosts you dialog. You can simply do that using CallCommand() (Command Utility Manual).

                Within your dialog, you can find your tag by getting the active tag.

                best wishes,
                Sebastian

                MAXON SDK Specialist

                Development Blog, MAXON Registered Developer

                1 Reply Last reply Reply Quote 0
                • First post
                  Last post