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

    How is the structure manager working?

    Scheduled Pinned Locked Moved PYTHON Development
    7 Posts 0 Posters 659 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 23/03/2013 at 16:16, xxxxxxxx wrote:

      Hello everybody,

      i think its a bit difficulte to explain what I want, but Ill try to do my best. I wrote a tagdata to be able to work with large data tables. The result of the the tagdata is the variable list_.

      import os  
      import sys  
      import c4d  
        
      from c4d import plugins, utils, bitmaps, gui  
      import datetime  
        
      # be sure to use a unique ID obtained from www.plugincafe.com  
      PLUGIN_ID = 10000045 #TEST ID  
        
      def ValInType(o_val) :  
        val = o_val  
        if "," in o_val: o_val = o_val.replace(",", ".")  
        try: return float(o_val)  
        except ValueError: return str(val)  
        
      class PlgTAG(plugins.TagData) :  
        
        list_ = []  
        
        def Init(self, node) :  
            return True  
        
        def Message(self, node, type, data) :  
            if type==c4d.MSG_DESCRIPTION_COMMAND:  
                if data['id'][0].id==10002:   
                    self.LoadList(node)  
                    self.GetVal(node)  
                    self.FillPorts(node)  
            return True  
        
        def LoadList(self, op) :  
            try: txt = open(op[10001], 'r')  
            except IOError:   
                gui.MessageDialog("File is not readable.")  
                return  
        
            lns = txt.readlines()  
            txt.close()  
            self.list_ = []  
            self.lncnt = len(lns)  
            ccnt = 0  
        
            for ln in lns:  
                cols = ln.split(";")  
                if ccnt < len(cols) : ccnt =  len(cols) #Just to get the largest columne count  
        
            for ii in range(len(lns)) :  
                new_ln = []  
                cols = lns[ii][0:len(lns[ii])-1].split(";")  
                for i in range(ccnt) :  
                    if i < len(cols) : new_ln.append(ValInType(cols[i]))  
                    else: new_ln.append("None")  
                self.list_.append(new_ln)  
        
            self.colcnt = ccnt  
        
            return  
        
        
      if __name__ == "__main__":  
         
        dir, file = os.path.split(__file__)  
        bmp = bitmaps.BaseBitmap()  
        bmp.InitWith(os.path.join(dir, "res", "icon.tif"))  
        
        plugins.RegisterTagPlugin(id=PLUGIN_ID,  
                                 str="PlgTAG",  
                                 g=PlgTAG,  
                                 description="PlgTAG",  
                                 icon=bmp,  
                                 info=c4d.TAG_MULTIPLE|c4d.TAG_EXPRESSION|c4d.TAG_VISIBLE)
      

      Now Id like to visible list_ in a commanddata.  I can get list_ from somewhere else with:

      class GetValues(object) :  
        def __init__(self, list_=None, lncnt = 0, colcnt = 0) :  
            super(GetValues, self).__init__()  
            self.list_ = list_  
            self.lncnt = lncnt  
            self.colcnt = colcnt  
        
      # tag is the tagdata  
      list_obj = GetValues()  
      tag.Message(10000045, list_obj)  
      list_ = list_obj.list_  
      lncnt = list_obj.lncnt  
      colcnt = list_obj.colcnt
      

      (https://developers.maxon.net/forum/topic/7036/7946_how-to-get-a-variable-from-plugintag  -> Thanks to littledevil and niklas)

      The commanddata:

      import c4d  
      from c4d import bitmaps, gui, plugins, utils  
      import collections, os  
        
      PLUGIN_ID = 10000055 #TestID only!!!!!!!!!!!!  
        
      class GetValues(object) :  
        def __init__(self, list_=None, lncnt = 0, colcnt = 0) :  
            super(GetValues, self).__init__()  
            self.list_ = list_  
            self.lncnt = lncnt  
            self.colcnt = colcnt  
        
      class MyDialog(gui.GeDialog) :  
        
        tag = None  
        list_ = []  
        lncnt = 10  
        colcnt = 10  
        
        def CreateLayout(self) :  
            ln = 0  
            col = 0  
            element = 0  
            cntr = 0  
            hole = (self.lncnt+1)*(self.colcnt+1)  
        
            self.ScrollGroupBegin(  
                id=0,   
                flags=c4d.BFH_SCALEFIT|c4d.BFV_SCALEFIT,  
                scrollflags=c4d.SCROLLGROUP_VERT | c4d.SCROLLGROUP_HORIZ,  
                initw=300,   
                inith=50  
                )  
            self.GroupBegin(id=1, flags=c4d.BFV_TOP|c4d.BFH_LEFT, title="", cols=self.colcnt+1)  
        
            for i in range(self.lncnt+1) :  
              for ii in range(self.colcnt+1) :  
                cntr += 1  
                if i == 0:   
                  if ii == 0: self.AddStaticText(id=i+2, flags=c4d.BFH_LEFT, name="", borderstyle=c4d.BORDER_NONE)  
                  else:   
                    self.AddStaticText(id=i+2, flags=c4d.BFH_LEFT, initw=100, name=str(col), borderstyle=c4d.BORDER_THIN_OUT)  
                    col += 1  
                else:  
                  if ii == 0:   
                    self.AddStaticText(id=i+2, flags=c4d.BFH_LEFT, initw=100, name=str(ln), borderstyle=c4d.BORDER_THIN_OUT)  
                    ln += 1  
                  else:  
                    a = int(float(element)/self.colcnt)  
                    b = element - (a*self.colcnt)  
                    self.AddStaticText(id=i+2, flags=c4d.BFH_LEFT, initw=100, name=str(self.list_[a][b]), borderstyle=c4d.BORDER_THIN_IN)  
                    element += 1  
        
            self.GroupEnd()  
            self.GroupEnd()  
        
            return True  
        
        def Command(self, id, msg) :  
            return True  
        
        def CoreMessage(self,id,msg) :  
            if id == c4d.EVMSG_CHANGE or id == 1970300003:  
              activeTag = c4d.documents.GetActiveDocument().GetActiveTag()  
              if self.tag != activeTag:  
        
                if activeTag == None:  
                  self.list_ = []  
                  self.lncnt = 0  
                  self.colcnt = 0  
        
                elif activeTag.GetType() != 10000045:  
                  self.list_ = []  
                  self.lncnt = 0  
                  self.colcnt = 0  
        
                else:  
                  self.tag = activeTag  
                  list_obj = GetValues()  
                  self.tag.Message(10000045, list_obj)  
                  self.list_ = list_obj.list_  
                  self.lncnt = list_obj.lncnt  
                  self.colcnt = list_obj.colcnt  
        
                self.tag = activeTag  
                self.LayoutFlushGroup(0)  
                self.CreateLayout()  
                self.InitValues()  
                self.LayoutChanged(0)  
        
            return True  
        
      class Test(plugins.CommandData) :  
        dialog = None  
        def Execute(self, doc) :  
            if self.dialog is None: self.dialog = MyDialog()  
            return self.dialog.Open(dlgtype=c4d.DLG_TYPE_ASYNC, pluginid=PLUGIN_ID, defaultw=1, defaulth=1)  
        def RestoreLayout(self, sec_ref) :  
            if self.dialog is None: self.dialog = MyDialog()  
            return self.dialog.Restore(pluginid=PLUGIN_ID, secret=sec_ref)  
        
      if __name__ == "__main__":  
         bmp = bitmaps.BaseBitmap()  
         dir, f = os.path.split(__file__)  
         fn = os.path.join(dir, "res", "icon.tif")  
         bmp.InitWith(fn)  
         plugins.RegisterCommandPlugin(id=PLUGIN_ID,   
                                      str="Test",  
                                      info=0,  
                                      help="Test",   
                                      dat=Test(),  
                                      icon=bmp)
      

      If the active tag of the document is a TableTag, the commanddata shows the table. If not, the commendata is empty. If the active tag is changed from one TableTag to an other the commanddata is refreshing its one and the table of the new active TableTag is shown. So far, so good.
      It works well, but this way simply takes to long for refreshing. I tested with 12.000 element (1000 line, 12 columnes, 1.8sec.) and 120.000 elements (10000 lines, 12columnes, more than 4min.).

      So my question is: How is the structure manager working? I think it is a commanddata, too. And it gets the information from the pointtag of the active object.

      I hope I explained comprehensibly.

      Thanks for help
      Greetings
      rown

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

        On 23/03/2013 at 16:50, xxxxxxxx wrote:

        Hi rown,

        just some points/thoughts:

        1. I don't see where you fill in the GetValues object you are passing to the tag's Message() method. Did I miss it?
        2. I recommend you to use symbols instead of integral numbers. Eg. PLUGIN_ID in place where 10000045 is used.
        3. If I understood you correctly: It is the dialog that changes, not the CommandData. Just to clear the missunderstandings here.
        4. You can not expect the same performance as the structure dialog. It is using an optimized GUI widget and is written in C++.

        Best regards,
        -Niklas

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

          On 23/03/2013 at 17:08, xxxxxxxx wrote:

          your code isn't very well commented, so it is kind of hard to tell what does make 
          your code so slow. i guess there are multiple reasons, but a pretty obvious reason 
          is that you are adding each data element as a seperate dialog element, so that your 
          gedialog has to display and manage 120 000 (statictext) members.

          c4d managers are generally speaking pretty sealed and we do not have much access 
          to them. when you have to display more complex data structures the common approach 
          would be to create a  ressource element which can display and manage this data on a 
          lower level. as we do neither have access to CustomDataType/CustumGuiData in python 
          nor we can use most of the existing CustomGui classes in python your best shot would 
          be a UserAera. either by drawing each element into the Userarea or by creating a
          large string and then just draw this string.

          i think the structure manager uses a table to display its data and i think we cannot use
          the table ressource element in python (not for sure, have never tested it).

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

            On 23/03/2013 at 17:16, xxxxxxxx wrote:

            I agree with littledevil, a UserArea might be the best (but not most comfortable) choice. Just
            make sure you're just drawing what needs to be displayed and not all 120k rows.. o_O

            Best,
            Niklas

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

              On 23/03/2013 at 17:18, xxxxxxxx wrote:

              Hey Niklas,

              I don't see where you fill in the GetValues object you are passing to the tag's Message() method. Did I or did you miss it?

              Im sorry, but I do not really understand your question. GetValues() is called from Coremessage.

              I recommend you to use symbols instead of integral numbers. Eg. PLUGIN_ID in place where 10000045 is used.

              Yes, you are right. But until now it is a quick and dirty code. I waned it working befor well-looking.

              If I understood you correctly: It is the dialog that changes, not the CommandData. Just to clear the missunderstandings here.

              Yes, of course, the dialog changes.

              You can not expect the same performance as the structure dialog. It is using an optimized GUI widget and is written in C++.

              Of course. But Im sure there is a faster solution. For example: If I create the dialoge in the tagdata and the commanddata is getting it from there, the dialog just has to be created one time. But I dont know how to let the commanddata know that something has changed.

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

                On 23/03/2013 at 17:34, xxxxxxxx wrote:

                just another note, while this would actually slow your code down, rather than speeding
                it up, the whole problem REALLY needs more modularization on the data level, instead 
                of pushing arround a bunch of dicts, list and tupples. it would make it much easier to 
                understand the code and add things like caching and displaying data in an efficient way.

                about getvalues. i think the main missconeption here is that the name sounds and looks like 
                a static method, while it is actually a class. you even fall yourself for this trap as you are saying 
                'GetValues() is called from Coremessage.'

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

                  On 23/03/2013 at 18:22, xxxxxxxx wrote:

                  about getvalues. i think the main missconeption here is that the name sounds and looks like 
                  a static method, while it is actually a class. you even fall yourself for this trap as you are saying 
                  'GetValues() is called from Coremessage.'

                  You are right. I know that I rarly use the right terms. But I already have that problem in my mother tongue. Ill start working on it.

                  Im still not really sure to know what higher, lower or data level means. I should keep my eyes open for that. And I will take a look at UserArea.

                  Thanks you both
                  rown

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