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

    SetString() to Update Dialog crash c4d

    Bugs
    python s22
    3
    5
    1.1k
    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.
    • chuanzhenC
      chuanzhen
      last edited by m_adam

      Hi,
      i use a CommandData plugins to creat a button to active my tool plugins,and use tool.Message(123) to pass messages to tool plugins and refresh the gui parameters of tool plugins.
      In practice it is:
      When I click the button, the tool is activated, and some str parameters of the tool are set, and then the changed parameters are displayed. As shown below image1, I will set some strings to these 2 gui elements

      image1:
      test.jpg

      Here's a screen recording showing c4d crashing:bug.gif

      I tried to find the reason, I found that c4d does not crash when I activate the tool by click button, without setting the parameters of the tool!

      two plugins use python!
      Thanks for any help!

      相信我,可以的!

      ferdinandF 1 Reply Last reply Reply Quote 0
      • ferdinandF
        ferdinand @chuanzhen
        last edited by ferdinand

        Hey @chuanzhen,

        Thank you for reaching out to us. While I understand the principal problem you have, I cannot give any concrete advice without the code. Please share a minimal setup of your project. You can use sdk_support(at)maxon(dot)net in case you do not want to make the code public.

        One thing though, you said you invoke tool.Message(123) which sounds dangerous. You should not send random message IDs and random data in that manner, as this can easily trip the C++ backend. You must at least use a unique plugin ID. I personally would recommend sending data with MSG_BASECONTAINER though, assuming this is what you want to do here, as there are much less things which can go wrong this way.

        I am not even sure if sending data over a raw plugin ID is supported, at least in my quick test t_data was None. You should of course also adhere to the threading restrictions, and based on what you do in your ToolData.Message, you should either make sure that you are on the main thread when you do something that should only be done on the main thread, or alternatively make sure to invoke myToolData.Message only from the main thread on the other end. But in a CommandData you should most of the time be on the main thread anyways.

        Find a quick sketch of what I did at the end of the posting. Other than that, I can only recommend providing a code example to us.

        Cheers,
        Ferdinand

        The result:
        3accec83-d315-4858-a3a4-94849f0a7d3e-image.png

        The sending part, here as a script manger scrip:

        import c4d
        
        def main() -> None:
            """
            """
            idSdkToolPlugin: int = 1054512 # The "Tool with GUI" ToolData plugin from the Python SDK
            idMyCommandDataPlugin: int = 1234567890 # A fictitious CommandData plugin to send data from.
        
            # Get the BasePlugin for the tool.
            myTool: c4d.plugins.BasePlugin = c4d.plugins.FindPlugin(idSdkToolPlugin, c4d.PLUGINTYPE_TOOL)
            if myTool is None:
                return
        
            # Setup some data to send.
            data: c4d.BaseContainer = c4d.BaseContainer(idMyCommandDataPlugin)
            data[0] = "Hello World"
            data[1] = 42
        
            # Send the data.
            if not myTool.Message(c4d.MSG_BASECONTAINER, data):
                raise RuntimeError("Sending data failed.")
        
            print ("Sent data to tool.")
        
        if __name__ == "__main__":
            main()
        

        The receiving part in the ToolData plugin:

        def Message(self, doc: c4d.documents.BaseDocument, data:c4d.BaseContainer, mid: int, 
                    t_data: any) -> bool:
            """Called when the tool receives messages.
            """
            idMyCommandDataPlugin: int = 1234567890
        
            # Check for a BaseContainer coming in with the ID of the CommandData plugin.
            if (mid == c4d.MSG_BASECONTAINER and 
                isinstance(t_data, c4d.BaseContainer) and 
                t_data.GetId() == idMyCommandDataPlugin): 
                print (t_data[0])
                print (t_data[1])
            return True
        
        

        MAXON SDK Specialist
        developers.maxon.net

        chuanzhenC 1 Reply Last reply Reply Quote 0
        • chuanzhenC
          chuanzhen @ferdinand
          last edited by chuanzhen

          Hey @ferdinand ,
          Using the following code, the crash will also occur

          test commanddata plugins code

          import c4d
          import os
          import sys
          from c4d import gui,bitmaps,plugins,utils,modules,storage
          
          
          PLUGIN_ID = 10000000 #only for test
          SUB_DIALOG_ID = 1000001
          
          
          
          SUB_DIALOG_GONE = 5000
          SUB_DIALOG_GONE_NAMETEXT = 5001
          SUB_DIALOG_GONE_NAME = 5002
          
          SUB_DIALOG_GONE_ANIGROUP = 5003
          SUB_DIALOG_GONE_ANIGROUP_ALLRANGE = 5004
          SUB_DIALOG_GONE_ANIGROUP_PRERANGE = 5005
          
          
          SUB_DIALOG_GTWO = 5010
          SUB_DIALOG_GTWO_EDITIMAGE = 5011
          SUB_DIALOG_GTWO_CUTIMAGE = 5012
          
          SUB_DIALOG_IMAGE = 5020
          SUB_DIALOG_SAVE = 5021
          
          #active tool
          def UseSreenShot(active=False,path=None,name=None,size=None,cutimage=False,child_path="image"):
              tool_id = 1000010
          
              doc = c4d.documents.GetActiveDocument()
              tool = plugins.FindPlugin(tool_id, c4d.PLUGINTYPE_TOOL)
              data = plugins.GetToolData(doc, tool_id)
          
              if active:
                  doc.SetAction(tool_id)
                  #c4d.CallCommand(1059888)  # S_ScreenShot
              if path:
                  data[1] = os.path.join(path,child_path)
              if name:
                  data[2] = name
              if size:
                  data[3] = size
          
              if cutimage:
                  tool.Message(tool_id)  #
              else:
                  tool.Message(tool_id) #
          
          
          
          #
          class S_Add_Dialog(gui.GeDialog):
              name = None
              path = None
              size = None
              mode = 0
              ua = None
          
          
              def CreateLayout(self):
                  if self.mode == 3:
                      self.SetTitle("Setting")
                  else:
                      self.SetTitle("New ")
          
                  self.GroupBegin(SUB_DIALOG_GONE,c4d.BFH_SCALEFIT,3,1)
                  self.AddStaticText(SUB_DIALOG_GONE_NAMETEXT,c4d.BFH_LEFT,100,10,"Name:")
                  self.AddEditText(SUB_DIALOG_GONE_NAME,c4d.BFH_SCALEFIT,150,20)
                  if self.mode == 1:
                      #
                      self.AddRadioGroup(SUB_DIALOG_GONE_ANIGROUP,c4d.BFH_RIGHT,2,1)
                      self.AddChild(SUB_DIALOG_GONE_ANIGROUP,SUB_DIALOG_GONE_ANIGROUP_ALLRANGE,"All Range")
                      self.AddChild(SUB_DIALOG_GONE_ANIGROUP, SUB_DIALOG_GONE_ANIGROUP_PRERANGE, "Preview Range")
                      self.SetInt32(SUB_DIALOG_GONE_ANIGROUP,SUB_DIALOG_GONE_ANIGROUP_ALLRANGE)
          
                  self.GroupEnd()
                  self.GroupBegin(SUB_DIALOG_GTWO,c4d.BFH_SCALEFIT,1,1)
                  self.AddButton(SUB_DIALOG_GTWO_CUTIMAGE,c4d.BFH_SCALEFIT,150,30,"Get Image")
                  #self.AddButton(SUB_DIALOG_GTWO_CUTIMAGE, c4d.BFH_SCALEFIT, 150, 30, "Get Image")
                  self.GroupEnd()
          
                  #self.AddUserArea(SUB_DIALOG_IMAGE,c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT,100,100)
                  #self.ua = UserArea_forAdd()
                  #self.AttachUserArea(self.ua,SUB_DIALOG_IMAGE)
                  self.AddButton(SUB_DIALOG_SAVE, c4d.BFH_SCALEFIT, 150, 30, "Save")
          
          
                  return True
              def Command(self, id, msg):
                  return True
          
          
          class S_Lib_Dialog(gui.GeDialog):
          
              sub_dialog = S_Add_Dialog()
          
              def CreateLayout(self):
                  self.SetTitle("test")
          
                  self.AddButton(1001,c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT,150,30,"test")
          
                  return True
          
              def Command(self, id, msg):
                  if id == 1001:
                      #SUB_DIALOG_ID = 1000001
                      UseSreenShot(active=True, path='AAAA', name='BBB')  # active tool,and send message
                      self.sub_dialog.Open(dlgtype=c4d.DLG_TYPE_ASYNC,pluginid=PLUGIN_ID, defaultw=100, defaulth=100,subid=SUB_DIALOG_ID)
          
          
          
                      return True
          
              def Restore(self, pluginid, secret):
                  if secret['subid'] == SUB_DIALOG_ID:
                      return self.sub_dialog.Restore(pluginid, secret)
                  else:
                      return super(S_Lib_Dialog, self).Restore(pluginid, secret)
          
          
          
          
          
          
          class S_Lib(plugins.CommandData):
          
              dialog = None
          
              def Execute(self,doc):
                  # create the dialog
                  if self.dialog is None:
                      self.dialog = S_Lib_Dialog()
          
                  return self.dialog.Open(dlgtype=c4d.DLG_TYPE_ASYNC, pluginid=PLUGIN_ID, defaultw=800, defaulth=400)
          
              def RestoreLayout(self, sec_ref):
                  # manage nonmodal dialog
                  if self.dialog is None:
                      self.dialog = S_Lib_Dialog()
          
                  return self.dialog.Restore(pluginid=PLUGIN_ID, secret=sec_ref)
          
          
          # Execute main()
          if __name__=='__main__':
              path, fn = os.path.split(__file__)
              plugins.RegisterCommandPlugin(id=PLUGIN_ID,
                                            str="test command",
                                            info=0,
                                            help="",
                                            dat=S_Lib(),
                                            icon=None)
          
          

          test tool plugins code:

          import c4d
          import os
          
          from c4d import gui, plugins, bitmaps
          
          PLUGIN_ID = 1000010 #only for test
          
          
          def GetSafeWindow_Center():
              doc = c4d.documents.GetActiveDocument()
              bd = doc.GetActiveBaseDraw()
              window_dict = bd.GetFrame()
          
              window_l = window_dict['cl']
              window_r = window_dict['cr']
              window_t = window_dict['ct']
              window_b = window_dict['cb']
          
          
          
              return bd, window_l, window_r, window_t, window_b
          
          
          def Draw2dbox(bd, x_min, x_max, y_min, y_max):
              p1 = c4d.Vector(x_min, y_min, 0)
              p2 = c4d.Vector(x_max, y_min, 0)
              p3 = c4d.Vector(x_max, y_max, 0)
              p4 = c4d.Vector(x_min, y_max, 0)
              bd.DrawLine2D(p1, p2)
              bd.DrawLine2D(p2, p3)
              bd.DrawLine2D(p3, p4)
              bd.DrawLine2D(p4, p1)
          
          
          def DrawBox(width_height, offset=c4d.Vector(0, 0, 0), color=c4d.Vector(1, 0, 0)):
              # draw box
          
              bd, safe_window_l, safe_window_r, safe_window_t, safe_window_b = GetSafeWindow_Center()
          
              safe_center = ((safe_window_r + safe_window_l) / 2 + offset[0], (safe_window_b + safe_window_t) / 2 + offset[1])
          
              # 
              width_half = width_height[0] / 2
              height_half = width_height[1] / 2
          
              x_min = safe_center[0] - width_half
              x_max = safe_center[0] + width_half
              y_min = safe_center[1] - height_half
              y_max = safe_center[1] + height_half
          
              # 
              bd.SetMatrix_Screen()
              bd.SetPen(color)
              bd.DrawPoint2D(c4d.Vector(safe_center[0], safe_center[1], 0))
              Draw2dbox(bd, x_min, x_max, y_min, y_max)
          
          class S_ScreenShot_Dialog(gui.SubDialog):
              parameters = None
          
              def __init__(self, para_in):
                  self.parameters = para_in
          
              def update(self,command=False):
                  if command:
                      #update gui parameter
                      if self.parameters['path']:
                          self.SetString(1012,self.parameters['path'])
                      if self.parameters['name']:
                          self.SetString(1014,self.parameters['name'])
          
          
              def Command(self,id,msg):
          
                  if id == 1033:
                      return True
                  if id == 1022 or id == 1032:
                      #
                      self.parameters['width'] = self.GetInt32(1022)
                      self.parameters['height'] = self.GetInt32(1032)
                      c4d.DrawViews(c4d.DRAWFLAGS_ONLY_BASEDRAW)
                      return True
          
                  if id == 1021:
                      #
                      self.SetInt32(1032, self.GetInt32(1022))
                      self.parameters['width'] = self.GetInt32(1022)
                      self.parameters['height'] = self.GetInt32(1032)
                      c4d.DrawViews(c4d.DRAWFLAGS_ONLY_BASEDRAW)
                      return True
          
                  if id == 1031:
                      #
                      self.SetInt32(1022, self.GetInt32(1032))
                      self.parameters['width'] = self.GetInt32(1022)
                      self.parameters['height'] = self.GetInt32(1032)
                      c4d.DrawViews(c4d.DRAWFLAGS_ONLY_BASEDRAW)
                      return True
          
                  if id == 1051:
                      #
                      self.parameters['offset'] = c4d.Vector(0)
                      c4d.DrawViews(c4d.DRAWFLAGS_ONLY_BASEDRAW)
                      return True
                  if id == 1012:
                      #
                      self.parameters['path'] = self.GetString(1012)
                      return True
          
                  if id == 1034:
                      #
                      self.parameters['showbmp'] = self.GetBool(1034)
                      return True
          
                  return True
          
              def CreateLayout(self):
                  self.GroupBegin(1010, c4d.BFH_SCALEFIT, 2, 1)
                  self.GroupBorderSpace(10, 10, 10, 10)
                  self.AddStaticText(1011, c4d.BFH_MASK, initw=50, inith=0, name="Path:")
                  bc = c4d.BaseContainer()
                  bc.SetBool(c4d.FILENAME_DIRECTORY,True)
                  self.AddCustomGui(1012, c4d.CUSTOMGUI_FILENAME,"",c4d.BFH_SCALEFIT, 50, 20, bc)
                  self.AddStaticText(1013, c4d.BFH_MASK, initw=50, inith=0, name="Name:")
                  self.AddEditText(1014,c4d.BFH_MASK, initw=200, inith=0)
          
                  self.GroupEnd()
                  self.GroupBegin(1020, c4d.BFH_SCALEFIT, 2, 1)
                  self.GroupBorderSpace(10, 10, 10, 10)
          
                  self.GroupBegin(1100, c4d.BFH_SCALEFIT, 2, 1)
                  self.AddButton(1021, c4d.BFH_MASK, initw=0, inith=0, name="Width:")
                  self.AddEditNumberArrows(1022, c4d.BFH_MASK, initw=70, inith=0)
                  self.AddButton(1031, c4d.BFH_MASK, initw=0, inith=0, name="Height")
                  self.AddEditNumberArrows(1032, c4d.BFH_MASK, initw=70, inith=0)
                  self.GroupEnd()
                  self.AddButton(1051, c4d.BFH_LEFT, initw=100, inith=20, name="Center")
          
                  self.GroupEnd()
                  self.AddButton(1033, c4d.BFH_MASK, 100, 20, "Cut")
                  self.AddCheckbox(1034,c4d.BFH_LEFT,100,10,"Show In Picture View ")
          
                  return True
          
              def InitValues(self):
                  self.SetInt32(1022, self.parameters['width'])
                  self.SetInt32(1032, self.parameters['height'])
                  self.SetBool(1034,self.parameters['showbmp'])
                  if self.parameters['path']:
                      self.SetString(1012, self.parameters['path'])
          
                  return True
          
          
          
          
          
          
          
          
          
          
          
          
          
          class S_ScreenShot(plugins.ToolData):
              subdialog = None
              def __init__(self):
                  self.data = {'width':512,'height':512,'offset':c4d.Vector(0),'path':'','name':'','showbmp':False}
                  self.subdialog = S_ScreenShot_Dialog(self.data)
          
                  #self.last_offset = c4d.Vector(0)
          
              def set_para_self(self,data):
                  #
                  if data[1]:
                      self.data['path'] = data[1]  # path
                  if data[2]:
                      self.data['name'] = data[2]  # name
          
          
              def Message(self,doc, data, type, t_data):
                  if type == PLUGIN_ID:
                      #get commanddata plugins message
                      self.set_para_self(data)
                      # update tool gui parameter
                      self.subdialog.update(True)
                      return True
          
                  return True
              def Draw(self, doc, data, bd, bh, bt, flags):
                  DrawBox((self.data['width'],self.data['height']), self.data['offset'])
                  return c4d.TOOLDRAW_NONE
          
              def MouseInput(self, doc, data, bd, win, msg):
          
                  mx = msg[c4d.BFM_INPUT_X]
                  my = msg[c4d.BFM_INPUT_Y]
          
                  device = 0
                  #print msg[c4d.BFM_INPUT_CHANNEL],c4d.BFM_INPUT_MOUSELEFT,c4d.BFM_INPUT_MOUSERIGHT,c4d.BFM_INPUT_MOUSEMIDDLE
                  if msg[c4d.BFM_INPUT_CHANNEL] == c4d.BFM_INPUT_MOUSELEFT:
                      device = c4d.KEY_MLEFT
                  elif msg[c4d.BFM_INPUT_CHANNEL] == c4d.BFM_INPUT_MOUSERIGHT:
                      device = c4d.KEY_MRIGHT
                  elif msg[c4d.BFM_INPUT_CHANNEL] == c4d.BFM_INPUT_MOUSEMIDDLE:
                      device = c4d.KEY_MMIDDLE
                  else:
                      return True
          
                  dx = 0.0
                  dy = 0.0
          
                  win.MouseDragStart(button=device, mx=int(mx), my=int(my),
                                     flags=c4d.MOUSEDRAGFLAGS_DONTHIDEMOUSE | c4d.MOUSEDRAGFLAGS_NOMOVE)
                  result, dx, dy, channel = win.MouseDrag()
          
                  while result == c4d.MOUSEDRAGRESULT_CONTINUE:
                      mx += dx
                      my += dy
          
          
                      # continue if user doesnt move the mouse anymore
                      if dx == 0.0 and dy == 0.0:
                          result, dx, dy, channel = win.MouseDrag()
                          continue
          
          
                      if msg[c4d.BFM_INPUT_QUALIFIER] & c4d.QSHIFT:
          
                          self.data['offset'] = c4d.Vector(dx, dy, 0) + self.data['offset']
          
                      if msg[c4d.BFM_INPUT_QUALIFIER] & c4d.QCTRL:
                          #
                          scale = dx * 0.0005 + 1.0
                          self.data['width'] *= scale
                          self.data['height'] *= scale
                          self.subdialog.update()
                      #set camera parameter
                      c4d.DrawViews(c4d.DA_ONLY_ACTIVE_VIEW | c4d.DA_NO_THREAD)
          
          
          
                      result, dx, dy, channel = win.MouseDrag()
          
          
                  return True
          
              def AllocSubDialog(self, bc):
                  self.subdialog = S_ScreenShot_Dialog(self.data)
                  return self.subdialog #always return new instance
          
          
          
          
          
          
          
          if __name__ == "__main__":
              dir, file = os.path.split(__file__)
              plugins.RegisterToolPlugin(id=PLUGIN_ID, str="tool_test",
                                         info=0, icon=None,
                                         help="",
                                         dat=S_ScreenShot())
          
          
          

          This is the screen recording of the crash of the above code example:

          bug2.gif

          相信我,可以的!

          1 Reply Last reply Reply Quote 0
          • ManuelM
            Manuel
            last edited by Manuel

            Hi,

            This is what is happening:

            • you have your tool active, the SubDialog is created, and everything works fine.
            • you switch to the move tool, an object inside the SubDialog is destroyed but not the SubDialog itself. You cannot use the subdialog anymore.
            • you use the CommandData to activate the tool, snd data and try to update the DialogBox
            • this is where it crashes because the SubDialog is not in a state allowing to use any GeDialog functionalities.

            After searching a solution for a long time, it seems not possible to catch an event and/or check if the SubDialog can be used or not.
            SetAction or GetAction to check if the current tool is yours cannot be used as it do not reflect the status of the SubDialog.
            i have implemented the CoreMessage function to the SubDialog to catch the event EVMSG_TOOLCHANGED but that do not change anything.

            My next move would be to create the toolData in c++ using DescriptionToolData or even a toolData because there are some functions in c++ to check if the GeDialog is initialized or not.

            Cheers
            Manuel

            MAXON SDK Specialist

            MAXON Registered Developer

            chuanzhenC 1 Reply Last reply Reply Quote 0
            • chuanzhenC
              chuanzhen @Manuel
              last edited by

              @m_magalhaes Thanks for the detailed explanation as to why it crashed!

              相信我,可以的!

              1 Reply Last reply Reply Quote 0
              • maxonM maxon moved this topic from Cinema 4D SDK on
              • First post
                Last post