ShowPopupDialog in SceneLoaderData
-
Hi. I have a sceneloader plugin that shows a popup menu in it's Load function. Until S26 it was working fine, but in S26 I get the following error when I call it:
result = gui.ShowPopupDialog(cd=None, bc=menu, x=c4d.MOUSEPOS, y=c4d.MOUSEPOS) RuntimeError:must be called from the main thread
I guess this has something to do with the new task management system, but how can approach this issue?
Thanks. -
This post is deleted! -
Hi,
things changed a bit for s26 as we can now load files asynchronously. I've send an email to our dev, because one solution would be to use ExecuteOnMainThread but this function does not exist on the python API.
Cheers,
Manuel -
Hi,
in s26, it is not possible to perform any GUI operation as the SceneLoaderData will not be called from the mainthread anymore.
Instead, you must use the description file when you register the plugin. Those options will be displayed when the user want to import the corresponding file, and they will be accessible from the preferences. The user will be able to save or load presets.
I created an example that we will post on github after being reviewed.Why you were displaying a dialog box that way in the first place?
import c4d PLUGIN_ID = 1059408 IES_IMPORT_PRINT_TO_CONSOLE = 10001 class ExampleDialog(c4d.gui.GeDialog): def CreateLayout(self): """ This Method is called automatically when Cinema 4D creates the Layout of the Dialog. Returns: bool: False if there was an error, otherwise True. """ # 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. 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 Ok") 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 class IESMetaLoader(c4d.plugins.SceneLoaderData): """IESMeta Loader""" dialog = None def Init(self, node): """ Called when a new instance of this object is created. In this context, this allow to define the option by default for the SceneLoaderPlugin that will be displayed to the user. Returns: bool: False if there was an error, otherwise True. """ # Define the default value for the parameters. self.InitAttr(node, bool, c4d.IES_IMPORT_PRINT_TO_CONSOLE) node[c4d.IES_IMPORT_PRINT_TO_CONSOLE] = True return True def Identify(self, node, name, probe, size): """ Cinema 4D calls this function for every registered scene loader plugin. This function should return True only if this plugin can handle the file. The parameter 'probe' contain a small part of the file, usually the 1024 first characters. This allow to check if the header of the file starts as expected, validating the fact this file can be read by the load function. Args: node (c4d.BaseList2D): The node object. name (str): The name of the loader. probe (memoryview): The start of a small chunk of data from the start of the file for testing this file type. Usually the probe size is 1024 bytes. Never call the buffer outside this method! size (int): The size of the chunk for testing this file type. Returns: bool: True if the SceneLoaderData can load this kind of files. """ # Check if the last three character are equal to 'txt' if "txt" in name[-3:]: # Check if the txt file start with the correct header. if bytes(probe[0:17]).decode().upper() == "IES Meta Exporter".upper(): return True return False def Load(self, node, name, doc, filterflags, error, bt): """ Called by Cinema 4D to load the file. This method is only called if the identify function returned True. The parameter 'node' allows to retrieve the options the user defined for this import. Args: node (c4d.BaseList2D): The node object representing the exporter. name (str): The filename of the file to save. doc (c4d.documents.BaseDocument): The document that should be saved. filterflags (SCENEFILTER): Options for the exporter. error (None): Not supported. bt (c4d.threading.BaseThread): The calling thread. Returns: FILEERROR: Status of the import process. """ dialogAllowed = bool(filterflags & c4d.SCENEFILTER_DIALOGSALLOWED) isMainThread = c4d.threading.GeIsMainThread() print ("is main thread {}".format(isMainThread)) print ("is dialog allowed? {}".format(dialogAllowed)) # GUI operation are not allowed if they are not executed from the main thread, always check # if this is the main thread. Check also if the flag is set to SCENEFILTER_DIALOGSALLOWED to # sure dialog can be displayed. if isMainThread: if dialogAllowed: # Open a GeDialog if self.dialog is None: self.dialog = ExampleDialog() self.dialog.Open( dlgtype=c4d.DLG_TYPE_ASYNC, pluginid=PLUGIN_ID, defaultw=400, defaulth=32 ) # Create and display a Popup Menu menu = c4d.BaseContainer() menu.InsData(1001, 'Item 1') menu.InsData(1002, 'Item 2') menu.InsData(0, '') # Append separator c4d.gui.ShowPopupDialog(cd=None, bc=menu, x=c4d.MOUSEPOS, y=c4d.MOUSEPOS) # Display the content of the file if the user check the option in the import options. # Opens the file in read mode and print all the lines if node[c4d.IES_IMPORT_PRINT_TO_CONSOLE]: with open(name, "r") as f: for line in f: print (line) return c4d.FILEERROR_NONE if __name__ == '__main__': c4d.plugins.RegisterSceneLoaderPlugin(id=PLUGIN_ID, str="Py-IES Meta (*.txt)", info = 0, g=IESMetaLoader, description="fies_loader", )
res file
CONTAINER fies_loader { INCLUDE Fbase; NAME fies_loader; GROUP IES_IMPORT_GROUP { DEFAULT 1; BOOL IES_IMPORT_PRINT_TO_CONSOLE {} } }
header file
#ifndef _FIES_LOADER_H__ #define _FIES_LOADER_H__ enum { fies_loader = 10000, IES_IMPORT_PRINT_TO_CONSOLE, IES_IMPORT_GROUP }; #endif
str file
STRINGTABLE fies_loader { fies_loader "IES Import Settings"; IES_IMPORT_GROUP "IES option group"; IES_IMPORT_PRINT_TO_CONSOLE "Print to console?"; }
Cheers,
Manuel -
I didn't display a dialog box, but a popup menu. I used it for drag&drop of a custom extension file. It was very convenient to prompt the user what to do with the dropped file. Anyway, I moved that logic to a messagedata plugin that gets called from the sceneloader and that way I can show the popup menu
-
Hello @kalugin,
without any further questions or other postings, we will consider this topic as solved and flag it as such by Friday, 17/06/2022.
Thank you for your understanding,
Ferdinand