Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware 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

    Add button to run a script

    Cinema 4D SDK
    3
    12
    2.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.
    • S
      stereo_stan
      last edited by

      Hi everyone, I am trying to learn how to code python by modifying the maxon scripts.

      I have this basic script working as a plugin. It makes the object editable and moves the anchor point. I am trying to figure out a way to make this work with a button click instead of just clicking on the script to run it.

      Can you please help me get one or two buttons to work with this code? Thank you for any guidance!

      import c4d
      import os

      def load_bitmap(path):
      path = os.path.join(os.path.dirname(file), path)
      bmp = c4d.bitmaps.BaseBitmap()
      if bmp.InitWith(path)[0] != c4d.IMAGERESULT_OK:
      bmp = None
      return bmp

      import c4d
      from c4d import gui

      class MakeEditableAnchor(c4d.plugins.CommandData):

      PLUGIN_ID = 1058020
      PLUGIN_NAME = 'MakeEditableAnchor'
      PLUGIN_INFO = 0
      PLUGIN_ICON = load_bitmap('res/icons/makeeditableanchor.jpg')
      PLUGIN_HELP = ''
      
      def Register(self):
          return c4d.plugins.RegisterCommandPlugin(
              self.PLUGIN_ID, self.PLUGIN_NAME, self.PLUGIN_INFO, self.PLUGIN_ICON,
              self.PLUGIN_HELP, self)
      
      def Execute(self, doc):
          gui.MessageDialog('Hello World!')
          c4d.CallCommand(12236) # Make Editable
          op = doc.GetActiveObject()
          #op = doc.SearchObject("obj1")
          ps = op.GetAllPoints() #Points in Local coordinates List
          m = op.GetMg() #Object Global Matrix
      
          center = op.GetMp() #Local coordinates center: https://tinyurl.com/wed88cn
          rad = op.GetRad() # Geometry radius: https://tinyurl.com/tb6vn64
      
          center -= c4d.Vector(0,rad.y,0) #Move down axis to lowest point. Comment this line to keep axis in a center
      
          center *= m #Convert to Global coordinates center
      
          new_m = c4d.Matrix(m) #Copy matrix
          new_m.off = center #Change its center
      
          loc_m = ~new_m * m #Get local matrix
      
          op.SetAllPoints([loc_m.Mul(p) for p in ps])
          op.SetMg(new_m)
      
          op.Message(c4d.MSG_UPDATE)
      
          c4d.EventAdd()
      
          # Execute main()
          return True
      

      if name == 'main':
      MakeEditableAnchor().Register()

      1 Reply Last reply Reply Quote 0
      • CairynC
        Cairyn
        last edited by

        I am actually not sure what you mean by

        with a button click instead of just clicking on the script

        A Command Plugin is essentially one command that is triggered by clicking the plugin, as the name says. If you want to embed a button in your GUI, you can just drag and drop the icon (I see that you have one) in any toolbar you like.

        You can of course load a dialog instead of directly executing the command. In that case you need to derive a new dialog from the class GeDialog and open the dialog asynchronously (see the other threads recently), then you can do tricks with it like embedding it into the layout and have C4D open it automatically on start. However, this makes only sense if you have more than one functionality to execute, or input fields for the user to enter data in, otherwise you end up with just one button in the dialog, which is a bit wasteful.

        Also, isn't that what you just asked in the other thread, where you already have a GeDialog created?

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

          Thank you. Yes, the goal is to be able to execute multiple commands in one plugin so I was trying to find a way to connect the GeDialog with some commands.

          I am new to programming and I have read through the GeDialog documentation and I can not figure out a way to connect the two concepts.

          Is there some example code that creates a few buttons that run some commands?

          Thank you for your help, I appreciate it.

          ferdinandF CairynC 2 Replies Last reply Reply Quote 0
          • ferdinandF
            ferdinand @stereo_stan
            last edited by ferdinand

            Hello @stereo_stan,

            thank you for reaching out to us. I am not one hundred percent sure how your question is meant.

            Scripts in the Python Scripting Manger in Cinema 4D can have their own button out of the box, you can even define the icon of that button. For details, please refer to the Icon section the Script Manger User Manual.

            In case your question was more about execution, @Cairyn already gave you most of the info (thanks!). In principle you would do this with a CommandData or a NodeData plugin, depending on what you have in mind exactly. Define there a STRING element in their interface definition and flag it as being MULTISTRING and Python. Also add a button to the interface. Then, in your implementation, listen for the button being clicked and load the Python code from the code window you did create. You could of course also add logic to populate that code window from a file or cut out the code window altogether and always execute files. Executing arbitrary modules is more a Python than a Python SDK question and therefore out of scope of support, but I have shown it here. There are multiple libraries in the standard Python library you can use for this, runpy is the least complicated of them.

            Cheers,
            ferdinand

            MAXON SDK Specialist
            developers.maxon.net

            1 Reply Last reply Reply Quote 0
            • CairynC
              Cairyn @stereo_stan
              last edited by

              @stereo_stan I suppose you don't mean to run an external script (Python allows this through Execute and Evaluate functions) since you're a beginner. You just want to call a functionality from your script (the same). So, you just need the dialog.

              The Github example (you found these, yes?) Memory Viewer contains a dialog that is called as part of the CommandData plugin. Let me quote this partially here (as it is (C)Maxon stuff anyway 😉 )

              class MemoryViewerCommandData(c4d.plugins.CommandData):
                  """
                  Command Data class that holds the MemoryViewerDialog instance.
                  """
                  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)
              

              This CommandData is relatively simple, it just opens your dialog. Noteworthy are that you need to open the dialog as DLG_TYPE_ASYNC, which will allow you to work in C4D while the dialog is open; and the implementation of a RestoreLayout function which C4D uses to open a dialog that is embedded in the layout.

              The example also contains the MemoryViewerDialog class which is used in this CommandData, but you may not want to study that as it shows a number of different techniques (like GeUserArea) that you don't want yet, so I don't quote it. However, I saw in your other thread that you can construct a dialog class already, so I guess you do not need further pointers.

              To get a button into your dialog, use AddButton in your dialog's CreateLayout method as often as you like, like that:
              self.AddButton(ID_BUTTON3, c4d.BFH_SCALEFIT, name="Jump into code")
              then evaluate the commands from that button in the dialog's Command method

                  def Command(self, id, msg):
                      if id==ID_BUTTON3:
                          print ("Button clicked")
                      return True
              
              1 Reply Last reply Reply Quote 0
              • S
                stereo_stan
                last edited by stereo_stan

                Thank you Cairyn and Ferdinand, this is all very helpful and I have a better understanding of different ways to approach this type of project.

                Yes, I do not think I want to run an external script, just have one script that a user can perform a few different commands from. Essentially so they can just have one command icon ( script ) in their interface and when they click on that it opens up a GeDialog with a bunch of options they can run by clicking on buttons inside of the GeDialog vs having to click on a bunch of different scripts. I assume those buttons are essentially other scripts that are all bundled together that they would drop in their scripts folder.

                I am so excited about all of the possibilities of adding programming into my daily work as a c4d animator-it is challenging to learn from a non-programming background but this forum has helped a lot.

                Thanks again.

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

                  Hello @stereo_stan

                  unless I am overlooking something here, you can then simply drag and drop the script into one of Cinema's palettes as described in my previous posting. No extra steps required.

                  Cheers,
                  Ferdinand

                  MAXON SDK Specialist
                  developers.maxon.net

                  1 Reply Last reply Reply Quote 0
                  • S
                    stereo_stan
                    last edited by ferdinand

                    One more question on this topic. I have tried to combine Cairyn's note on creating a button with the py-commanddata_dialogr13 script from Maxon github to create an async dialog.

                    For some reason, it will not add my button to the dialog-basically just trying to add buttons to the py-commanddata script. Do you have any possible suggestions on making something like this work? Basically would like an async dialog with buttons. Thank you for any advice.

                    """
                    Copyright: MAXON Computer GmbH
                    Author: Maxime Adam
                    
                    Description:
                        - Creates a Dialog which display 2 buttons OK and Cancel.
                    
                    Class/method highlighted:
                        - c4d.plugins.CommandData
                        - CommandData.Execute()
                        - c4d.gui.GeDialog
                        - GeDialog.CreateLayout()
                        - GeDialog.Command()
                    """
                    import c4d
                    from c4d import gui
                    
                    
                    # Be sure to use a unique ID obtained from www.plugincafe.com
                    PLUGIN_ID = 1057171
                    
                    
                    
                    class ExampleDialog(c4d.gui.GeDialog):
                    
                        def CreateLayout(self):
                            """This Method is called automatically when Cinema 4D Create the Layout (display) of the Dialog."""
                            # Defines the title of the Dialog
                            self.SetTitle("This is an example Dialog")
                    
                            # Creates a Ok and Cancel Button
                            self.AddDlgGroup(c4d.DLG_OK | c4d.DLG_CANCEL)
                    
                            
                    
                            return True
                    
                        def Command(self, messageId, bc):
                            """This Method is called automatically when the user clicks on a gadget and/or changes its value this function will be called.
                            It is also called when a string menu item is selected.
                    
                            self.AddButton(ID_BUTTON3, c4d.BFH_SCALEFIT, name="Jump into code")
                    
                            Args:
                                messageId (int): The ID of the gadget that triggered the event.
                                bc (c4d.BaseContainer): The original message container.
                    
                            Returns:
                                bool: False if there was an error, otherwise True.
                            """
                            # User click on Ok buttonG
                            if messageId == c4d.DLG_OK:
                                print("User Click on yep")
                                return True
                    
                            if ID == ID_BUTTON3:
                                print ("Button clicked")
                                return True    
                    
                            # User click on Cancel button
                            elif messageId == c4d.DLG_CANCEL:
                                print("User Click on Cancel")
                    
                                # Close the Dialog
                                self.Close()
                                return True
                    
                            return True
                    
                        def Command(self, id, msg):
                            if id == ID_BUTTON3:
                                print ("Button clicked")
                                return True    
                    
                    
                    class ExampleDialogCommand(c4d.plugins.CommandData):
                        """Command Data class that holds the ExampleDialog instance."""
                        dialog = None
                        
                        def Execute(self, doc):
                            """Called when the user executes a command via either CallCommand() or a click on the Command from the extension menu.
                    
                            Args:
                                doc (c4d.documents.BaseDocument): The current active document.
                    
                            Returns:
                                bool: True if the command success.
                            """
                            # Creates the dialog if its not already exists
                            if self.dialog is None:
                                self.dialog = ExampleDialog()
                    
                            # Opens the dialog
                            return self.dialog.Open(dlgtype=c4d.DLG_TYPE_ASYNC, pluginid=PLUGIN_ID, defaultw=400, defaulth=32)
                    
                        def RestoreLayout(self, sec_ref):
                            """Used to restore an asynchronous dialog that has been placed in the users layout.
                    
                            Args:
                                sec_ref (PyCObject): The data that needs to be passed to the dialog.
                    
                            Returns:
                                bool: True if the restore success
                            """
                            # Creates the dialog if its not already exists
                            if self.dialog is None:
                                self.dialog = ExampleDialog()
                    
                            # Restores the layout
                            return self.dialog.Restore(pluginid=PLUGIN_ID, secret=sec_ref)
                    
                    
                    # main
                    if __name__ == "__main__":
                        # Registers the plugin
                        c4d.plugins.RegisterCommandPlugin(id=PLUGIN_ID,
                                                          str="Py-CommandData Dialog",
                                                          info=0,
                                                          help="Display a basic GUI",
                                                          dat=ExampleDialogCommand(),
                                                          icon=None)
                    
                    
                    ferdinandF CairynC 2 Replies Last reply Reply Quote 0
                    • ferdinandF
                      ferdinand @stereo_stan
                      last edited by ferdinand

                      Heelo @stereo_stan,

                      @stereo_stan said in Add button to run a script:

                      or some reason, it will not add my button to the dialog-basically just trying to add buttons to the py-commanddata script.

                      The posted code does not add any button. This would have to happen in ExampleDialog.CreateLayout() which does only set the title of the dialog and adds a cancle group. Is that what you mean with "button"?

                      Cheers,
                      Ferdinand

                      MAXON SDK Specialist
                      developers.maxon.net

                      1 Reply Last reply Reply Quote 0
                      • CairynC
                        Cairyn @stereo_stan
                        last edited by

                        @stereo_stan

                        As Ferdinand already said, you forgot to add the AddButton call, or rather (as I can see my line self.AddButton(ID_BUTTON3, c4d.BFH_SCALEFIT, name="Jump into code") in the script), you inserted it in the wrong place (it's now inside a comment section, where it doesn't do anything, and also in the wrong method.

                        Additionally, I need to tell you that you must define ID_BUTTON3 somewhere, or it will raise an error. And the snippet if ID == ID_BUTTON3: will not work since the parameter ID that I used is actually called messageId in your command method. Oops, and you duplicated the whole command method so now you have two of them and Python will only consider the latter and ignore the first.

                        Okay, I guess Maxon hates me for self promoting here, but this is not so much an API question now but a matter of plain Python understanding, so you may perhaps consider my Python/C4D API course under
                        https://www.patreon.com/cairyn
                        In section 10 I have multiple examples for dialog setup with layout, buttons, command method, text fields, radio buttons, separators, columns, etc.; too much to replicate here.

                        I have corrected the script for you a final time, removing all the comments and reducing the sample to the most necessary stuff:

                        import c4d
                        from c4d import gui
                        
                        PLUGIN_ID = 1057171
                        ID_BUTTON3 = 1001
                        
                        class ExampleDialog(c4d.gui.GeDialog):
                        
                            def CreateLayout(self):
                                self.SetTitle("This is an example Dialog")
                                self.AddButton(ID_BUTTON3, c4d.BFH_SCALEFIT, name="Jump into code")
                                self.AddDlgGroup(c4d.DLG_OK | c4d.DLG_CANCEL)
                                return True
                        
                            def Command(self, messageId, bc):
                                if messageId == c4d.DLG_OK:
                                    print("User Click on yep")
                                    return True
                        
                                elif messageId == ID_BUTTON3:
                                    print ("Button clicked")
                                    return True    
                        
                                elif messageId == c4d.DLG_CANCEL:
                                    print("User Click on Cancel")
                                    self.Close()
                                    return True
                        
                                return True
                        
                        class ExampleDialogCommand(c4d.plugins.CommandData):
                            dialog = None
                            
                            def Execute(self, doc):
                                if self.dialog is None:
                                    self.dialog = ExampleDialog()
                                return self.dialog.Open(dlgtype=c4d.DLG_TYPE_ASYNC, pluginid=PLUGIN_ID, defaultw=400, defaulth=32)
                        
                            def RestoreLayout(self, sec_ref):
                                if self.dialog is None:
                                    self.dialog = ExampleDialog()
                        
                                return self.dialog.Restore(pluginid=PLUGIN_ID, secret=sec_ref)
                        
                        if __name__ == "__main__":
                            c4d.plugins.RegisterCommandPlugin(id=PLUGIN_ID,
                                                              str="Py-CommandData Dialog",
                                                              info=0,
                                                              help="Display a basic GUI",
                                                              dat=ExampleDialogCommand(),
                                                              icon=None)
                        

                        827c6608-acfc-4a41-b73c-a3348488d297-image.png

                        1 Reply Last reply Reply Quote 1
                        • S
                          stereo_stan
                          last edited by

                          Thank you Cairyn and Ferdinand again. This is all making much more sense now, I appreciate you helping me understand how to correctly set up a simple interface. I do need to spend more time just learning the fundamentals of Python.

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

                            Hello @stereo_stan,

                            without further questions or replies, we will consider this topic as solved by Monday, the 30th and flag it accordingly.

                            Thank you for your understanding,
                            Ferdinand

                            MAXON SDK Specialist
                            developers.maxon.net

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