Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    Send message from Modal dialog to Async (main) dialog

    Cinema 4D SDK
    python sdk
    2
    3
    661
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • HerzogVonWieselH
      HerzogVonWiesel
      last edited by

      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!

      ferdinandF 1 Reply Last reply Reply Quote 0
      • HerzogVonWieselH
        HerzogVonWiesel
        last edited by

        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

        1 Reply Last reply Reply Quote 0
        • ferdinandF
          ferdinand @HerzogVonWiesel
          last edited by ferdinand

          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 its NodeData.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 using GeDialog.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++ the p1 and p2 arguments are meant to be pointers to arbitrary data).

          Cheers,
          Ferdinand

          PS: Yeah using the CPython API you can cast/wrap things (in order to use p1 and p2 actually in the manner they are intended to) but be aware that any C magic is not officially supported by us.

          MAXON SDK Specialist
          developers.maxon.net

          1 Reply Last reply Reply Quote 0
          • First post
            Last post