Change an attribute from a dialogue plug-in
I have a plug-in that lists objects along with their attributes. It updates correctly, but I want the update to be mutual.
Currently, the scene/object manager updates the plug-in but it does not work the other way around.
You can see the illustration of the problem here:
In the same block, I have this lines:
# retrieve the data from the scene/object manager self.SetString(text_ID, value=object.GetName()) # retrieve data from the plug-in and apply the changes to the scene/object (This block is not working as seen in the video above) newString = self.GetString(text_ID) object.SetName(newString)
Is there a way around this?
Thank you for looking at my problem.
ferdinand last edited by
have you tried invoking
object.Message(c4d.MSG_UPDATE)after you changed the name?
Thanks for the response.
Yes, I tried both commands but it does not work as expected (i.e. typing something in the plug-in does not change the object)
Also I think
c4d.EventAdd()is only necessary on script. And the
MessageI think only works if you are going to modify a parameter specific to an object (say a point). Correct me if I'm wrong.
c4d.EventAdd()is always needed when one changes the active document in the context of some user interaction. This includes user interaction in a GeDialog (Core Messages Manual).
You should react to user interaction in your GeDialog's Command() function. Can you share the complete code of your Command() function?
Sure sure. There is not much in the
Commandfunction except the return
Here is the working code so far (it 's the same code as I previously asked in my previous threads.
Thank you for looking at it.
import c4d import os from c4d import gui, plugins, bitmaps, utils, documents PLUGIN_ID = 1811321 class MyDialog(gui.GeDialog): def __init__(self): # Be sure that we have the object list before building the UI # as InitValues is called after CreateLayout() self.objectsList =  self.GetAllObjects() self.object_count = len(self.objectsList) def InitValues(self): # Use to Init and in Case of an Update needed. self.objectsList =  self.GetAllObjects() self.object_count = len(self.objectsList) self.Update() return True def GetNextObject(self, op ): # use a non-recursive method to iterate the hierarchy # https://developers.maxon.net/?p=596 # allow to not hit the recursive stack limit. if not op: return None if op.GetDown(): return op.GetDown() while not op.GetNext() and op.GetUp(): op = op.GetUp() return op.GetNext() def GetAllObjects(self): doc = c4d.documents.GetActiveDocument() op = doc.GetFirstObject() self.objectsList =  while op: self.objectsList.append(op) op = self.GetNextObject(op) def Update(self): # NEW CODE if self.object_count == 0: self.LayoutFlushGroup(1001) self.AddStaticText(0, c4d.BFH_CENTER, name="There are no objects in the scene") self.LayoutChanged(1001) if self.object_count > 0 : self.LayoutFlushGroup(1001) self.AddStaticText(0, 1, name="Name") self.AddStaticText(0, 0, name="Enable") self.AddStaticText(0, 0, name="Xray") self.AddSeparatorV(0) self.AddSeparatorV(0) self.AddSeparatorV(0) for idx, object in enumerate(self.objectsList): # where the glitch happens text_ID = 100000 + idx enable_ID = 200000 + idx xray_ID = 300000 + idx self.AddEditText(text_ID, flags=c4d.BFH_LEFT, initw=200, editflags=c4d.EDITTEXT_HELPTEXT) self.AddCheckbox(enable_ID, flags=c4d.BFH_LEFT, initw=20, inith=10, name="Enable") self.AddCheckbox(xray_ID, flags=c4d.BFH_LEFT, initw=20, inith=10, name="Xray") self.SetString(text_ID, value=object.GetName()) self.SetInt32(enable_ID, value=object[c4d.ID_BASEOBJECT_GENERATOR_FLAG]) self.SetInt32(xray_ID, value=object[c4d.ID_BASEOBJECT_XRAY]) newString = self.GetString(text_ID) object.SetName(newString) object.Message(c4d.MSG_UPDATE) self.LayoutChanged(1001) def CreateLayout(self): print "nombre d'objet ", self.object_count self.GroupBegin(id=1001, flags=c4d.BFH_FIT, cols=3, rows=20, title="Rigging") self.GroupEnd() self.Update() return True def Command(self, id, msg): return True def CoreMessage(self, id, msg): if id == c4d.EVMSG_CHANGE: self.InitValues() return True class MyMenuPlugin(plugins.CommandData): dialog = None def Execute(self, doc): if self.dialog is None: self.dialog = MyDialog() return self.dialog.Open(dlgtype=c4d.DLG_TYPE_ASYNC, pluginid=PLUGIN_ID, defaultw=200, defaulth=150, xpos=-1, ypos=-1) if __name__ == "__main__": plugins.RegisterCommandPlugin(PLUGIN_ID, "test_dynamic_update",0, None, "test_dynamic_update", MyMenuPlugin())
s_bach last edited by s_bach
as I said above, you have to handle user interaction in the dialog's Command() function. The function if called when the user interacts with the dialog's gadgets. Something like this:
def Command(self, id, msg): if id >= 100000 and id < 200000: idx = id - 100000 newString = self.GetString(id) obj = self.objectsList[idx] if obj is not None: c4d.StopAllThreads() obj.SetName(newString) c4d.EventAdd() return c4d.gui.GeDialog.Command(self, id, msg)
Thanks for the response. I used the code. It works but I'm unable to type anything other than a single stroke. I think the
c4d.EventAddupdates so fast.
You can check the illustration of the problem here:
I tried removing the
c4d.EventAdd(). It lets me type something but it only updates when I click outside the plug-in.
Is there a possibility of both worlds? Update only when I move to a next field (i.e. tab) perhaps?
you have to call
EventAdd()to inform Cinema that you have changed something.
But in your code, you call your Update() function after a
EVMSG_CHANGEmessage. In your Update() function, you delete and rebuild your UI, thus you loose the focus on the current UI gadget.
You must find a way to either not call Update() after you edit the name of the object or to restore the focus on the newly created UI.
Alternatively, you can check if the ENTER key way pressed and only update when this is the case (the user finished entering the string, see Interaction with Gadgets).
# get state of the "Enter" key. state = c4d.BaseContainer() res = c4d.gui.GetInputState(c4d.BFM_INPUT_KEYBOARD, c4d.KEY_ENTER, state) if res: if state[c4d.BFM_INPUT_VALUE] != 1: return False idx = id - 100000 newString = self.GetString(id) obj = self.objectsList[idx] if obj is not None: c4d.StopAllThreads() obj.SetName(newString) c4d.EventAdd()
This way, the dialog would behave similiar to the Attribute Manager.
Thanks for the response. I change the
It works as expected, but it doesn't continue to the next line when I hit tab. It goes back to the first line.
You can see an illustration of the problem here:
I was expecting when I hit
Tabfrom the fourth line it would continue to all the parameters until it reaches to the fifth line.
Is this possible? Or is this a limitation on the GetInputState?
as I said above: you rebuild your UI after each EVMSG_CHANGE. Thus, you loose the focus of your UI elements (since they are destroyed and rebuilt).
You have to find a way to avoid that unnecessary rebuild of your layout after user interaction in your dialog.
RE: You have to find a way to avoid that unnecessary rebuild of your layout after user interaction in your dialog.
I'm not really sure how to do that. Since I just butchered the code from a sample plug-in.
That will probably a problem for another thread.
Anyway, it's functional at the moment. I'll settle for this.
Thanks again and sorry for the trouble.
Will close the thread now.
Have a great day ahead!