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

    c4d.gui.QuestionDialog() in Python plugin makes c4d crash every time

    Cinema 4D SDK
    2
    6
    994
    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.
    • mocolocoM
      mocoloco
      last edited by

      Hi there,

      I'm working on a simple Tag plugin in Python, which must run under R21.

      On the plugin itself, I'm checking inside Execute() if there is a change on the FILENAME field of my GUI. If there is a change, I'm checking the file extension and depending on this I'm proceeding to a conversion upon the agreement of the user to a question.

      But... if seems that the simple call to c4d.gui.QuestionDialog() makes c4d crash instantly.

      That's the facts.

      Honestly, I'm using an identical call to this function elsewhere in the code in Message() without any issue.

      Can this crash occurs because I'm using this in Execute() or can this be related to something I'm doing in a wrong way?
      Of course I've check the documentation but I didn't notice anything relevant.

      Here the excerpt of the code that makes c4d crash.

      import sys
      import os
      
      import c4d
      import time
      import json
      
      from c4d import gui, plugins, bitmaps
      
      #../.. Inside the class which define the Tag plug-in
      
      
          def __init__ (self):
              pass
      
      
      
          def Init(self, node):
              return True
      
      
           def Execute(self, tag, doc, op, bt, priority, flags):
      
              # .../...
      
              currentFILE = self.currentTagData.GetFilename(c4d.mMOTIONFILE_PATH)
              if self.currentTagData.GetFilename(c4d.mMOTIONFILE_PATH) != self.previousFILE:
                  self.previousFILE = currentFILE
      
                  if currentFILE.lower().endswith(".json"): # Is a JSON?
                      return True
                  elif currentFILE.lower().endswith(".csv"): # Is a CSV?
                      # CRASH HERE - Ask to convert the CSV file into a JSON one
                      askConvertToJSON = c4d.gui.QuestionDialog("Would you like to convert this file to a compatible JSON?")
                      if not askConvertToJSON:
                          return False
                      return True
                  else:
                      raise ValueError("This file extensoin is not supported, please load a compatible JSON or CSV file")
                      return False
              
              return True
      

      Any helps are more than welcome.
      Thanks!

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

        Hello @mocoloco,

        welcome to the forum and the Cinema 4D community and thank you for reaching out to us.

        The reason why your plugin is not working, is that your program design is a violation of Cinema 4D's threading restrictions. TagData.Execute is not being called from the main thread, i.e., is being called in parallel. Which makes GUI operations, e.g., opening a dialog, off limits there. For more information on things that are forbidden to do outside of the main thread, please read the mentioned article about threading restrictions. Since your code also does indicate that you are planning to do file operations in TagData.Execute: This will also cause problems when multiple threads try to operate on the same file.

        You have move such operations to a method of NodeData which you know is being called (at least sometimes) on the main thread. A common candidate is NodeData.Message and listening for a button click. There you can then check for being on the main thread with c4d.threading.GeIsMainThread() and then do anything that would be unsafe to do in asynchronous execution, like modifying the scene graph or doing GUI operations.

        Cheers,
        Ferdinand

        MAXON SDK Specialist
        developers.maxon.net

        1 Reply Last reply Reply Quote 0
        • mocolocoM
          mocoloco
          last edited by mocoloco

          Thanks @ferdinana for your message and informations. As it is my first plugin I didn't dig that much in SDK and didn't noticed that..

          My first try was to get that information on def Message(), but FILENAME declared on .res when clicked, doesn't fired any message while all other buttons are. This one is not maybe due to the fact that's not really a button, I'm gonna check if an another TYPE is changed with this particular FILENAME.

          I will read all this and see if I can figured out with this.

          Thanks a lot,
          Christophe

          1 Reply Last reply Reply Quote 0
          • mocolocoM
            mocoloco
            last edited by mocoloco

            After some test, I didn't find what message in NodeData check to see if there is any file change on the FILENAME GUI Item ID XXXX. That's frustrating.

            I checked the following, that sounds relevant, but they unfortunetly aren't

                def Message(self, node, type, data):
                    if type == c4d.MSG_DESCRIPTION_CHECKUPDATE:
                    
                    if type == c4d.MSG_DESCRIPTION_VALIDATE:
                    
                    if type == c4d.MSG_DESCRIPTION_EDIT_ENTRY:
            

            Scanning datas and types didn't helped neither.

            I'm just looking for a way to get a acknowledgment from the user prior doing an operation on the file selected... I thought that it could be simpler. If anybody ave a workaround for this and will to share it, please let me know!

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

              Hello @mocoloco,

              yes, finding the correct message to look for can be a bit tricky. In this case you should listen for MSG_DESCRIPTION_POSTSETPARAMETER, i.e., the message for when some parameter of a node has changed its value. You will find an example as a Python programming tag at the end of the posting which will translate quite directly to NodeData.Message(). It is only that the signature of the message function in a programming tag is slightly different.

              Cheers,
              Ferdinand

              The scene file:
              example.c4d

              The code:

              """Example for reacting to parameter changes in a NodeData node.
              """
              
              import c4d
              
              def message(mid, mdata):
                  """The equivalent to NodeData.Message().
              
                  The signature of NodeData.Message() is a bit different, as it has slightly 
                  different arguments and requires you to return something else than None.
                  
                  Args:
                      mid (int): The message type
                      mdata (any): The message data.
                  """
                  if mid == c4d.MSG_DESCRIPTION_POSTSETPARAMETER:
              
                      # Here we are in a scripting object with a user data parameter, so
                      # the parameter id is a bit more complex. We are checking if the
                      # raising element was the element with the id (c4d.ID_USERDATA, 1),
                      # i.e., the first user data element. In your case you would have to
                      # only check the first DescLevel in the DescID passed as the message
                      # data for MSG_DESCRIPTION_POSTSETPARAMETER.
                      descId = mdata["descid"]
                      if descId[0].id == c4d.ID_USERDATA and  descId[1].id == 1:
                          # When we are in the main-thread, open a dialog.
                          if c4d.threading.GeIsMainThread():
                              # Op is predefined in a scripting object. In case of a
                              # programming tag it is the tag itself. We simply query the
                              # tag for the value with the DescId which has been sent has
                              # the message data.
                              file = op[descId]
                              c4d.gui.MessageDialog("The the file is: {}".format(file))
              def main():
                  pass
              

              MAXON SDK Specialist
              developers.maxon.net

              1 Reply Last reply Reply Quote 0
              • mocolocoM
                mocoloco
                last edited by

                Wow, I couldn't have though that I have to watch c4d.MSG_DESCRIPTION_POSTSETPARAMETER.
                I'm starting to understand the way to use threading as well, thanks a lot for the help and tips, all is working perfectly now.

                1 Reply Last reply Reply Quote 0
                • mikeudinM mikeudin referenced this topic on
                • First post
                  Last post