How is the structure manager working?



  • 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://plugincafe.maxon.net/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



  • 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



  • 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).



  • 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



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



  • 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.'



  • 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


Log in to reply