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
    • Recent
    • Tags
    • Users
    • Login

    Lists as members are not unique?

    Scheduled Pinned Locked Moved PYTHON Development
    7 Posts 0 Posters 598 Views
    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 Offline
      Helper
      last edited by

      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.Confused

      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

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

        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.

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

          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

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

            On 21/03/2013 at 21:02, xxxxxxxx wrote:

            Argh!
            Nope.. Still doesn't work. 😠

            -ScottA

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

              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[:]

              related thread

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

                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

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

                  On 22/03/2013 at 08:30, xxxxxxxx wrote:

                  Thanks Guys.
                  That's got it working. 👍

                  -ScottA

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