CommandData with Options Dialog - Docked command button
-
Hi,
I have a
CommandData
plugin which opens an asynchronous dialog and also supports an options dialog (PLUGINFLAG_COMMAND_OPTION_DIALOG
).Everything is nice and dandy, until I dock the command into a palette in C4D's layout. In this case I get a shiny button with a cogwheel on the right side. Nice.
The problem is, if the command has been called to open the dialog (and thus is enabled, i.e. shows highlighted in menu), then the cogwheel part of the command button does no longer work. Instead of callingExecuteOptionID()
it then calls the normalExecute()
, which is obviously not what I would expect. From menu, this works as expected, regardless of the command's state, clicking the cogwheel in menu callsExecuteOptionID()
, but not so from a button palette.Is this intended operation or maybe a bug?
I have tested the behavior in R21 and S24.
Cheers,
AndreasEdit: I forgot to mention, all my test were done on Win 10.
-
Hello Andreas,
thank you for reaching out to us. And special thank you for making the effort of describing and testing bugs in such a detailed manner as you do and have done in the past, much appreciated.
With that being said, I struggle a bit with reproducing your problem. I might be misunderstanding something here.
- You talk about a
CommandData
being highlighted in the menu when the dialog is opened. But that is not the case forCommandData
in my experience. Other than for tool plugins, e.g.,ToolData
in Python, their icon is not being highlighted "when they are running". I double checked in S24 and R21 and could not reproduce this. The icon of aCommandData
is only being highlighted when hovered. It can be enabled or disabled via overwritingCommandData.GetState
, but that has nothing to do with an execution state or however wants to call this. - Regarding the reported mixup of
Execute()
andExecuteOptionID()
, I can unfortunately also not reproduce this. I opened the option dialog of the example plugin shown at the end of the posting both in R21 and S24. Then I added the plugin as an icon to a palette and invoked the major and minor/option gadget of that icon while dialog was still opened. In all versions Cinema did execute the correct method.
So, I would have to ask you to clarify where I have misunderstood you or share some code, so, that we can to the bottom of why this is happening for you.
Cheers,
FerdinandWhat I did (in S24):
The code:
"""Example for executing an options dialog. As discussed in: https://developers.maxon.net/forum/topic/13407/ """ import c4d class PluginDialog (c4d.gui.GeDialog): """A dialog for a CommandData plugin. """ ID_INT_VALUE = 1000 def CreateLayout(self): """Adds gadgets to the dialog. """ self.SetTitle("Some Dialog") self.AddEditSlider( id=PluginDialog.ID_INT_VALUE, flags=c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT) return True def InitValues(self): """Initialize the dialog values. """ self.SetInt32(PluginDialog.ID_INT_VALUE, 42) return True class PC13407(c4d.plugins.CommandData): """A CommandData plugin with a dialog. """ ID_PLUGIN = 1057432 _pluginDialog = None @classmethod def GetPluginDialog(cls): """Returns the class bound instance of the plugin dialog. """ if cls._pluginDialog is None: cls._pluginDialog = PluginDialog() return cls._pluginDialog def Execute(self, doc): """ """ print("Running {}.Execute()".format(self)) return True def ExecuteOptionID(self, doc, plugid, subid): """Opens the option dialog. """ print("Running {}.ExecuteOptionID()".format(self)) dialog = PC13407.GetPluginDialog() result = True if not dialog.IsOpen(): result = dialog.Open(dlgtype=c4d.DLG_TYPE_ASYNC, pluginid=PC13407.ID_PLUGIN) return result def RestoreLayout(self, secret): """Restores the dialog on layout changes. """ dialog = PC13407.GetPluginDialog() return dialog.Restore(PC13407.ID_PLUGIN, secret) if __name__ == '__main__': c4d.plugins.RegisterCommandPlugin( id=PC13407.ID_PLUGIN, str="Options Test", info=c4d.PLUGINFLAG_COMMAND_OPTION_DIALOG, icon=None, help="", dat=PC13407())
- You talk about a
-
Hi Ferdinand,
thanks for your quick reply.
Please find a small plugin to reproduce below.
Looks like you thanked me too early for giving detailed bug reports, because obviously I did not mention an important detail (GetState()
)...As far as I know, every manager dialog in C4D gets opened via
CommandData
, don't they? And all of these represent their open state by highlighting/checking their command usingGetState()
. And this is what I'd like to reproduce, except that my manager not only has a main dialog, but also makes use of an additional "options command".So in order to reproduce, use below plugin.
Then:- Customize a palette and drag the "Test Command Button" into it.
- You should get a button with a cogwheel.
- Click the cogwheel to see "ExecuteOptionID" being printed to the console.
- Click the left/main section of the button to have it open its dialog (now, thinking about it, the dialog is redundant here, any state change would do... anyway too lazy to change the example code again...)
- Now, the palette button is highlighted.
- Press the cogwheel again.
- See "Execute" being printed to the console, instead of the expected "ExecuteOptionID".
Thanks for looking into it.
Cheers,
Andreasimport c4d PLUGIN_ID = 1234567 # MAKE SURE TO USE A UNIQUE ID FROM Plugin Café PLUGIN_NAME = 'Test Command Button' PLUGIN_TOOLTIP = 'Dock command to layout palette' class DialogMain(c4d.gui.GeDialog): def CreateLayout(self): self.SetTitle('{0}'.format(PLUGIN_NAME)) self.AddDlgGroup(c4d.DLG_OK) return True def Command(self, id, msg): if id == c4d.DLG_OK: self.Close() return True class CommandDataTest(c4d.plugins.CommandData): _dlgMain = None def Execute(self, doc): print('Execute') if self._dlgMain is None: self._dlgMain = DialogMain() if self._dlgMain.IsOpen(): self._dlgMain.Close() return True self._dlgMain.Open(c4d.DLG_TYPE_ASYNC, PLUGIN_ID, xpos=-1, ypos=-1, defaultw=300, defaulth=0) return True def ExecuteOptionID(self, doc, plugid, subid): print('ExecuteOptionID') return True def GetState(self, doc): state = c4d.CMD_ENABLED if self._dlgMain is not None and self._dlgMain.IsOpen(): state |= c4d.CMD_VALUE return state if __name__ == '__main__': c4d.plugins.RegisterCommandPlugin(PLUGIN_ID, PLUGIN_NAME, c4d.PLUGINFLAG_COMMAND_OPTION_DIALOG, None, PLUGIN_TOOLTIP, CommandDataTest())
-
Hello Andreas,
thank you for the clarification,
CMD_VALUE
was indeed the part that was not obvious to me. I can confirm the behavior and would also consider it to be a bug, especially since there has been a bug in 18.04 which has been fixed and was similar in nature.There is unfortunately not much what you can do currently regarding workarounds. At least I do not see one. This is because the root of the problem lies within the handling of palettes (and might be related to/ a side effect of the fix of the 18.04 issue) and therefore far outside of the wiggle room of the public API/SDK. I have reached out to the dev who fixed the older bug, and we will see if we want to consider this to be a bug or an accepted limitation.
I will give you here a status update on what we will do once I know it.
Cheers,
FerdinandThe pruned plugin example to reproduce this:
"""Example for buggy ExecuteOptionID behavior when a CommandData state is c4d.CMD_ENABLED | c4d.CMD_VALUE. As discussed in: https://developers.maxon.net/forum/topic/13407/ """ class PC13407(c4d.plugins.CommandData): """A CommandData plugin implementing GetState and being registered with PLUGINFLAG_COMMAND_OPTION_DIALOG. The CommandData has an internal toggle to either set it as checked, i.e., c4d.CMD_VALUE, or not. Which is here being toggled in Execute() and then reflected in GetState(). The problem which then arises is that when the plugin icon is docked into a palette, and brought into the c4d.CMD_VALUE state, that Cinema will not distinguish properly anymore between clicks onto the major gadget, the icon, and the cogwheel next to it. The user looses then in this state the ability to invoke ExecuteOptionID() via clicking onto the cogwheel. Clicking onto the cogwheel will then invoke Execute() instead. When invoking the plugin from a menu, e.g., the Extensions menu, this does not happen. """ ID_PLUGIN = 1057432 _isActive = False def Execute(self, doc): PC13407._isActive = not PC13407._isActive print(f'Execute(), _isActive: {PC13407._isActive}') return True def ExecuteOptionID(self, doc, plugid, subid): print('ExecuteOptionID()') return True def GetState(self, doc): return (c4d.CMD_ENABLED if not PC13407._isActive else c4d.CMD_ENABLED | c4d.CMD_VALUE) if __name__ == '__main__': c4d.plugins.RegisterCommandPlugin( id=PC13407.ID_PLUGIN, str="Options Test", info=c4d.PLUGINFLAG_COMMAND_OPTION_DIALOG, icon=None, help="", dat=PC13407())
-
Hi Ferdinand,
thanks for the confirmation and looking into it.
While it's a bit unfortunate, I do not consider this a major road block. From my side this thread can be considered close.
Cheers
-