PluginMessage and pycobject [SOLVED]
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) :
In plugin nr 1 (receiving) :
def PluginMessage(id, data) :
print "Received in NR1 - id, data: ", id, data
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)?
On 14/10/2014 at 08:49, xxxxxxxx wrote:
Another way may be to use SpecialEventAdd. Both ways use rough memory in form of a PyCObject. You find some examples on that in this threads:
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.
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.
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())
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())
On 14/10/2014 at 14:18, xxxxxxxx wrote:
Thank you Pim.
That helps me out a lot.
On 15/10/2014 at 00:59, xxxxxxxx wrote:
Ok, I'm glad to do something in return.
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?
On 15/10/2014 at 08:02, xxxxxxxx wrote:
Sorry, I thought this was solved. I will remove the "Solved".
On 16/10/2014 at 07:31, xxxxxxxx wrote:
there may be a way to avoid any handling of a "PyCObject" object. You can set a value in Cinema's WorldContainer 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
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.
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?
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?
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.
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!!!
On 17/10/2014 at 07:12, xxxxxxxx wrote:
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().
On 17/10/2014 at 07:58, xxxxxxxx wrote:
Thank you for backing me up on this Sebastian.