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

    set GvNode value via python

    Scheduled Pinned Locked Moved Cinema 4D SDK
    windowspython2026
    4 Posts 2 Posters 47 Views 2 Watching
    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.
    • DunhouD Offline
      Dunhou
      last edited by

      Hey community,

      This my first dive into graphview node, sorry if this is a stupid basic question.

      I want to set condition node input port (GvPort) value, but it seems I can not do this via __settitem__ / SetParameter, is this is a limit or I do it in wrong way?

      Cheers~
      DunHou
      b8c3289c-5cf6-4c7b-b30d-278e78852e8a-image.png

      import c4d
      
      def iterateNodes(node):
          while node:
              description = node.GetDescription(c4d.DESCFLAGS_DESC_0)
              data: c4d.BaseContainer
              pid: c4d.DescID
              for data, pid, _ in description:
                  value: any | None = None
                  try:
                      value = node[pid]
                  except:
                      value = "Inaccessible in Python"
                  
                  if str(data[c4d.DESC_NAME]).startswith("Input"):
                      print(f"\tInput '{data[c4d.DESC_NAME]}'(c4d.{data[c4d.DESC_IDENT]}) = {value} type: {type(value)}")
                      
              
              node[2000, 1001] = 2 # TypeError: __setitem__ got unexpected type 'int'.
              node.SetParameter([2000, 1002],2,0) # no error but not worrk.
              print(node[2000, 1001].GetDataInstance()) # AttributeError: parameter access failed
              
              if node.IsGroupNode():
                  iterateNodes(node.GetDown())
      
              node = node.GetNext()
      
      def main():
          # Checks if selected object is valid
          if op is None:
              raise ValueError("op is none, please select one object.")
      
          # Retrieves the xpresso Tag
          xpressoTag = op.GetTag(c4d.Texpresso)
          if xpressoTag is None:
              raise ValueError("Make sure the selected object get an Xpresso Tag.")
      
          # Retrieves the node master
          gvNodeMaster = xpressoTag.GetNodeMaster()
          if gvNodeMaster is None:
              raise RuntimeError("Failed to retrieve the Node Master.")
      
          # Retrieves the Root node (the Main XGroup) that holds all others nodes
          gvRoot = gvNodeMaster.GetRoot()
          if gvRoot is None:
              raise RuntimeError("Failed to retrieve the Root Node.")
      
          # Iterates overs all nodes of this root node.
          iterateNodes(gvRoot)
      
      
      if __name__ == '__main__':
          main()
      

      https://boghma.com
      https://github.com/DunHouGo

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

        Hey @Dunhou,

        Thank you for reaching out to us. This is probably possible, but I'll have to check with a debugger myself. These are dynamically generated ports which tend to be a pain in the butt in Xpresso, as pretty much every node type handles them slightly differently, and even by looking at our source code you often do not get any wiser. My guess would have been that it is something like c4d.DescID(c4d.DescLevel(c4d.GV_CONDITION_INPUT, c4d.DTYPE_SUBCONTAINER, condNode.GetType()), c4d.DescLevel(0, c4d.DTYPE_REAL, 0)) or the same but other values for the second desc level such as 1000, 2000, etc. instead of 0. GV_CONDITION_INPUT is defined in the description as the input group.

        I will have to attach a debugger to see what is going on, but where I am currently working from, I have no compiled version of Cinema 4D. Will check in the next days from my Mac, where I do have a compiled version. You can check this thread for context, the function PythonNodeLayerShaderAccess goes into some details (a Python node also has a dynamic/variadic number of in- and output ports).

        Cheers,
        Ferdinand

        """My attempt in setting the dynamic ID_OPERATOR_CONDITION operator in ports (does not work). 
        """
        
        import c4d
        
        from c4d.modules.graphview import GvNodeMaster, GvNode, GvPort, XPressoTag
        from mxutils import CheckType
        
        doc: c4d.documents.BaseDocument  # The currently active document.
        op: c4d.BaseObject | None  # The primary selected object in `doc`. Can be `None`.
        
        
        def main() -> None:
            """Called by Cinema 4D when the script is being executed.
            """
            null: c4d.BaseObject = CheckType(c4d.BaseObject(c4d.Onull))
            tag: XPressoTag = CheckType(null.MakeTag(c4d.Texpresso))
            master: GvNodeMaster = CheckType(tag.GetNodeMaster())
            doc.InsertObject(null)
        
            root: GvNode = CheckType(master.GetRoot())
            condNode: GvNode = CheckType(master.CreateNode(root, c4d.ID_OPERATOR_CONDITION, x=200, y=200))
        
            # Set values:
            did_1: c4d.DescID = c4d.DescID(
                c4d.DescLevel(c4d.GV_CONDITION_INPUT, c4d.DTYPE_SUBCONTAINER, condNode.GetType()),
                c4d.DescLevel(0, c4d.DTYPE_REAL, 0)
            )
            did_2: c4d.DescID = c4d.DescID(
                c4d.DescLevel(c4d.GV_CONDITION_INPUT, c4d.DTYPE_SUBCONTAINER, condNode.GetType()),
                c4d.DescLevel(1, c4d.DTYPE_REAL, 0)
            )
        
            a = condNode.SetParameter(did_1, 10.0, c4d.DESCFLAGS_SET_0)
            b = condNode.SetParameter(did_2, 20.0, c4d.DESCFLAGS_SET_0)
            print(a, b)
        
            c4d.EventAdd()
        
        
        if __name__ == '__main__':
            main()
        

        MAXON SDK Specialist
        developers.maxon.net

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

          Hey,

          so, I actually did get it right, I apparently just did not check properly when I tested the common case of second level IDs of dynamic properties starting off at 1000.

          Cheers,
          Ferdinand

          """Demonstrates how to set values on dynamic input ports of a Condition node in an XPresso tag.
          """
          
          import c4d
          
          from c4d.modules.graphview import GvNodeMaster, GvNode, GvPort, XPressoTag
          from mxutils import CheckType
          
          doc: c4d.documents.BaseDocument  # The currently active document.
          op: c4d.BaseObject | None  # The primary selected object in `doc`. Can be `None`.
          
          
          def main() -> None:
              """Called by Cinema 4D when the script is being executed.
              """
              # Create a new XPresso tag with a Condition node.
              null: c4d.BaseObject = CheckType(c4d.BaseObject(c4d.Onull))
              tag: XPressoTag = CheckType(null.MakeTag(c4d.Texpresso))
              master: GvNodeMaster = CheckType(tag.GetNodeMaster())
              doc.InsertObject(null)
          
              root: GvNode = CheckType(master.GetRoot())
              condNode: GvNode = CheckType(master.CreateNode(root, c4d.ID_OPERATOR_CONDITION, x=200, y=200))
          
              # Count the number of input ports on the Condition node. For our case, a freshly instantiated
              # node we do not really have to do this, since there will always be static "Switch" port and
              # two dynamic input ports. But when we are working on an existing graph, we have to count the
              # ports to know how many dynamic ports there are.
              inputPorts: list[GvPort] = condNode.GetInPorts()
              portCount: int = len(inputPorts)
          
              # There is no predefined symbol for this, the implementation counts just ports, so dynamic ports
              # start at 1001 because the input port GV_CONDITION_SWITCH has the ID 1000.
              idDynamicStart: int = 1001
          
              # Iterate over all dynamic input ports (-1 -> skipping the first static "Switch" port).
              for i in range(portCount - 1): 
                  # Define the DescID for the dynamic input port.
                  did: c4d.DescID = c4d.DescID(
                      # The first desc level for variadic/dynamic ports is always a sub container. For a 
                      # condition node where the variadic ports can only be inputs and always are of one type,
                      # this is GV_CONDITION_INPUT. Other nodes which can have variadic ports of different 
                      # types (like the Python node) have different sub container IDs for inputs and outputs, 
                      # e.g., IN_REAL for all real inputs, IN_STRING for all string inputs, OUT_LONG for all 
                      # long outputs, etc.
                      c4d.DescLevel(c4d.GV_CONDITION_INPUT, c4d.DTYPE_SUBCONTAINER, condNode.GetType()),
                      # And the second desc level is the dynamic port ID, starting at 1001 for the first dynamic
                      # input port, 1002 for the second dynamic input port, and so on. It unfortunately not very
                      # standardized, where these start.
                      c4d.DescLevel(idDynamicStart + i, c4d.DTYPE_REAL, 0)
                  )
                  # Set the value of the dynamic input port to some float value.
                  result: bool = condNode.SetParameter(did, float((i + 1) * 10), c4d.DESCFLAGS_SET_0)
                  if not result:
                      print("Failed to set value on port index:", i)
          
              # Or another common workflow, we want to set a dynamic port by port label (instead of knowing
              # the index in advance).
          
              # Find the first port that starts with "Input".
              targetLabel: str = "Input"
              targetPort: GvPort | None = next((port for port in inputPorts 
                                                if port.GetName(condNode).startswith(targetLabel)), None)
              
              # We found a port with that label.
              if targetPort is not None:
                  # Unlike in the Nodes API, we cannot set the value on a port directly, we always have to go
                  # through the node and use a DescID to identify the port. So we must find the index of the 
                  # port in the input ports list to calculate the dynamic port ID.
                  index: int = inputPorts.index(targetPort)
                  result: bool = condNode.SetParameter(
                      c4d.DescID(
                          c4d.DescLevel(c4d.GV_CONDITION_INPUT, c4d.DTYPE_SUBCONTAINER, condNode.GetType()),
                          c4d.DescLevel(idDynamicStart + (index - 1), c4d.DTYPE_REAL, 0)
                      ),
                      99.9, # Set the value to 99.9
                      c4d.DESCFLAGS_SET_0
                  )
                  if not result:
                      print("Failed to set value on port:", targetPort.GetName(condNode))
          
              c4d.EventAdd()
          
          if __name__ == '__main__':
              main()
          

          MAXON SDK Specialist
          developers.maxon.net

          DunhouD 1 Reply Last reply Reply Quote 1
          • DunhouD Offline
            Dunhou @ferdinand
            last edited by

            Thanks for @ferdinand awesome answer!
            I found DescLevel and DescId always confused to me, but them do can access more than simple set item, time to dive deeper to the DescLevel part 😦

            Cheers~
            DunHou

            https://boghma.com
            https://github.com/DunHouGo

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