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

    Gray Out Custom UI Bitmap Button?

    Cinema 4D SDK
    r21 python
    3
    9
    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.
    • B
      bentraje
      last edited by bentraje

      Hi,

      Is there a way I can gray out a button that doesn't need a specific condition?
      You can see an illustration of the problem here:
      https://www.dropbox.com/s/rfw2nw8o298r0ej/c4d162_python_gray_out_custom_gui_button.jpg?dl=0

      I used the SetToggleState(set), BITMAPBUTTON_ICONID1 and BITMAPBUTTON_ICONID2 but I am having a problem on implementing it.

      Here is the code so far:

      import c4d
      from c4d import bitmaps, documents, gui, plugins, threading, utils
      
      PLUGIN_ID   = 1011328
      
      class MyDialog(gui.GeDialog):
      
          def CreateLayout(self):
      
              self.SetTitle('Colorizer')
              self.doc = c4d.documents.GetActiveDocument()
              
              # Prepare a red bitmap for the button.
              w = 50
              h = 50
      
              # BitmapButton configuration
              bcBitmapButton = c4d.BaseContainer()
              bcBitmapButton[c4d.BITMAPBUTTON_BUTTON] = True
              bcBitmapButton[c4d.BITMAPBUTTON_ICONID1] = c4d.Ocube
              bcBitmapButton[c4d.BITMAPBUTTON_ICONID2] = c4d.Oplane # should be the greyed out Ocube
      
              buttonId = 2000
              _bitmapButton = self.AddCustomGui(buttonId, c4d.CUSTOMGUI_BITMAPBUTTON, "", c4d.BFH_CENTER|c4d.BFV_CENTER, w, h, bcBitmapButton)
      
              icon = c4d.bitmaps.InitResourceBitmap(c4d.Ocube)
              _bitmapButton.SetImage(icon, True)
      
              if len(doc.GetActiveObjects())>0: 
                  _bitmapButton.SetToggleState(0)
              else:
                  _bitmapButton.SetToggleState(1)
      
              return True
      
          def Command(self, id, msg):
              
              if id==2000 : 
                  print "Create Cube"
      
              return True
      
      class MyMenuPlugin(plugins.CommandData):
      
          dialog = None
          def Execute(self, doc):
          # create the dialog
             if self.dialog is None:
                self.dialog = MyDialog()
      
             return self.dialog.Open(dlgtype=c4d.DLG_TYPE_ASYNC, pluginid=PLUGIN_ID, defaultw=200, defaulth=150, xpos=-1, ypos=-1)
      
          def RestoreLayout(self, sec_ref):
          # manage the dialog
             if self.dialog is None:
                self.dialog = MyDialog()
             return self.dialog.Restore(pluginid=PLUGIN_ID, secret=sec_ref)
      
      if __name__ == "__main__":
      
          okyn = plugins.RegisterCommandPlugin(PLUGIN_ID, "Cubey",0, None, "Cubey initialized", MyMenuPlugin())
          if (okyn):
              print "Cubey initialized"
      
      

      Thank you for looking at my problem

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

        The correct way would be to have a timer or to forward the GetState method from the CommandData to the GeDialog. And in this method call GeDialog.Enable(buttonId, False).

        Let me know if it fulfills your needs.
        Cheers,
        Maxime.

        MAXON SDK Specialist

        Development Blog, MAXON Registered Developer

        1 Reply Last reply Reply Quote 0
        • B
          bentraje
          last edited by

          @m_adam

          Thanks for the response.

          RE: And in this method call GeDialog.Enable(buttonId, False).
          This was clear to me. I was able to dim it down.

          But how do I implement this one:
          The correct way would be to have a timer or to forward the GetState method from the CommandData to the GeDialog.

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

            Hi, sorry I totally forget about EVMSG_CHANGE which is called when anything in the scene changed.

            Side note doesn't store the current active document in a class variable since the active document could change while the dialog is opened, so the preferred way is to retrieve it each time (I've done it through a python property).

            class MyDialog(c4d.gui.GeDialog):
            
                buttonId = 2000
            
                @property
                def doc(self):
                    return c4d.documents.GetActiveDocument()
            
                def CreateLayout(self):
            
                    self.SetTitle('Colorizer')
            
                    # Prepare a red bitmap for the button.
                    w = 50
                    h = 50
            
                    # BitmapButton configuration
                    bcBitmapButton = c4d.BaseContainer()
                    bcBitmapButton[c4d.BITMAPBUTTON_BUTTON] = True
                    bcBitmapButton[c4d.BITMAPBUTTON_ICONID1] = c4d.Ocube
                    bcBitmapButton[c4d.BITMAPBUTTON_ICONID2] = c4d.Oplane # should be the greyed out Ocube
                    
                    _bitmapButton = self.AddCustomGui(self.buttonId, c4d.CUSTOMGUI_BITMAPBUTTON, "", c4d.BFH_CENTER|c4d.BFV_CENTER, w, h, bcBitmapButton)
            
                    icon = c4d.bitmaps.InitResourceBitmap(c4d.Ocube)
                    _bitmapButton.SetImage(icon, True)
                    return True
                
                def InitValues(self):
                    self.SetEnableCheck()
                    return True
            
                def SetEnableCheck(self):
                    if len(self.doc.GetActiveObjects(c4d.GETACTIVEOBJECTFLAGS_NONE))>0: 
                        self.Enable(self.buttonId, True)
                    else:
                        self.Enable(self.buttonId, False)
            
                def Command(self, id, msg):
                    if id==2000 : 
                        print "Create Cube"
                    return True
                
                def CoreMessage(self, id, data):
                    if id == c4d.EVMSG_CHANGE:
                        self.SetEnableCheck()
                    return True
            

            Cheers,
            Maxime.

            MAXON SDK Specialist

            Development Blog, MAXON Registered Developer

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

              @m_adam Side question: Is this really how it's done in general?

              On every EVMSG_CHANGE (which happens often) the dialog checks the enabling status for this button by looking up GetActiveObjects() which returns a list. So, the system had to assemble a list of active objects which may have hundreds of entries out of thousands of living objects in the scene.

              Now there are hundreds of icons in my layout, and a good deal of them have a functionality like this. Being CommandData objects, they cannot share the result of GetActiveObjects() (like it would be possible in a dialog), so for each of them, this overhead is created just to determine whether something is selected?

              I know that we measure processor speed in billions of executions per second, but doing this on EVMSG_CHANGE still seems incredibly wasteful. Maybe my understanding of the overhead is wrong and we could perform much more stuff during an EVMSG_CHANGE than I currently am comfortable with?

              1 Reply Last reply Reply Quote 1
              • B
                bentraje
                last edited by

                @m_adam

                Thanks for the response. It works as expected. It took me a while to get the code.
                I was thinking how is the id == c4d.EVMSG_CHANGE where the id buttonId = 2000. Then it hit me, the id in the Command() and CoreMessage is actually different ids .

                Hahaha. Anyhow thanks!

                @Cairyn

                Just out of curiosity. I'm assuming you are not using the workflow above, may I ask how would you have done it?

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

                  @bentraje I do not have the issue at the moment; my last plugins were all independent of selections and therefore always active. When I program for myself, I normally skip the niceties and test the requirements after clicking the button; I can always stop execution after that.

                  Currently I am working on writing a Python tutorial series - the Python script templates have a State() function but don't tell me when they are called. Maybe that actually IS during an EVMSG_CHANGE.

                  Given the internal messaging system of C4D, it seems logical. It just is a lot of stuff to do during that timeframe.

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

                    @Cairyn sorry for the delay, I missed your reply.

                    React to EVMSG_CHANGE is used a lot (I agree with you, performance-wise it's not the best idea, but until everything is moved to the new core, this is like that).
                    So as long as you don't do something fancy there, this is not a big issue, and it's also what all render engine do to tracks change into the current document to refresh their IPR.

                    GetState is called at each UI redraw so it's called multiple time per frames each frame (so I would say it's even more critical than EVMSG_CHANGE, as you can really slow down Cinema 4D quickly)

                    Unfortunately, currently this is the only way.
                    Cheers,
                    Maxime.

                    MAXON SDK Specialist

                    Development Blog, MAXON Registered Developer

                    1 Reply Last reply Reply Quote 0
                    • B
                      bentraje
                      last edited by

                      @Cairyn

                      Gotcha. Thanks for sharing!

                      @m_adam
                      ". . . but until everything is moved to the new core"

                      That's a nice tease there 🙂

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