Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware 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
    1. Maxon Developers Forum
    2. beatgram
    • Profile
    • Following 0
    • Followers 0
    • Topics 4
    • Posts 19
    • Best 1
    • Controversial 0
    • Groups 0

    Shinsuke Matsumoto

    @beatgram

    Beat Driven Creative.
    Freelance Animator Based in Tokyo.
    Friend of After Effects & Toon Boom Harmomy.

    I sometimes write crappy code.
    Newbie to C4D.

    1
    Reputation
    21
    Profile views
    19
    Posts
    0
    Followers
    0
    Following
    Joined Last Online
    Website beatgram.net Location Tokyo Age 49

    beatgram Unfollow Follow

    Best posts made by beatgram

    • RE: Python Tag Plugin Doesn't Work Properly With Resource Files

      I double checked my hierarchy / structure and finally I found my mistake!
      The structure below works well and I think it's correct one.

      Test Plugin/
          test_plugin.pyp
          res/
              c4d_symbols.h
              description/
                  ttestplugin.h
                  ttestplugin.res
              strings_en-US/
                  description  -------------------- This folder needed! --------------------
                      ttestplugin.str
      
      posted in Cinema 4D SDK
      beatgramB
      beatgram

    Latest posts made by beatgram

    • RE: Attribute Manamger Update Delay When Description Added Dynamically

      @m_adam Oh, that's a little update for many users but huge for tiny developers like me!
      Thank you so much for the heads up. 😳

      posted in Cinema 4D SDK
      beatgramB
      beatgram
    • RE: Undo Problem For TagData Plugin

      @m_magalhaes Thank you for your checking, Manuel.

      I didn't think it's a bug! And yep, your workaround with bracket notation works fine.
      So these bracket notation style is safer than using BaseContainer in any case?
      Anyway, let us know if you confirm this is a bug.

      posted in Cinema 4D SDK
      beatgramB
      beatgram
    • Undo Problem For TagData Plugin

      Hey folks,

      I encountered Undo related issue again. It might be due to my lack of understanding for undo things but let me ask you a question.

      I wrote a test tag plugin with python which follows another object's position with a little offset.
      It simply works but when I undo, the object that has the tag doesn't react until I click somewhere (see below).

      Is it possible to solve this issue with python?
      I know undo things are prohibited in execute() of tagdata, so is there any workaround?

      Please help me if you know the solution.

      undo_issue_1.gif

      Here's quick code for this.

      testtag.pyp

      import os
      import c4d
      from c4d import plugins, bitmaps, utils
      
      
      PLUGIN_ID = *******
      
      
      class TestTagData(plugins.TagData):
          def Init(self, node):
              pd = c4d.PriorityData()
              if pd is None:
                  raise MemoryError("Failed to create a priority data.")
              pd.SetPriorityValue(lValueID = c4d.PRIORITYVALUE_MODE, data = c4d.CYCLE_INITIAL)
              node[c4d.EXPRESSION_PRIORITY] = pd
              return True
      
          def Execute(self, tag, doc, op, bt, priority, flags):
              bc = tag.GetDataInstance()
              obj = bc.GetLink(c4d.TTEST_TARGET_OBJECT)
              if obj is None:
                  return False
              mrx = obj.GetMg()
              mrx.off += c4d.Vector(100, 0, 0)
              op.SetMg(mrx)
              return c4d.EXECUTIONRESULT_OK
      
      
      if __name__ == "__main__":
          plugins.RegisterTagPlugin(id = PLUGIN_ID, str = "Test Tag", info = c4d.TAG_EXPRESSION | c4d.TAG_VISIBLE, g = TestTagData, description = "ttest", icon = None)
      

      ttest.res

      CONTAINER Ttest
      {
          NAME Ttest;
          INCLUDE Texpression;
      
          GROUP ID_TAGPROPERTIES
          {
              LINK TTEST_TARGET_OBJECT
              {
                  ANIM ON;
                  ACCEPT { Obase; };
              }
      }
      
      posted in Cinema 4D SDK python r23
      beatgramB
      beatgram
    • RE: How To Add Undo For Description Added Dynamically

      @Cairyn Thank you so much for helping me again! 😊

      posted in Cinema 4D SDK
      beatgramB
      beatgram
    • RE: How To Add Undo For Description Added Dynamically

      @m_magalhaes Thank you for your explanation! It's really helpful to know several solutions for a problem.
      C4D python doc is not user friendly for newbies like me but this community is super friendly and pretty awesome! 🤛

      posted in Cinema 4D SDK
      beatgramB
      beatgram
    • RE: How To Add Undo For Description Added Dynamically

      So I tried some changes based on a hint @Cairyn points out, my code seems to work now.
      But I'm still not sure this is a proper way to add undo for dynamic descriptions, so please let me know if you know the correct / better way!

      test_tag.pyp

      import os
      import c4d
      from c4d import plugins
      
      
      PLUGIN_ID = *******
      LINK_NO = 1100
      FLOAT_NO = 1200
      
      
      class TestTagData(c4d.plugins.TagData):
          def Init(self, node):
              self.InitAttr(node, int, c4d.TTESTTAG_CONTROLLERS_NUM)  #-------------------- Changed --------------------
              bc = node.GetDataInstance()  #-------------------- Changed --------------------
              bc.SetInt32(c4d.TTESTTAG_CONTROLLERS_NUM, 0)  #-------------------- Changed --------------------
              pd = c4d.PriorityData()
              if pd is None:
                  raise MemoryError("Failed to create a priority data.")
              pd.SetPriorityValue(lValueID = c4d.PRIORITYVALUE_MODE, data = c4d.CYCLE_EXPRESSION)
              node[c4d.EXPRESSION_PRIORITY] = pd
              return True
      
          def Execute(self, tag, doc, op, bt, priority, flags):
              return c4d.EXECUTIONRESULT_OK
      
          def Message(self, node, type, data):
              if type == c4d.MSG_DESCRIPTION_COMMAND:
                  if data["id"][0].id == c4d.TTESTTAG_BUTTON_ADD:
                      doc = c4d.documents.GetActiveDocument()
                      doc.StartUndo()
                      doc.AddUndo(c4d.UNDOTYPE_CHANGE_SMALL, node)
                      bc = node.GetDataInstance()  #-------------------- Changed --------------------
                      bc.SetInt32(c4d.TTESTTAG_CONTROLLERS_NUM, bc.GetInt32(c4d.TTESTTAG_CONTROLLERS_NUM) + 1)  #-------------------- Changed --------------------
                      doc.EndUndo()
                  elif data["id"][0].id == c4d.TTESTTAG_BUTTON_REMOVE:
                      bc = node.GetDataInstance()  #-------------------- Changed --------------------
                      controllers_num = bc.GetInt32(c4d.TTESTTAG_CONTROLLERS_NUM)  #-------------------- Changed --------------------
                      if controllers_num > 0:  #-------------------- Changed --------------------
                          doc = c4d.documents.GetActiveDocument()
                          doc.StartUndo()
                          doc.AddUndo(c4d.UNDOTYPE_CHANGE_SMALL, node)
                          bc.SetInt32(c4d.TTESTTAG_CONTROLLERS_NUM, controllers_num - 1)  #-------------------- Changed --------------------
                          doc.EndUndo()
              return True
      
          def GetDDescription(self, node, description, flags):
              if not description.LoadDescription(node.GetType()):
                  return False
              singleId = description.GetSingleDescID()
              groupId = c4d.DescID(c4d.DescLevel(c4d.ID_TAGPROPERTIES))
              bc = node.GetDataInstance()  #-------------------- Changed --------------------
              controllers_num = bc.GetInt32(c4d.TTESTTAG_CONTROLLERS_NUM)  #-------------------- Changed --------------------
              if controllers_num > 0:
                  for i in range(controllers_num):
                      linkId = c4d.DescID(c4d.DescLevel(LINK_NO + (i + 1)))
                      if singleId is None or linkId.IsPartOf(singleId)[0]:
                          link_bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_BASELISTLINK)
                          link_bc.SetString(c4d.DESC_NAME, "Controller " + str(i + 1))
                          link_bc.SetString(c4d.DESC_SHORT_NAME, "Controller " + str(i + 1))
                          link_bc.SetInt32(c4d.DESC_ANIMATE, c4d.DESC_ANIMATE_ON)
                          if not description.SetParameter(linkId, link_bc, groupId):
                              return False
                      floatId = c4d.DescID(c4d.DescLevel(FLOAT_NO + (i + 1)))
                      if singleId is None or floatId.IsPartOf(singleId)[0]:
                          float_bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_REAL)
                          float_bc.SetString(c4d.DESC_NAME, "Rate")
                          float_bc.SetString(c4d.DESC_SHORT_NAME, "Rate")
                          float_bc.SetFloat(c4d.DESC_DEFAULT, 1)
                          float_bc.SetInt32(c4d.DESC_ANIMATE, c4d.DESC_ANIMATE_ON)
                          float_bc.SetInt32(c4d.DESC_UNIT, c4d.DESC_UNIT_PERCENT)
                          float_bc.SetFloat(c4d.DESC_MIN, -1000)
                          float_bc.SetFloat(c4d.DESC_MAX, 1000)
                          float_bc.SetFloat(c4d.DESC_MINSLIDER, -1)
                          float_bc.SetFloat(c4d.DESC_MAXSLIDER, 1)
                          float_bc.SetFloat(c4d.DESC_STEP, 0.01)
                          float_bc.SetInt32(c4d.DESC_CUSTOMGUI, c4d.CUSTOMGUI_REALSLIDER)
                          if not description.SetParameter(floatId, float_bc, groupId):
                              return False
                      separatorId = c4d.DescID(0)
                      if singleId is None or separatorId.IsPartOf(singleId)[0]:
                          separator_bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_SEPARATOR)
                          separator_bc.SetInt32(c4d.DESC_CUSTOMGUI, c4d.CUSTOMGUI_SEPARATOR)
                          separator_bc.SetBool(c4d.DESC_SEPARATORLINE, True)
                          if not description.SetParameter(separatorId, separator_bc, groupId):
                              return False
              return (True, flags | c4d.DESCFLAGS_DESC_LOADED)
      
      
      if __name__ == "__main__":
          c4d.plugins.RegisterTagPlugin(id = PLUGIN_ID, str = "Test Tag", info = c4d.TAG_EXPRESSION|c4d.TAG_VISIBLE, g = TestTagData, description = "ttesttag", icon = None)
      

      ttesttag.res

      CONTAINER Ttesttag
      {
          NAME Ttesttag;
          INCLUDE Texpression;
      
          GROUP ID_TAGPROPERTIES
          {
              GROUP TTESTTAG_BUTTONS_GROUP
              {
                  COLUMNS 2;
                  BUTTON TTESTTAG_BUTTON_ADD { SCALE_H; }
                  BUTTON TTESTTAG_BUTTON_REMOVE { SCALE_H; }
                  LONG TTESTTAG_CONTROLLERS_NUM { HIDDEN; ANIM OFF; MIN 0; MAX 10000; }  //-------------------- Added --------------------
              }
          }
      }
      

      ttesttag.h

      #ifndef _TTESTTAG_H_
      #define _TTESTTAG_H_
      
      enum
      {
          TTESTTAG_BUTTONS_GROUP = 1001,
          TTESTTAG_BUTTON_ADD = 1002,
          TTESTTAG_BUTTON_REMOVE = 1003,
          TTESTTAG_CONTROLLERS_NUM = 1004,  //-------------------- Added --------------------
      }
      
      #endif
      

      ttesttag.str

      STRINGTABLE Ttesttag
      {
          Ttesttag "Test Tag";
          TTESTTAG_BUTTONS_GROUP "";
          TTESTTAG_BUTTON_ADD "Add";
          TTESTTAG_BUTTON_REMOVE "Remove";
          TTESTTAG_CONTROLLERS_NUM "";  //-------------------- Added --------------------
      }
      
      posted in Cinema 4D SDK
      beatgramB
      beatgram
    • RE: Attribute Manamger Update Delay When Description Added Dynamically

      @m_adam Thank you for confirming.
      I already know symbolcache thingy, so it's no problem.

      posted in Cinema 4D SDK
      beatgramB
      beatgram
    • RE: How To Add Undo For Description Added Dynamically

      @Cairyn Thank you for your time!

      Hmm, I see. I'm not sure the actual solution for the point you mention but your explanation is so helpful. Anyway, I'll try some workaround on my end.

      posted in Cinema 4D SDK
      beatgramB
      beatgram
    • How To Add Undo For Description Added Dynamically

      Hi folks,

      I'm still trying to make a python tag plugin which has 2 buttons in tag property. It dynamically adds new descriptions to tag property when "add" button is clicked, and it dynamically removes those descriptions when "remove" button is clicked. So far my code works but I noticed undo doesn't work for these descriptions (see below).

      Is there a way to add undo for descriptions created dynamically? Please tell me how to add undo for that if it's possible for python plugin.
      I tried to insert basic undo things (StartUndo(), AddUndo(), and EndUndo()) into Message() function but it doesn't seem to work.
      My goal is it works like "IK-Spline" does. (see below)

      "Current Status"
      undo_issue.gif

      "My Goal"
      like_ik_spline.gif

      Here's my code.

      test_tag.pyp

      import os
      import c4d
      from c4d import plugins
      
      
      PLUGIN_ID = *******
      LINK_NO = 1100
      FLOAT_NO = 1200
      
      
      class TestTagData(c4d.plugins.TagData):
          def Init(self, node):
              self.controllers_num = 0
              pd = c4d.PriorityData()
              if pd is None:
                  raise MemoryError("Failed to create a priority data.")
              pd.SetPriorityValue(lValueID = c4d.PRIORITYVALUE_MODE, data = c4d.CYCLE_EXPRESSION)
              node[c4d.EXPRESSION_PRIORITY] = pd
              return True
      
          def Execute(self, tag, doc, op, bt, priority, flags):
              return c4d.EXECUTIONRESULT_OK
      
          def Message(self, node, type, data):
              if type == c4d.MSG_DESCRIPTION_COMMAND:
                  if data["id"][0].id == c4d.TTESTTAG_BUTTON_ADD:
                      doc = c4d.documents.GetActiveDocument()
                      doc.StartUndo()                    #---------- This doesn't seem to work! ----------
                      doc.AddUndo(c4d.UNDOTYPE_CHANGE_NOCHILDREN, node)
                      self.controllers_num += 1
                      doc.EndUndo()                    #----------------------------------------
                      node.SetDirty(c4d.DIRTYFLAGS_DESCRIPTION)
                  elif data["id"][0].id == c4d.TTESTTAG_BUTTON_REMOVE:
                      if self.controllers_num > 0:
                          doc = c4d.documents.GetActiveDocument()
                          doc.StartUndo()                    #---------- This doesn't seem to work! ----------
                          doc.AddUndo(c4d.UNDOTYPE_CHANGE_NOCHILDREN, node)
                          self.controllers_num -= 1
                          doc.EndUndo()                    #----------------------------------------
                          node.SetDirty(c4d.DIRTYFLAGS_DESCRIPTION)
              return True
      
          def GetDDescription(self, node, description, flags):
              if not description.LoadDescription(node.GetType()):
                  return False
              singleId = description.GetSingleDescID()
              groupId = c4d.DescID(c4d.DescLevel(c4d.ID_TAGPROPERTIES))
              controllers_num = self.controllers_num
              if controllers_num > 0:
                  for i in range(controllers_num):
                      linkId = c4d.DescID(c4d.DescLevel(LINK_NO + (i + 1)))
                      if singleId is None or linkId.IsPartOf(singleId)[0]:
                          link_bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_BASELISTLINK)
                          link_bc.SetString(c4d.DESC_NAME, "Controller " + str(i + 1))
                          link_bc.SetString(c4d.DESC_SHORT_NAME, "Controller " + str(i + 1))
                          link_bc.SetInt32(c4d.DESC_ANIMATE, c4d.DESC_ANIMATE_ON)
                          if not description.SetParameter(linkId, link_bc, groupId):
                              return False
                      floatId = c4d.DescID(c4d.DescLevel(FLOAT_NO + (i + 1)))
                      if singleId is None or floatId.IsPartOf(singleId)[0]:
                          float_bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_REAL)
                          float_bc.SetString(c4d.DESC_NAME, "Rate")
                          float_bc.SetString(c4d.DESC_SHORT_NAME, "Rate")
                          float_bc.SetFloat(c4d.DESC_DEFAULT, 1)
                          float_bc.SetInt32(c4d.DESC_ANIMATE, c4d.DESC_ANIMATE_ON)
                          float_bc.SetInt32(c4d.DESC_UNIT, c4d.DESC_UNIT_PERCENT)
                          float_bc.SetFloat(c4d.DESC_MIN, -1000)
                          float_bc.SetFloat(c4d.DESC_MAX, 1000)
                          float_bc.SetFloat(c4d.DESC_MINSLIDER, -1)
                          float_bc.SetFloat(c4d.DESC_MAXSLIDER, 1)
                          float_bc.SetFloat(c4d.DESC_STEP, 0.01)
                          float_bc.SetInt32(c4d.DESC_CUSTOMGUI, c4d.CUSTOMGUI_REALSLIDER)
                          if not description.SetParameter(floatId, float_bc, groupId):
                              return False
                      separatorId = c4d.DescID(0)
                      if singleId is None or separatorId.IsPartOf(singleId)[0]:
                          separator_bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_SEPARATOR)
                          separator_bc.SetInt32(c4d.DESC_CUSTOMGUI, c4d.CUSTOMGUI_SEPARATOR)
                          separator_bc.SetBool(c4d.DESC_SEPARATORLINE, True)
                          if not description.SetParameter(separatorId, separator_bc, groupId):
                              return False
              return (True, flags | c4d.DESCFLAGS_DESC_LOADED)
      
      
      if __name__ == "__main__":
          c4d.plugins.RegisterTagPlugin(id = PLUGIN_ID, str = "Test Tag", info = c4d.TAG_EXPRESSION|c4d.TAG_VISIBLE, g = TestTagData, description = "ttesttag", icon = None)
      

      ttesttag.res

      CONTAINER Ttesttag
      {
          NAME Ttesttag;
          INCLUDE Texpression;
      
          GROUP ID_TAGPROPERTIES
          {
              GROUP TTESTTAG_BUTTONS_GROUP
              {
                  COLUMNS 2;
                  BUTTON TTESTTAG_BUTTON_ADD { SCALE_H; }
                  BUTTON TTESTTAG_BUTTON_REMOVE { SCALE_H; }
              }
          }
      }
      

      ttesttag.h

      #ifndef _TTESTTAG_H_
      #define _TTESTTAG_H_
      
      enum
      {
          TTESTTAG_BUTTONS_GROUP = 1001,
          TTESTTAG_BUTTON_ADD = 1002,
          TTESTTAG_BUTTON_REMOVE = 1003,
      }
      
      #endif
      

      ttesttag.str

      STRINGTABLE Ttesttag
      {
          Ttesttag "Test Tag";
          TTESTTAG_BUTTONS_GROUP "";
          TTESTTAG_BUTTON_ADD "Add";
          TTESTTAG_BUTTON_REMOVE "Remove";
      }
      

      ---------- User Information ----------

      Cinema 4D version: R23
      OS: Windows 10
      Language: Python

      posted in Cinema 4D SDK python r23
      beatgramB
      beatgram
    • RE: Attribute Manamger Update Delay When Description Added Dynamically

      @Cairyn Interesting investigation but it's a bit difficult for newbie like me. 🙄
      Anyway, I appreciate your help!

      posted in Cinema 4D SDK
      beatgramB
      beatgram