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
    • Register
    • Register
    • Login

    Axis lock/unlock in message function / UI update?

    General Talk
    programming
    3
    7
    1.4k
    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.
    • jochemdkJ
      jochemdk
      last edited by

      Hi, I’m working with a Python Tag (not a plugin) attached to an object.
      The goal is to only let the user scale the object if the document is in objectMode.

      So, I try setting the XYZ axis states accordingly within the message function.
      This kind of works..
      _ transformHandles are instantly updated
      _ but the XYZ axis icons in the main UI ain't always updated..
      _ hovering over the XYZ icons will update the state (which is far from ideal)

      So, how to update/redraw the generic C4D user interface?..
      And is there a cleaner/better way to catch some messageType/Event to handle this?

      Tia, Jochem

      import c4d
      
      doc: c4d.documents.BaseDocument
      op: c4d.BaseTag
      
      def message(msg_type, data):
          if msg_type == c4d.MSG_NOTIFY_EVENT:
              event_data = data['event_data']
              
              ### ---------------------- the part reacting to UserData buttons ---------------------- ###
              if event_data['msg_id'] == c4d.MSG_DESCRIPTION_COMMAND:
                  desc_id = event_data['msg_data']['id']
                  try:
                      if desc_id[1].id == 18: print("some udButton press..")
                  except (IndexError): pass
      
                  ### ---------------- below: the part reacting to UserData changes ----------------- ###
              elif event_data['msg_id'] == c4d.MSG_DESCRIPTION_POSTSETPARAMETER and op.GetObject().GetUserDataContainer():
                  if not c4d.threading.GeIsMainThread():
                      return
                  print("some udData change..")
      
              ### ------------------ which messageType/Event?.. now kind of hacky.. ----------------- ###
              if doc.GetAction() == 200000089 and c4d.threading.GeIsMainThread(): # scale/mainThread..
      
                  if doc.GetMode() == 1: # objMode..
                      for ax in [12153,12154,12155]:
                          if c4d.IsCommandChecked(ax) == True: c4d.CallCommand(ax)
                      #print("unLocked")
          
                  elif doc.GetMode() != 1:
                      for ax in [12153,12154,12155]:
                          if c4d.IsCommandChecked(ax) == False: c4d.CallCommand(ax)
                      #print("locked")
      
                  # this kind of works..
                  # _ transformHandles are instantly updated
                  # _ but the XYZ axis icons in the main UI ain't always updated..
                  # _ hovering over the XYZ icons will update the state
                  # so, how to update/redraw the generic C4D user interface?.. 
                  # _ & is there a better solution for the above if-block..
      
      
      def main():
          # listen to buttonPresses on host
          if not op.GetObject().FindEventNotification(doc, op, c4d.NOTIFY_EVENT_MESSAGE) and op[c4d.EXPRESSION_ENABLE] == True:
              bc = c4d.BaseContainer()
              op.GetObject().AddEventNotification(op, c4d.NOTIFY_EVENT_MESSAGE, 0, bc)
          #...
      
      i_mazlovI 1 Reply Last reply Reply Quote 0
      • i_mazlovI
        i_mazlov @jochemdk
        last edited by

        Hi Jochem,

        Please excuse a long delay in my answer.

        Your question would be so much easier to process if there was less fuzz in what behavior you're actually expecting to achieve. Screenshot of what cinema 4d part you're talking about would not hurt either 😉

        Regarding your question. First of all, I assume that you're talking about XYZ-axis mode buttons: 96dc43ce-65be-425d-8988-5f5a60a69f0e-image.png
        Secondly, I assume you would like to control the state (and the functionality) of these mode buttons from the python tag. In other worlds, it is not scale tool axis handles, you're talking about here.

        The answer then is that you're breaking the threading rule, namely the function c4d.CallCommand() is not allowed in python tag:
        a4ceb3db-8f93-46d8-a977-f9ae8e290600-image.png

        You can find more info on the threading stuff in our Threading Manual.

        Unfortunately, I don't see any good way for you to achieve this effect from the python tag, as the implementation of these functions is burried in our legacy code and you cannot reach out to them from outside. You could try playing around with c4d.StopAllThreads() function, but I doubt you can achieve any consistent result even with it.

        Cheers,
        Ilia

        MAXON SDK Specialist
        developers.maxon.net

        1 Reply Last reply Reply Quote 0
        • jochemdkJ
          jochemdk
          last edited by

          Hi @i_mazlov

          Thx for answering… I’ll try to be clearer - incl. screenshots next time :} & Yes, I was talking about the XYZ-axis mode buttons (and scaleTool).

          Now I’m kind of confused, since I do try to stick to the threading rules. I can work around the axis modes, but my most important question is below:

          One of the things mentioned on this forum by the devs was that the CallCommand should only be used from within the main thread.
          That’s why I did put it in the message function, where I assume(d) it would be safe after:
          “if c4d.threading.GeIsMainThread(): 'do some stuff..'”

          Isn't that kind of the same as from within a Python tag you can use CallCommands within a BUTTON function (where you can also use EventAdd()))?

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

            Hey @jochemdk,

            Thank you for reaching out to us. I must apologize, your posting was fine and your question was clear. And images are not necessary 🙂 We also talked in our morning meeting about that you did (almost) everything right with checking for being on the main thread. @i_mazlov must have forgotten.

            You seem to simply miss a c4d.gui.GeUpdateUI() call, as for me the script is then doing what it should. At least from my understanding of your intentions. But I removed also some fluff in your script. The propagation of the button press via an event notification is a bit iffy, but since you do the MT check, it should not be a problem.

            File: axis_toggle.c4d
            Code:

            """Demonstrates how to toggle the axis commands when a button is being pressed in a scripting object.
            """
            
            import c4d
            
            doc: c4d.documents.BaseDocument
            op: c4d.BaseTag
            
            ID_BTN_AXIS: int = 1
            
            def message(message: int, data: any) -> None:
                """Called by Cinema 4D to convey information to the scripting object.
                """
                # When a button has been pressed, we want to toggle the axis commands.
                if message == c4d.MSG_DESCRIPTION_COMMAND:
                    if not isinstance(data, dict):
                        raise TypeError(f"Unexpected message data type: {type(data)}")
                    
                    # This is the relevant button which is being pressed.
                    did: c4d.DescID = data["id"]
                    if did.GetDepth() >= 2 and did[1].id == ID_BTN_AXIS:
                        print("Button pressed.")
            
                        # When the document is in scale tool mode and we are on the main thread, toggle all
                        # active axis commands.
                        if doc.GetAction() == c4d.ID_MODELING_SCALE and c4d.threading.GeIsMainThread():
                            for ax in [12153, 12154, 12155]:
                                if not c4d.IsCommandChecked(ax):
                                    c4d.CallCommand(ax)
            
                    # Force the UI of Cinema 4D to update.
                    c4d.gui.GeUpdateUI()
            
            def main():
                # I removed the event notification fluff of propagating user data actions from the host object
                # to the scripting tag, as it is not necessary for this example.
                pass
            

            MAXON SDK Specialist
            developers.maxon.net

            1 Reply Last reply Reply Quote 0
            • jochemdkJ
              jochemdk
              last edited by

              Thx a lot @ferdinand !!!
              Sorry, was a bit busy the last days, but I'll check asap.

              1 Reply Last reply Reply Quote 0
              • jochemdkJ
                jochemdk
                last edited by

                Yes, a question, where did the "mark as solved" button go 🙂

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

                  We removed it because it was just extra work for us to close all the threads where people never bothered to mark them as solved. But I am happy that you seem to have found your solution.

                  MAXON SDK Specialist
                  developers.maxon.net

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