Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush Python API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    PluginMessage and pycobject [SOLVED]

    PYTHON Development
    0
    16
    1.8k
    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.
    • H
      Helper
      last edited by

      On 13/10/2014 at 07:17, xxxxxxxx wrote:

      I am using c4d.GePluginMessage() to send a message to another plugins - PluginMessage().
      It is working for the id. I do receive the correct id, but getting the data is more difficult.
      Data in PluginMessage() is a PyCObject and I do not know jow to set and get parameters from a PyCObject?

      In Plugin nr 2 (sending) :
           c4d.GePluginMessage(PLUGIN_ID1, None)

      In plugin nr 1 (receiving) :
      def PluginMessage(id, data) :
          print "Received in NR1 - id, data: ", id, data
          return True

      1 Reply Last reply Reply Quote 0
      • H
        Helper
        last edited by

        On 14/10/2014 at 07:53, xxxxxxxx wrote:

        Or is there another way to communicate and pass data between plugin (not using shared memory)?

        1 Reply Last reply Reply Quote 0
        • H
          Helper
          last edited by

          On 14/10/2014 at 08:49, xxxxxxxx wrote:

          Hello,

          Another way may be to use SpecialEventAdd[URL-REMOVED]. Both ways use rough memory in form of a PyCObject. You find some examples on that in this threads:

          1. SpecialEventAdd data

          2. How to identify different calls to CoreMessage?

          best wishes,
          Sebastian


          [URL-REMOVED] @maxon: This section contained a non-resolving link which has been removed.

          1 Reply Last reply Reply Quote 0
          • H
            Helper
            last edited by

            On 14/10/2014 at 10:08, xxxxxxxx wrote:

            Can someone please make a working example of this rather than posting bit's and pieces?
            Everything works as expected in C++ for me. I can send and receive casted object data using a custom message. Or using the GePluginMessage() function.
            But I cannot get any of these things to work in Python.

            All I need to see is a very simple GeDialog plugin with one button in it that sends a message with a value in it. Any value is fine. But I'd prefer the value be based on the selected object.
            And a very simple tag plugin that just receives the message and gets the data.

            All these little bits and pieces spread over the forums are too confusing. And missing important pieces.

            Thanks,
            -ScottA

            1 Reply Last reply Reply Quote 0
            • H
              Helper
              last edited by

              On 14/10/2014 at 12:23, xxxxxxxx wrote:

              Here a working example thanks to Sebastian and the other Post posters, thanks.
              One small issue: when the input value (the value to send taken from the input slider) that is send as p2, is zero (p2=0), I get the error:

              TypeError: PyCObject_AsVoidPtr with non-Cobject.

              The code below is for two command plugins nr1 and nr2.
              Plugin nr2 sends specialevent to nr1 where is is received using CoreMessage and output into the dialog.

              I made it in R15.
              If you want the sources, send me a pm.

              -Pim

              Plugin nr:

                
              import c4d
              from c4d import gui, plugins, utils, bitmaps
              import os
              import sys
              from ctypes import pythonapi, c_void_p, py_object
                
              PLUGIN_ID1  = 1014874   #plugin nr1
              PLUGIN_ID2  = 1014873   #plugin nr2
                
              class MyDialog(gui.GeDialog) :
                
                  def CreateLayout(self) :
                      self.AddStaticText(id=1024, flags=c4d.BFH_LEFT, initw=150, name="Value to send", borderstyle=c4d.BORDER_NONE)
                      self.AddEditSlider(id=1025, flags=c4d.BFH_SCALEFIT, initw=70)
                      self.AddButton(1026, c4d.BFV_MASK, initw=100, name="Send to nr1")
                      return True    
                
                  def Command(self, id, msg) :      
                      if (id == 1026) :
                          print "self.GetInt32(1025) : ", self.GetInt32(1025)
                          c4d.SpecialEventAdd(PLUGIN_ID1, p1=PLUGIN_ID1, p2=self.GetInt32(1025))      
                          return True       
                      
                      return True
                
              class PluginNr2(plugins.CommandData) :
                  dialog = None
                  
                  def Execute(self, doc) :
                  # create the dialog
                     if self.dialog is None:
                        self.dialog = MyDialog()
                     return self.dialog.Open(dlgtype=c4d.DLG_TYPE_ASYNC, pluginid=PLUGIN_ID2, defaultw=200, defaulth=150, xpos=-1, ypos=-1)
              		
                  def RestoreLayout(self, sec_ref) :
                  # manage the dialog
                     if self.dialog is None:
                        self.dialog = MyDialog()
                     return self.dialog.Restore(pluginid=PLUGIN_ID2, secret=sec_ref)
              	   
              if __name__ == "__main__":
                
                  path, fn = os.path.split(__file__)
                  bmp = bitmaps.BaseBitmap()                                 #We need an instance of BaseBitmap class to use an image  
                  bmp.InitWith(os.path.join(path, "res/icons/", "icon.tif")) #The location where the menu image exists
                
                  plugins.RegisterCommandPlugin(PLUGIN_ID2, "Plugin r15 nr2",0, bmp, "Plugin r15 nr2", PluginNr2())  
                
              

              Plugin nr1:

                
              import c4d
              from c4d import gui, plugins, utils, bitmaps
              import os
              from ctypes import pythonapi, c_void_p, py_object
                
              PLUGIN_ID1  = 1014874   #plugin nr1
              PLUGIN_ID2  = 1014873   #plugin nr2
                
              class MyDialog(gui.GeDialog) :
                
                  def CreateLayout(self) :
                      self.AddStaticText(id=1024, flags=c4d.BFH_LEFT, initw=150, name="Value received", borderstyle=c4d.BORDER_NONE)
                      self.AddEditNumber(id=1025, flags=c4d.BFH_SCALEFIT, initw=70)
                      return True    
                
                  def Command(self, id, msg) :      
                      return True
                
                  def CoreMessage(self, id, msg) :
                      if id == PLUGIN_ID1:
                          # Get the Void Container
                          P1MSG_UN = msg.GetVoid(c4d.BFM_CORE_PAR1)
                          
                          # Get the actual data (This is beyond my knowledge but it works!)
                          pythonapi.PyCObject_AsVoidPtr.restype = c_void_p
                          pythonapi.PyCObject_AsVoidPtr.argtypes = [py_object]
                          P1MSG_EN = pythonapi.PyCObject_AsVoidPtr(P1MSG_UN)
                          
                          P2MSG_UN = msg.GetVoid(c4d.BFM_CORE_PAR2)
                          pythonapi.PyCObject_AsVoidPtr.restype = c_void_p
                          pythonapi.PyCObject_AsVoidPtr.argtypes = [py_object]
                          P2MSG_EN = pythonapi.PyCObject_AsVoidPtr(P2MSG_UN)
                          
                          print "Data received CoreMessage in MyDialog: ", P1MSG_EN, P2MSG_EN
                          self.SetInt32(1025, P2MSG_EN)
                      
                      return True
                
              class PluginNr1(plugins.CommandData) :
                  dialog = None
                  
                  def Execute(self, doc) :
                  # create the dialog
                     if self.dialog is None:
                        self.dialog = MyDialog()
                     return self.dialog.Open(dlgtype=c4d.DLG_TYPE_ASYNC, pluginid=PLUGIN_ID1, defaultw=200, defaulth=150, xpos=-1, ypos=-1)
              		
                  def RestoreLayout(self, sec_ref) :
                  # manage the dialog
                     if self.dialog is None:
                        self.dialog = MyDialog()
                     return self.dialog.Restore(pluginid=PLUGIN_ID1, secret=sec_ref)
                  
              if __name__ == "__main__":
                
                  path, fn = os.path.split(__file__)
                  bmp = bitmaps.BaseBitmap()                                 #We need an instance of BaseBitmap class to use an image  
                  bmp.InitWith(os.path.join(path, "res/icons/", "icon.tif")) #The location where the menu image exists
                
                  plugins.RegisterCommandPlugin(PLUGIN_ID1, "Plugin r15 nr1",0, bmp, "Plugin r15 nr1", PluginNr1())  
                
              
              
              1 Reply Last reply Reply Quote 0
              • H
                Helper
                last edited by

                On 14/10/2014 at 14:18, xxxxxxxx wrote:

                Thank you Pim.
                That helps me out a lot. 👍

                -ScottA

                1 Reply Last reply Reply Quote 0
                • H
                  Helper
                  last edited by

                  On 15/10/2014 at 00:59, xxxxxxxx wrote:

                  Ok, I'm glad to do something in return.

                  1 Reply Last reply Reply Quote 0
                  • H
                    Helper
                    last edited by

                    On 15/10/2014 at 07:52, xxxxxxxx wrote:

                    The code only handle integer > 0.
                    When the value received was 0, it gave an error (perhaps because 0 = Null pointer?).
                    Here some code to handle positive and negative integers and 0 itself.
                    I still do not know how to send strings, because SpecialEventAdd requires two intergers P1 and P2

                                # c_int: Handle integer value negative and positive
                                P2MSG_UN = msg.GetVoid(c4d.BFM_CORE_PAR2)
                                pythonapi.PyCObject_AsVoidPtr.restype = c_int             #was c_void_p
                                pythonapi.PyCObject_AsVoidPtr.argtypes = [py_object]
                                
                                try:    #handle value of 0
                                    P2MSG_EN = pythonapi.PyCObject_AsVoidPtr(P2MSG_UN)
                                except TypeError:
                                    P2MSG_EN = 0
                    

                    Note: Who put [solved] in the Post header?

                    1 Reply Last reply Reply Quote 0
                    • H
                      Helper
                      last edited by

                      On 15/10/2014 at 08:02, xxxxxxxx wrote:

                      Sorry, I thought this was solved. I will remove the "Solved".

                      Best wishes,
                      Sebastian

                      1 Reply Last reply Reply Quote 0
                      • H
                        Helper
                        last edited by

                        On 16/10/2014 at 07:31, xxxxxxxx wrote:

                        Hello,

                        there may be a way to avoid any handling of a "PyCObject" object. You can set a value in Cinema's WorldContainer[URL-REMOVED] and then send your message. When your target objects catch that message they find the proper values again in the WorldContainer:

                        Sending the message:

                          
                              worldContainer = c4d.GetWorldContainerInstance()  
                              worldContainer.SetString(1000100,"This is just some string!")  
                                
                              c4d.GePluginMessage(1000101, 0)  
                        

                        Receiving the message:

                          
                        def PluginMessage(id, data) :  
                          if id==1000101:  
                              worldContainer = c4d.GetWorldContainerInstance()  
                              print("message received: "+worldContainer.GetString(1000100))  
                          
                              return True  
                          
                          return False  
                        

                        best wishes,
                        Sebastian


                        [URL-REMOVED] @maxon: This section contained a non-resolving link which has been removed.

                        1 Reply Last reply Reply Quote 0
                        • H
                          Helper
                          last edited by

                          On 16/10/2014 at 08:08, xxxxxxxx wrote:

                          Great, I'll give it a test.
                          I also see that in R16 you have now the Cast function.
                          Perhaps we can use it to cast PyCObject - CType.

                          1 Reply Last reply Reply Quote 0
                          • H
                            Helper
                            last edited by

                            On 16/10/2014 at 09:18, xxxxxxxx wrote:

                            *Please don't tell people to put things in the World Container!!!!!!!!
                            That will store data in there permanently!!
                            I do not want plugin makers adding things to my WC without my permission!!

                            This is a bad, bad, bad, practice.
                            The WC should only be used to add preference options that will get deleted when the plugin is removed.
                            NEVER STORE DATA IN THE WORLD CONTAINER!!!...EVER!!

                            Is that forceful enough? Should I use bigger letters? Tongue
                            Please guys. Don't do it...just don't.
                            Think about what would happen if everyone dumps stuff into your WC.
                            Your WC will become so full of crap that you don't even know is in there.

                            Does anyone know if there's a way I can lock my WC. To prevent people from adding stuff to it that I don't want?

                            -ScottA

                            1 Reply Last reply Reply Quote 0
                            • H
                              Helper
                              last edited by

                              On 16/10/2014 at 12:10, xxxxxxxx wrote:

                              It's just preferences that are stored in the WC. You do want preferencs of your installed plugins to be saved, don't you? It's true that the stored data won't be deleted if the plugin is and there could be a better system, but it's not like you store tremendous amount of data in it.

                              1 Reply Last reply Reply Quote 0
                              • H
                                Helper
                                last edited by

                                On 16/10/2014 at 13:49, xxxxxxxx wrote:

                                It's fine to store preferences data in there. As long as it shows up in the preferences pallet.
                                That way we can at least visually see that someone added something to it. But it should stop there and go no further.
                                Anyone that puts preference data in there MUST have a remove mechanism in place to remove that data when the plugin is removed.
                                And you just know that people will either forget this...or do it wrong. And the data will sit there forever.

                                If people start using this as a quick and dirty way to store data by creating custom containers with data in there. Then this quickly turns into a garbage dump on my computer.
                                Eventually people will use the same container ID's as another person. And then there will be bugs and crashing. And people won't know why.

                                This is just a really, really, bad idea all around. And it should not be used or promoted.
                                Dumping hidden permanent data on someone's computer is never ever acceptable! EVER!!!

                                -ScottA

                                1 Reply Last reply Reply Quote 0
                                • H
                                  Helper
                                  last edited by

                                  On 17/10/2014 at 07:12, xxxxxxxx wrote:

                                  Hello,

                                  Indeed, the above idea has potential risks and should only be applied when absolutely necessary. An unique ID must be used to avoid a collision.

                                  The suggested solution is a workaround because we haven't thought of anything better. While it is a workaround, it is a solution for a feature that is currently missing. There is no such rule, that you are not allowed to store into the world container. Anybody with a viable reason, may do so, as long as a valid registered ID is used to store the data.

                                  To make sure that the WorldContainer is "clean" after your operation you may use RemoveData()[URL-REMOVED].

                                  best wishes,
                                  Sebastian


                                  [URL-REMOVED] @maxon: This section contained a non-resolving link which has been removed.

                                  1 Reply Last reply Reply Quote 0
                                  • H
                                    Helper
                                    last edited by

                                    On 17/10/2014 at 07:58, xxxxxxxx wrote:

                                    Thank you for backing me up on this Sebastian.

                                    -ScottA

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