CommandData.Message() implementation and Message concepts
-
Hi,
I'm writing my first plugin and I have a hard time understanding how to implement the Message method of the CommandData class.
The command is straightforward: it creates new shaders and the corresponding widgets for the Corona Node Material Editor. So in essence all the command is doing is modifying BaseList2D objects (shaders and node widgets) which are parented to another BaseList2D object (the Node Editor view).
In this case, using EventAdd() refresh the Node Editor view instantly and populate it with new nodes, shaders, and links. On the other hand, using C4DAtom.Message() gives partial results = broken objects and links. And for the Message method, I just don't get it, even after looking at the docs and the example plugins extensively.class AddFilterShader(c4d.plugins.CommandData): def Execute(self, doc): context = CoronaNodeContext() nodes = context.active_nodes active_view = context.active_view if nodes: parent_shader = set() for node in nodes: # Get the shader represented by the node widget node_shader = node[2001] # Skip if its a material if isinstance(node_shader, c4d.BaseMaterial): c4d.StatusSetText("Can't run on a material") continue # Filter shader filter = c4d.BaseShader(c4d.Xfilter) filter[c4d.SLA_FILTER_TEXTURE] = node_shader # We check for a parent node, if not # we parent to the Corona Node shaderhook parent = node_shader.GetUp() if parent is None: parent = node_shader.GetMain() # Add the parent for further operations parent_shader.add(parent) # Look for the link in the parent if isinstance(parent,c4d.BaseMaterial) or isinstance(parent, c4d.BaseShader) : parent_bc = parent.GetDataInstance() index = bc_get_id(parent_bc, node_shader) parent_bc.SetLink(index, filter) # Create a new widget for the shader cnode = create_node_widget(filter) # Insert shaders node_shader.InsertUnder(filter) if isinstance(parent,c4d.BaseMaterial): parent.InsertShader(filter) else: filter.InsertUnder(parent) # Insert widget cnode.InsertUnder(active_view) # Selection state node.DelBit(c4d.BIT_ACTIVE) cnode.SetBit(c4d.BIT_ACTIVE) # Update the node and the shaders # Doesn't work ? node.Message(c4d.MSG_UPDATE) parent.Message(c4d.MSG_UPDATE) # Update the view # Doesn't work ? active_view.Message(c4d.MSG_UPDATE) c4d.EventAdd() return True
As you can see I tried the C4DAtom.Message() command inside the Execute method but it didn't get any better.
So how should I use the Message method to get the same result as with EventAdd()? Or should I not bother and use the function? But even so, I'd like to understand the Message concept.
Thanks !
-
Hey @John_Do,
Thank you for reaching out to us. I must point out that your question is out of scope of support because you are using Corona. Thrid party libraries are out of scope of support as declared in our Forum Procedures. Please understand that we cannot run, debug, or help you with foreign APIs.
I'm writing my first plugin and I have a hard time understanding how to implement the Message method of the CommandData class.
You do not implement any
Message
method in your code, and also not in particularCommandData.Message
. I think you just misspoke a little, and meant to sayuse
. But since this has been placed so prominently in the title and first sentence, I had to point it out.So how should I use the Message method to get the same result as with EventAdd()? Or should I not bother and use the function? But even so, I'd like to understand the Message concept.
In general I would recommend to have a look at the Message Manual (which you might have already done), because there are quite a few misconceptions here.
There are three main message streams in Cinema 4D:
- Messages sent to scene elements such as objects, shaders, materials, etc. They are sent with
C4DAtom.Message
and.MultiMessage
. These messages always concern only the node(s) they are being sent to and do stuff like "hey your parameter xyz has changed", "hey, your button uvw has been clicked", "hey, could you please update your icon?". - Messages sent to the core of Cinema 4D. They can be sent in multiple ways but the must prominent one is
SendCoreMessage
. These message are sent to the core of Cinema 4D but can also be received in a few other places like aMessageData
plugin or aGeDialog
. They convey information like "the scene just has been updated", "please redraw the whole UI", etc. I.e., broad information that concerns everyone. - The third category are GUI messages which are irreverent in this case.
MSG_UPDATE
is a scene element message which is sent to nodes when they have been manipulated and must update their internal data. A common example are point objects, you must sendMSG_UPDATE
to them when you have changed their points. But sendingMSG_UPDATE
is the exception, and you usually do not have to do it for pure parameter manipulation. Stacking shaders together does not require anyMSG_UPDATE
call. At least in our API, Chaos Group could be doing things differently with Corona (but that seems a bit unlikely).EventAdd
is adds an item to the event queue in Cinema 4D and corresponds to the core messageEVMSG_CHANGE
. This mechanism primarily exists so that the GUI and other secondary systems can catch up with the primary data, the scene graph. You call it when you add for example a cube to scene. The cube will be part of your scene as soon as you callBaseDocument.InsertObject
. But only when you callEventAdd
, it will be reflected in the UI, have its caches built, etc.¹So, your question does not make too much sense, and all the
MSG_UPDATE
calls are likely not required in the first place.What is more likely going wrong here, is how you assemble your shader graph. For your first plugin, this little stretch of code is trying to do a lot all at once. And especially when I would run into problems, I would try to reduce that. In play comes here then how the Corona API works. It is probably best when you ask the Corona team for help with that.
Cheers,
Ferdinand¹ An added event will not be carried out right away but when the event queue is resolved. I.e., having one
EventAdd
at the end of your operation is equivalent to having multiple calls in one operation.EventAdd
will only be carried out once you give control back to Cinema 4D.CommandData
plugins also callEventAdd
on their own after theirExecute
ran, so you do not have to do it yourself. - Messages sent to scene elements such as objects, shaders, materials, etc. They are sent with
-
Hi @ferdinand,
As always thank you very much for the detailed answer. Please mind that I'm not a seasoned programmer at all, not even a programmer. That doesn't excuse anything, but there are likely many mistakes in my messages.
Thank you for reaching out to us. I must point out that your question is out of scope of support because you are using Corona. Thrid party libraries are out of scope of support as declared in our Forum Procedures. Please understand that we cannot run, debug, or help you with foreign APIs.
I know that Corona is out of the scope of this forum since I've already posted some questions about it, and we already discussed about it. But the objects and data particular to Corona are based on the class and methods of the Cinema 4D API as far as I can tell. If it is still out of scope let me know. I've already mailed the Corona devs and had a bit of help few years ago but that's all. I made a diagram recently that shows how the Node Editor is integrated through the API.
Messages
Thanks for the explanation regarding messages, but I still don't get it. I think it doesn't make sense to me that I have to tell Cinema something has changed since something has really changed ( = through the interpreted Python code) from my point of view when executing the code. My lack of programming background maybe doesn't help me here.
So in most cases, messages are used to trigger UI events / updates?
How do I know if I have to send a message or not to an object to update it ( or it's representation in the current document )?
How should I leverage CommandData.Message() and what is the difference with calling C4DAtom.Message / SendCoreMessage in Execute ? I'm really puzzled here.What is more likely going wrong here, is how you assemble your shader graph. For your first plugin, this little stretch of code is trying to do a lot all at once. And especially when I would run into problems, I would try to reduce that. In play comes here then how the Corona API works. It is probably best when you ask the Corona team for help with that.
It's quite possible, but the thing is the code do exactly what I want (inserting a filter shader on top of any selected shader). But maybe I take too many shorcuts or that things are done in a unorthodox way. What would be the proper way to do this ? Split what's happening in Execute into multiple methods ?
¹ An added event will not be carried out right away but when the event queue is resolved. I.e., having one EventAdd at the end of your operation is equivalent to having multiple calls in one operation. EventAdd will only be carried out once you give control back to Cinema 4D.
Is it a problem ? The command is really simple and linear, really it's just a script wrapped in a class' method.
CommandData plugins also call EventAdd on their own after their Execute ran, so you do not have to do it yourself.
I suspected that but I don't get the same result if I comment out EventAdd. Ui doesn't update and links are missing on the node widgets. So what's happening here ?
-
Hey @John_Do,
you can add as many
EventAdd
as you want, it does not really hurt. All that will happen is that Cinema 4D will see that there is already an event and then do nothing. Technically speaking, it can make sense to have multipleEventAdd
calls in a row - when you set theflags
inc4d.EventAdd(flags=EVENT_NONE)
as your call might then add a flag to the next upcoming event.It is a bit odd that you need an
EventAdd
at the end of yourCommandData:Execute
, but your screen grab clearly shows that it is necessary, so just keep it there. Maybe I also just misremembered something there.But I am a bit confused to what is now concretely not working.
Cheers,
Ferdinand