Axis lock/unlock in message function / UI update?
-
Hi, I’m working with a Python Tag (not a plugin) attached to an object.
The goal is to only let the user scale the object if the document is in objectMode.So, I try setting the XYZ axis states accordingly within the message function.
This kind of works..
_ transformHandles are instantly updated
_ but the XYZ axis icons in the main UI ain't always updated..
_ hovering over the XYZ icons will update the state (which is far from ideal)So, how to update/redraw the generic C4D user interface?..
And is there a cleaner/better way to catch some messageType/Event to handle this?Tia, Jochem
import c4d doc: c4d.documents.BaseDocument op: c4d.BaseTag def message(msg_type, data): if msg_type == c4d.MSG_NOTIFY_EVENT: event_data = data['event_data'] ### ---------------------- the part reacting to UserData buttons ---------------------- ### if event_data['msg_id'] == c4d.MSG_DESCRIPTION_COMMAND: desc_id = event_data['msg_data']['id'] try: if desc_id[1].id == 18: print("some udButton press..") except (IndexError): pass ### ---------------- below: the part reacting to UserData changes ----------------- ### elif event_data['msg_id'] == c4d.MSG_DESCRIPTION_POSTSETPARAMETER and op.GetObject().GetUserDataContainer(): if not c4d.threading.GeIsMainThread(): return print("some udData change..") ### ------------------ which messageType/Event?.. now kind of hacky.. ----------------- ### if doc.GetAction() == 200000089 and c4d.threading.GeIsMainThread(): # scale/mainThread.. if doc.GetMode() == 1: # objMode.. for ax in [12153,12154,12155]: if c4d.IsCommandChecked(ax) == True: c4d.CallCommand(ax) #print("unLocked") elif doc.GetMode() != 1: for ax in [12153,12154,12155]: if c4d.IsCommandChecked(ax) == False: c4d.CallCommand(ax) #print("locked") # this kind of works.. # _ transformHandles are instantly updated # _ but the XYZ axis icons in the main UI ain't always updated.. # _ hovering over the XYZ icons will update the state # so, how to update/redraw the generic C4D user interface?.. # _ & is there a better solution for the above if-block.. def main(): # listen to buttonPresses on host if not op.GetObject().FindEventNotification(doc, op, c4d.NOTIFY_EVENT_MESSAGE) and op[c4d.EXPRESSION_ENABLE] == True: bc = c4d.BaseContainer() op.GetObject().AddEventNotification(op, c4d.NOTIFY_EVENT_MESSAGE, 0, bc) #...
-
Hi Jochem,
Please excuse a long delay in my answer.
Your question would be so much easier to process if there was less fuzz in what behavior you're actually expecting to achieve. Screenshot of what cinema 4d part you're talking about would not hurt either
Regarding your question. First of all, I assume that you're talking about XYZ-axis mode buttons:
Secondly, I assume you would like to control the state (and the functionality) of these mode buttons from the python tag. In other worlds, it is not scale tool axis handles, you're talking about here.The answer then is that you're breaking the threading rule, namely the function c4d.CallCommand() is not allowed in python tag:
You can find more info on the threading stuff in our Threading Manual.
Unfortunately, I don't see any good way for you to achieve this effect from the python tag, as the implementation of these functions is burried in our legacy code and you cannot reach out to them from outside. You could try playing around with c4d.StopAllThreads() function, but I doubt you can achieve any consistent result even with it.
Cheers,
Ilia -
Hi @i_mazlov
Thx for answering… I’ll try to be clearer - incl. screenshots next time :} & Yes, I was talking about the XYZ-axis mode buttons (and scaleTool).
Now I’m kind of confused, since I do try to stick to the threading rules. I can work around the axis modes, but my most important question is below:
One of the things mentioned on this forum by the devs was that the CallCommand should only be used from within the main thread.
That’s why I did put it in the message function, where I assume(d) it would be safe after:
“if c4d.threading.GeIsMainThread(): 'do some stuff..'”Isn't that kind of the same as from within a Python tag you can use CallCommands within a BUTTON function (where you can also use EventAdd()))?
-
Hey @jochemdk,
Thank you for reaching out to us. I must apologize, your posting was fine and your question was clear. And images are not necessary We also talked in our morning meeting about that you did (almost) everything right with checking for being on the main thread. @i_mazlov must have forgotten.
You seem to simply miss a
c4d.gui.GeUpdateUI()
call, as for me the script is then doing what it should. At least from my understanding of your intentions. But I removed also some fluff in your script. The propagation of the button press via an event notification is a bit iffy, but since you do the MT check, it should not be a problem.File: axis_toggle.c4d
Code:"""Demonstrates how to toggle the axis commands when a button is being pressed in a scripting object. """ import c4d doc: c4d.documents.BaseDocument op: c4d.BaseTag ID_BTN_AXIS: int = 1 def message(message: int, data: any) -> None: """Called by Cinema 4D to convey information to the scripting object. """ # When a button has been pressed, we want to toggle the axis commands. if message == c4d.MSG_DESCRIPTION_COMMAND: if not isinstance(data, dict): raise TypeError(f"Unexpected message data type: {type(data)}") # This is the relevant button which is being pressed. did: c4d.DescID = data["id"] if did.GetDepth() >= 2 and did[1].id == ID_BTN_AXIS: print("Button pressed.") # When the document is in scale tool mode and we are on the main thread, toggle all # active axis commands. if doc.GetAction() == c4d.ID_MODELING_SCALE and c4d.threading.GeIsMainThread(): for ax in [12153, 12154, 12155]: if not c4d.IsCommandChecked(ax): c4d.CallCommand(ax) # Force the UI of Cinema 4D to update. c4d.gui.GeUpdateUI() def main(): # I removed the event notification fluff of propagating user data actions from the host object # to the scripting tag, as it is not necessary for this example. pass
-
Thx a lot @ferdinand !!!
Sorry, was a bit busy the last days, but I'll check asap. -
Yes, a question, where did the "mark as solved" button go
-
We removed it because it was just extra work for us to close all the threads where people never bothered to mark them as solved. But I am happy that you seem to have found your solution.