Lists as members are not unique?
-
On 21/03/2013 at 16:51, xxxxxxxx wrote:
During the process of porting a C++ plugin to Python I stumbled upon something REALLY wacky.
In C++ we can write a plugin that consists of a Gedialog plugin and a TagData plugin.
Then we can get the Tag plugin's class member variables from within the GeDialog code and use it.Upon doing this same thing in Python. It seemed to mostly work the same way as C++...Except in the case of lists (arrays) for some strange reason.
If I add a list as a class member of the tag. Then change that list via the GeDialog plugin. When I make a new tag on another object. That list's values get carried over to that new tag!!?
In other words. The class member list in my tags are not unique ( per tag ) as they are supposed to be.Other types of variables don't do this... Only lists.
Every time a new tag is created. Other types of class members are unique as expected. But not when it comes to lists.Here's an example of a GeDialog plugin that sets and gets the class members on a tag plugin.
import c4d,sys,os from c4d import plugins, utils, bitmaps, gui, documents TAG_PLUGIN_ID = 1000008 # THIS id IS FOR TESTING PUPOSES ONLY!!! DIALOG_PLUGIN_ID =1000009 # THIS id IS FOR TESTING PUPOSES ONLY!!! ################################################################ ###################### Tag Plugin ############################## class StorageTag(plugins.TagData) : tagListData = ["Nothing"] #A list that will be changed by the dialog plugin tagVariableValue = "Nothing" #A variable that will be changed by the dialog plugin def Init(self, tag) : data = tag.GetDataInstance() return True def Execute(self, tag, doc, op, bt, priority, flags) : return True if __name__ == "__main__": path, fn = os.path.split(__file__) bmp = bitmaps.BaseBitmap() bmp.InitWith(os.path.join(path, "res/icons/", "myicon.png")) plugins.RegisterTagPlugin(TAG_PLUGIN_ID, "PyStorageTag",c4d.TAG_VISIBLE + c4d.TAG_EXPRESSION,StorageTag,"MyTag_dec", bmp) ################################################################ ################### Dialog Plugin ############################## #enums BUTTON = 1001 BUTTON2= 1002 class MyDialog_Gui(gui.GeDialog) : def CreateLayout(self) : self.SetTitle("Dialog") self.AddButton(BUTTON, c4d.BFH_CENTER, 60, 10, name="Press Me") self.AddButton(BUTTON2, c4d.BFH_CENTER, 80, 10, name="Get Tag value") return True def InitValues(self) : return True def Command(self, id, msg) : doc = documents.GetActiveDocument() activeObj = doc.GetActiveObject() #The obect holding the tag if not activeObj: return False tag = activeObj.GetTag(TAG_PLUGIN_ID) #Get the tag on the object if not tag: return False st = tag.GetNodeData() #Get The tag's data (including the class members) if id == BUTTON: st.tagVariableValue = "variable Value Changed" #Change the value of the tag's member variable st.tagListData[0] = "List Value Changed" #Change the value of the tag's member list if id == BUTTON2: print st.tagVariableValue #Get the current values of the tag's members print st.tagListData[0] c4d.EventAdd() return True class myDialog_Main(plugins.CommandData) : dialog = None def Execute(self, doc) : if self.dialog is None: self.dialog = MyDialog_Gui() return self.dialog.Open(dlgtype=c4d.DLG_TYPE_ASYNC, pluginid=DIALOG_PLUGIN_ID, defaultw=0, defaulth=0, xpos=-1, ypos=-1) def RestoreLayout(self, sec_ref) : if self.dialog is None: self.dialog = MyDialog_Gui() return self.dialog.Restore(pluginid=DIALOG_PLUGIN_ID, secret=sec_ref) if __name__ == "__main__": path, fn = os.path.split(__file__) bmp = bitmaps.BaseBitmap() bmp.InitWith(os.path.join(path, "res/icons/", "None")) plugins.RegisterCommandPlugin(DIALOG_PLUGIN_ID, "My Dialog",0,None,"", myDialog_Main())
This is really wacky. And doesn't seem to happen in C++ plugins.
How do I stop lists from spilling over into other instances of the tag like this?
Is this a bug?-ScottA
-
On 21/03/2013 at 18:58, xxxxxxxx wrote:
1. have you tried to reference to class instance explicitly on member allocation / use a constructor ?
at least for me this is working for a somehwat compareable case.2. GetNodeData() is actually not listed as method for GeListNode in the python docs, so i am not
sure if we are supposed to use it or how far it does work. -
On 21/03/2013 at 19:40, xxxxxxxx wrote:
This seems to fix the problem:
class StorageTag(plugins.TagData) : def __init__(self, tagListData=[]) : super(StorageTag, self).__init__() self.tagListData = tagListData
It's strange that we are forced to use custom constructors to make lists work. But not other types of variables.
-ScottA
-
On 21/03/2013 at 21:02, xxxxxxxx wrote:
Argh!
Nope.. Still doesn't work.-ScottA
-
On 21/03/2013 at 21:43, xxxxxxxx wrote:
do not pass lists as optional parameters, it will cause all instances to share the
same list if you skip the parameter on invoking the method. a possible workarround
is the following :def __init__ (self, somelist = []) :
self.foo = somelist[:] -
On 22/03/2013 at 00:41, xxxxxxxx wrote:
Hi Scott,
list objects are mutable. The list is created once at the place you wrote it. Every instance will have
a reference to the exactly same list when you create it on class-level or as a default-argument (since
those are created only once, too). One has to be very careful with mutable types within class
instances. No value in Python is passed by value, they're always passed by reference. But strings
and integers and others are immutable which creates the illusion of them being passed by
value.Best,
-Niklas -
On 22/03/2013 at 08:30, xxxxxxxx wrote:
Thanks Guys.
That's got it working.-ScottA