Send message from Modal dialog to Async (main) dialog
-
Hi there!
Currently running into an issue that seems a little bit like a noob question, but with my main dialog, I open another Modal dialog to edit some settings. Now, I'd like to click a button in the Modal dialog, which should send the message id of clicking that button to the main async dialog.
How would I do that? Thank you!
-
Solved it!
def decodeMessage(message): # As taken from https://developers.maxon.net/docs/py/2023_2/manuals/misc/python3_migration.html pythonapi.PyCapsule_GetPointer.restype = c_int pythonapi.PyCapsule_GetPointer.argtypes = [py_object] return pythonapi.PyCapsule_GetPointer(message.GetVoid(c4d.BFM_CORE_PAR1), None)
Thanks to these two links:
https://developers.maxon.net/forum/topic/8098/10538_specialeventadd-data/2
https://developers.maxon.net/docs/py/2023_2/manuals/misc/python3_migration.html -
Hello @HerzogVonWiesel,
Thank you for reaching out to us. Using the message system could be possible here but seems a bit overkill. Since you own both implementations, it would be best if you simply tie together the dialog instances you want to exchange information between. E.g.:
import c4d class ConnectedDialog(c4d.gui.GeDialog): """Realizes a dialog type which has a binding to another dialog instance. Note that all methods in this example are not part of the GeDialog interface scheme, i.e., "custom" methods. """ def __init__(self) -> None: """ """ # The list of dialogs this dialog instance does exchange information with. self._connectedDialogs: list[ConnectedDialog] = [] super().__init__() def Bind(self, other: "ConnectedDialog", twoWay: bool = True) -> None: """Binds the dialog #other to #self and makes the connection optionally two-way. Also ensures that bindings are unique, i.e, two dialogs cannot be bound more than once in one direction. """ if not isinstance(other, ConnectedDialog): raise TypeError(f"{other = }") if other not in self._connectedDialogs: self._connectedDialogs.append(other) if twoWay and self not in other._connectedDialogs: other._connectedDialogs.append(self) def SpecialMessage(self, sender: "ConnectedDialog", *args) -> None: """Receives message stream from all connected dialogs. """ print (args) def Action(self, value: any, condition: any) -> None: """Exemplifies a method which informs all other dialog instances about an event. """ if condition: for dlg in self._connectedDialogs: dlg.SpecialMessage(self, value, condition) if __name__ == "__main__": # Instantiate five dialogs and creating bindings between all of them. dialogCollection: tuple[ConnectedDialog] = (ConnectedDialog() for _ in range(5)) for a in dialogCollection: for b in dialogCollection: a.Bind(b)
You could do three million and one thing differently here; this is just an example to illustrate a pattern. The crucial information might be here for you (since we just talked about threading and dialogs before) that:
The methods both of modal and async dialogs run on the main thread. Async in an async dialog are only the drawing routines which you do not have access to, even when you implement a dialog with a custom GeUserArea
. That area only enqueues drawing instructions into a buffer and does not do the actual drawing. So, there is no danger of access violations, which Python of course does not know in the first place due to its GIL.When tying dialogs together is not possible then you can use the message system of Cinema 4D. But tying objects together is always possible in Python even when the objects live in two modules which do not have access to each other. You can either use sockets (a bit overkill) or be lazy and just setup shop in a commonly accessible object, e.g., the
sys
module.When you go for messages, I would recommend having a look at the Message Manual first as I gave there a rough overview. In short:
GeDialog.Message
is for UI messages and just like itsNodeData.Message
counter part instance specific. It is not meant to establish a binding between two dialog instances but to let Cinema 4D or elements in a UI tree communicate with the dialog ("button to dialog: I have been pressed").- What you can do, is set off a core event with
c4d.SpecialEventAddd
to then catch that core message in all other dialogs usingGeDialog.CoreMessage
.- Note that the Python API does filter message streams, and unlike in C++, you cannot just "invent" a new message type, except for using
c4d.SpecialEventAdd
. But there you are limited to sending three integers in Python (in C++ thep1
andp2
arguments are meant to be pointers to arbitrary data).
- Note that the Python API does filter message streams, and unlike in C++, you cannot just "invent" a new message type, except for using
Cheers,
FerdinandPS: Yeah using the CPython API you can cast/wrap things (in order to use
p1
andp2
actually in the manner they are intended to) but be aware that any C magic is not officially supported by us.