How to pass a value from GeDialog to another class
-
This is probably a silly question, but I'm stuck and need some help so I can move forward in my plugin without using global dialog.
The original dialog has much more checkboxes and edit texts to guide the whole processing, but I've been able to get a very simplified sample of my code with a similar structure.I have a class
class processing
that needs to retrieve data from the dialog in order to process the script, but I really don't know how to get it since the code below is returning this error:
AttributeError: 'OptionsDialog' object has no attribute 'print_msg1'
I have to keep the structure like it is at the moment.
This the sample, any help would be awesome:
import c4d pluginVersion = "v1.0.0" pluginName = "Get Value From Dialog" PLUGIN_ID = 1111222 class OptionsDialog (c4d.gui.GeDialog): GROUP_OPTIONS = 1011 CHKBX1 = 1007 CHKBX2 = 1008 CHKBX3 = 1009 BTN_OK = 1010 def __init__(self): self._hasBeenInitalized = False self._hasLayoutDone = False def Message(self, msg, result): if msg.GetId() == c4d.BFM_ACTION: msg[c4d.BFM_ACTION_VALUE] return c4d.gui.GeDialog.Message(self, msg, result) def CreateLayout(self): self.SetTitle(pluginName + ' ' + pluginVersion) self.AddCheckbox(self.CHKBX1, c4d.BFH_SCALEFIT, initw=1, inith=1, name='Checkbox 1') self.AddCheckbox(self.CHKBX2, c4d.BFH_SCALEFIT, initw=1, inith=1, name='Checkbox 2') self.AddCheckbox(self.CHKBX3, c4d.BFH_SCALEFIT, initw=1, inith=1, name='Checkbox 3') if self.GroupBegin(self.GROUP_OPTIONS, c4d.BFH_CENTER, 1, 1): self.AddButton(self.BTN_OK, c4d.BFH_SCALEFIT, initw=500, inith=15,name='Go!') self.GroupEnd() self._hasLayoutDone = True self._hasBeenInitalized = False return True def Command(self, id, msg): if id==self.BTN_OK or id==c4d.IDC_OK: self.print_msg1 = self.GetBool(self.CHKBX1) self.print_msg2 = self.GetBool(self.CHKBX2) self.print_msg3 = self.GetBool(self.CHKBX3) self.Run() return True def InitValues(self): if self._hasBeenInitalized is True: return True self.SetBool(self.CHKBX1, True) self._hasBeenInitalized = True return True def Run(self): message = processing().set_nice_message() print(message) c4d.EventAdd() return True class processing: dlg = OptionsDialog() def set_nice_message(self): """ how can I get values from OptionsDialog() ??? """ if self.dlg.print_msg1: result = 'Hello world 1' if self.dlg.print_msg2: result = 'Hello world 2' if self.dlg.print_msg3: result = 'Hello world 3' return result class DialogCommandData(c4d.plugins.CommandData): dlg = None def Execute(self, doc): if self.dlg is None: self.dlg = OptionsDialog() return self.dlg.Open(dlgtype=c4d.DLG_TYPE_ASYNC, pluginid=PLUGIN_ID, xpos=-2, ypos=-2, defaultw=720, defaulth=50) def RestoreLayout(self, secret): if self.dlg is None: self.dlg = OptionsDialog() return self.dlg.Restore(pluginid=PLUGIN_ID, secret=secret) if __name__ == '__main__': myPlugin = DialogCommandData() c4d.plugins.RegisterCommandPlugin(id=PLUGIN_ID, str=pluginName, info=0, icon=None, help=pluginName, dat=myPlugin)
-
First of all, your dialog in the class processing is never used. So, you never open it or pass it or receive it from somewhere else. It just is generated, and then ... exists. Thus, the attempts in set_nice_message to read the print_msg attributes must fail... as these attributes are only generated when you press the OK button, and the class's local dialog is not open.
Remove the OptionsDialog() from the class processing.
Pass the actual dialog in the parameter list of set_nice_message.
Consequentially, remove the self.dlg from the body of that method, and replace it by the parameter.
In Run(), pass self (the real, actual dialog that is currently open) to set_nice_message(). -
@cairyn thanks for your time and helpful answer.
I was trying to retrieve the value of a variable from another class by calling it directly. This is why you could see OptionsDialog() within class processing. This is the source of my problems, because as far as I know, this is how a class variable should be called from another class.
Probably I'm wrong or things are different when there's a dialog involvedAnyways, this is the working code, with your solution addressed:
import c4d pluginVersion = "v1.0.0" pluginName = "Get Value From Dialog" PLUGIN_ID = 1111222 class OptionsDialog (c4d.gui.GeDialog): GROUP_OPTIONS = 1011 CHKBX1 = 1007 CHKBX2 = 1008 CHKBX3 = 1009 BTN_OK = 1010 def __init__(self): self._hasBeenInitalized = False self._hasLayoutDone = False def Message(self, msg, result): if msg.GetId() == c4d.BFM_ACTION: msg[c4d.BFM_ACTION_VALUE] return c4d.gui.GeDialog.Message(self, msg, result) def CreateLayout(self): self.SetTitle(pluginName + ' ' + pluginVersion) self.AddCheckbox(self.CHKBX1, c4d.BFH_SCALEFIT, initw=1, inith=1, name='Checkbox 1') self.AddCheckbox(self.CHKBX2, c4d.BFH_SCALEFIT, initw=1, inith=1, name='Checkbox 2') self.AddCheckbox(self.CHKBX3, c4d.BFH_SCALEFIT, initw=1, inith=1, name='Checkbox 3') if self.GroupBegin(self.GROUP_OPTIONS, c4d.BFH_CENTER, 1, 1): self.AddButton(self.BTN_OK, c4d.BFH_SCALEFIT, initw=500, inith=15,name='Go!') self.GroupEnd() self._hasLayoutDone = True self._hasBeenInitalized = False return True def Command(self, id, msg): if id==self.BTN_OK or id==c4d.IDC_OK: self.print_msg1 = self.GetBool(self.CHKBX1) self.print_msg2 = self.GetBool(self.CHKBX2) self.print_msg3 = self.GetBool(self.CHKBX3) self.Run() return True def InitValues(self): if self._hasBeenInitalized is True: return True self.SetBool(self.CHKBX1, True) self._hasBeenInitalized = True return True def Run(self): message = processing().set_nice_message(self.print_msg1, self.print_msg2, self.print_msg3) print(message) c4d.EventAdd() return True class processing: def set_nice_message(self, msg1, msg2, msg3): if msg1: result = 'Hello world 1' if msg2: result = 'Hello world 2' if msg3: result = 'Hello world 3' return result class DialogCommandData(c4d.plugins.CommandData): dlg = None def Execute(self, doc): if self.dlg is None: self.dlg = OptionsDialog() return self.dlg.Open(dlgtype=c4d.DLG_TYPE_ASYNC, pluginid=PLUGIN_ID, xpos=-2, ypos=-2, defaultw=720, defaulth=50) def RestoreLayout(self, secret): if self.dlg is None: self.dlg = OptionsDialog() return self.dlg.Restore(pluginid=PLUGIN_ID, secret=secret) if __name__ == '__main__': myPlugin = DialogCommandData() c4d.plugins.RegisterCommandPlugin(id=PLUGIN_ID, str=pluginName, info=0, icon=None, help=pluginName, dat=myPlugin)
-
Hello @danielsian,
Thank you for reaching out to us. It is good to hear that you have found a working solution. For future readers I want to emphasize however a few points.
- A
GeDialog
is a Python object like any other you can just pass it around. Either inside a method of that dialog and by passing outself
, i.e., the instance of the dialog. Or in the place where you did instantiate/attached your dialog, i.e.DialogCommandData
in your case. set_nice_message
is only formally a method ofprocessing
as it does not make use of the object instanceself
. You should either make the method a module function (i.e., a function without a class) or add the@staticmethod
decorator, so that you do not have to insatiate aprocessing
instance every time you want to call it, e.g., heremessage = processing().set_nice_message(self.print_msg1, self.print_msg2, self.print_msg3)
.
Find below an example which briefly demonstrates both points.
Cheers,
FerdinandThe result:
Ran <__main__.Processor object at 0x0000014E8F61CB10> for <__main__.MyDialog object at 0x0000014E8F828010>. Ran <__main__.Processor object at 0x0000014E8F61CAD0> for <__main__.MyDialog object at 0x0000014E8F828010>. Ran <__main__.Processor object at 0x0000014E8F61CA90> for <__main__.MyDialog object at 0x0000014E8F828010>. args: (42,), kwargs: {'object': <__main__.MyDialog object at 0x0000014E8F828010>, 'msg': "bob's your uncle."} >>>
The code:
import c4d class Processor(object): """Your processor class """ def Excecute(self, dialog): """The execution function of a processor. """ if not isinstance(dialog, c4d.gui.GeDialog): raise RuntimeError(f"Expected GeDialog for 'dialog': {dialog}") # Do stuff with the dialog ... print (f"Ran {self} for {dialog}.") # Some global processor instances g_processors = [Processor(), Processor(), Processor()] class MyDialog(c4d.gui.GeDialog): """The dialog which passes itself to all processor executions. """ def CallProcessors(self): """ The function in the dialog which calls the processors and passes the dialog itself. """ # Call all processors. for p in g_processors: p.Excecute(dialog=self) # Call the global function. SomeGlobalFunction(42, object=self, msg="bob's your uncle.") def SomeGlobalFunction(*args, **kwargs): """Just prints its arguments. """ print (f"args: {args}, kwargs: {kwargs}") def main(): """Entry point. """ dialog = MyDialog() dialog.CallProcessors() if __name__ == '__main__': main()
- A
-
@ferdinand Thank you for your awesome explanation!
I will keep this post as a reference for my studies as your approach seems a bit complicated for my understanding at the moment.
I'm learning a lot with your contribution in this forum, and in this case here it's not gonna be different.
Cheers