c4d.gui.QuestionDialog() in Python plugin makes c4d crash every time
-
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! -
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 inTagData.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 isNodeData.Message
and listening for a button click. There you can then check for being on the main thread withc4d.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 -
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()
, butFILENAME 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 anotherTYPE
is changed with this particularFILENAME
.I will read all this and see if I can figured out with this.
Thanks a lot,
Christophe -
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!
-
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 themessage
function in a programming tag is slightly different.Cheers,
FerdinandThe scene file:
example.c4dThe 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
-
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. -