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
    • Register
    • Login

    After calling BaseContainer.Sort(), the stored values are lost

    Scheduled Pinned Locked Moved Cinema 4D SDK
    2026python
    3 Posts 2 Posters 28 Views 1 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.
    • chuanzhenC Offline
      chuanzhen
      last edited by chuanzhen

      Hi,
      My requirement is to rearrange the basecontainer in order of id, so I used the BaseContainer().Sort() function. However, there were two issues in the simple test:

      1. the stored values were lost
      2. it was not arranged in order of id
      import c4d
      
      doc: c4d.documents.BaseDocument  # The currently active document.
      op: c4d.BaseObject | None  # The primary selected object in `doc`. Can be `None`.
      
      def main() -> None:
          bc = c4d.BaseContainer()
          for i in range(4,12):
              bc.SetInt32(i,i)
          for i in range(3):
              bc.SetInt32(i,i)
          for id,v in bc:
              print(id,v)
          print("--------")
          bc.Sort()
          for id,v in bc:
              print(id,v)
          
          
      if __name__ == '__main__':
          main()
      

      Did I misunderstand this function?

      相信我,可以的!

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

        Hey @chuanzhen,

        Thank you for reporting this. Yes, you are misunderstanding this function a bit. But I have to admit that I did too first, because the function is very weird in what it does. But when you look at the C++ docs, there is a warning.

        846d8773-a0bf-4ac4-b6b0-6207ca995bf6-image.png

        I have just updated the Python docs for the next release to also contain the warning. So, this function is effectively only useable for containers holding strings. As you can see by my example below, it does not only remove all values but turns them into strings (and then leaves them empty when the source was not a string). The function is badly named.

        Find also a little example below to do what you want: sort a container by values.

        Cheers,
        Ferdinand

        Result

        Root (None , id = -1):
        ├── 4 (DTYPE_LONG): 4
        ├── 5 (DTYPE_LONG): 5
        ├── 6 (DTYPE_LONG): 6
        ├── 7 (DTYPE_LONG): 7
        ├── 8 (DTYPE_LONG): 8
        ├── 9 (DTYPE_LONG): 9
        ├── 10 (DTYPE_LONG): 10
        ├── 11 (DTYPE_LONG): 11
        ├── 0 (DTYPE_LONG): 0
        ├── 1 (DTYPE_LONG): 1
        └── 2 (DTYPE_LONG): 2
        --------
        Root (None , id = -1):
        ├── 1 (DTYPE_STRING):
        ├── 4 (DTYPE_STRING):
        ├── 5 (DTYPE_STRING):
        ├── 6 (DTYPE_STRING):
        ├── 7 (DTYPE_STRING):
        ├── 8 (DTYPE_STRING):
        ├── 9 (DTYPE_STRING):
        ├── 10 (DTYPE_STRING):
        ├── 11 (DTYPE_STRING):
        ├── 0 (DTYPE_STRING):
        └── 2 (DTYPE_STRING):
        

        Code

        import c4d
        import mxutils
        
        doc: c4d.documents.BaseDocument  # The currently active document.
        op: c4d.BaseObject | None  # The primary selected object in `doc`. Can be `None`.
        
        def main() -> None:
            bc = c4d.BaseContainer()
            for i in range(4,12):
                bc.SetInt32(i,i)
            for i in range(3):
                bc.SetInt32(i,i)
            print(mxutils.GetContainerTreeString(bc))
            print("--------")
            bc.Sort()
            print(mxutils.GetContainerTreeString(bc))
            
            
        if __name__ == '__main__':
            main()
        

        Custom Sorting Function

        Result

        Root (None , id = -1):
        ├── 0 (DTYPE_LONG): 0
        ├── 1 (DTYPE_LONG): 1
        ├── 2 (DTYPE_LONG): 2
        ├── 3 (DTYPE_LONG): 3
        ├── 5 (DTYPE_LONG): 5
        ├── 6 (DTYPE_LONG): 6
        ├── 7 (DTYPE_LONG): 7
        ├── 8 (DTYPE_LONG): 8
        ├── 9 (DTYPE_LONG): 9
        ├── 10 (DTYPE_LONG): 10
        ├── 11 (DTYPE_LONG): 11
        ├── 12 (DTYPE_LONG): 12
        ├── 13 (DTYPE_LONG): 13
        ├── 14 (DTYPE_LONG): 14
        ├── 15 (DTYPE_LONG): 15
        ├── 16 (DTYPE_LONG): 16
        ├── 17 (DTYPE_LONG): 17
        ├── 18 (DTYPE_LONG): 18
        ├── 19 (DTYPE_LONG): 19
        └── 4 (DTYPE_SUBCONTAINER , id = -1):
            ├── 0 (DTYPE_LONG): 0
            ├── 1 (DTYPE_LONG): 1
            ├── 2 (DTYPE_LONG): 2
            ├── 3 (DTYPE_LONG): 3
            ├── 4 (DTYPE_LONG): 4
            ├── 5 (DTYPE_LONG): 5
            ├── 6 (DTYPE_LONG): 6
            ├── 7 (DTYPE_LONG): 7
            ├── 8 (DTYPE_LONG): 8
            ├── 9 (DTYPE_LONG): 9
            ├── 10 (DTYPE_LONG): 10
            ├── 11 (DTYPE_LONG): 11
            ├── 12 (DTYPE_LONG): 12
            ├── 13 (DTYPE_LONG): 13
            ├── 14 (DTYPE_LONG): 14
            ├── 15 (DTYPE_LONG): 15
            ├── 16 (DTYPE_LONG): 16
            ├── 17 (DTYPE_LONG): 17
            ├── 18 (DTYPE_LONG): 18
            └── 19 (DTYPE_LONG): 19
        

        Code

        import c4d
        import mxutils
        
        import random
        
        doc: c4d.documents.BaseDocument  # The currently active document.
        op: c4d.BaseObject | None  # The primary selected object in `doc`. Can be `None`.
        
        def SortContainer(bc: c4d.BaseContainer, mode: str = "value") -> None:
            """Sorts the given container by ID or value.
        
            This function reallocates sub-containers but modifies the passed in container in place. It would also be
            possible to modify the sub-container in place, but more work to implement and also more complex to run.
            Due to the fact that we have to copy container data, this is also not the cheapest function.
            """
            def copy(bc: c4d.BaseContainer) -> list[tuple[int, any]]:
                """Copies the contents of the given BaseContainer to a list of (ID, value) tuples.
                """
                result = []
                for i, v in bc:
                    if isinstance(v, c4d.BaseContainer):
                        v = copy(v)
                    result.append((i, v))
                return result
        
            def build(bc: c4d.BaseContainer, items: list[tuple[int, any]]) -> None:
                """Builds a BaseContainer from the given list of (ID, value) tuples.
                """
                bc.FlushAll()
                for i, v in items:
                    if isinstance(v, list):
                        sub = c4d.BaseContainer()
                        build(sub, v)
                        v = sub
                    bc[i] = v
        
            def sort_items(items: list[tuple[int, any]], mode: str) -> list[tuple[int, any]]:
                """Sorts the given list of (ID, value) tuples by ID or value. Nested containers are sorted recursively.
                """
                def is_nested(item: tuple[int, any]) -> bool:
                    return isinstance(item[1], (list, tuple))
        
                def sort_key(item: tuple[int, any]) -> tuple[bool, any]:
                    item_id, value = item
        
                    # The first key puts nested values after scalar values.
                    # The second key is only compared within the same group.
                    if is_nested(item):
                        return True, item_id if mode == "id" else 0
        
                    return False, item_id if mode == "id" else value
        
                # Sort nested contents recursively before sorting this level.
                for index, (item_id, value) in enumerate(items):
                    if is_nested((item_id, value)):
                        items[index] = (item_id, sort_items(value, mode))
        
                return sorted(items, key=sort_key)
        
            items: list[tuple[int, any]] = copy(bc)
            return build(bc, sort_items(items, mode))
        
        def main() -> None:
            """
            """
            bc: c4d.BaseContainer = c4d.BaseContainer()
            data: list[int] = list(range(20))
            random.shuffle(data)
            for v in data:
                bc[v] = v
        
            copy: c4d.BaseContainer = bc.GetClone(0)
            bc[4] = copy
        
            print(mxutils.GetContainerTreeString(bc))
            print("--------")
            SortContainer(bc)
            print(mxutils.GetContainerTreeString(bc))
            
            
        if __name__ == '__main__':
            main()
        

        MAXON SDK Specialist
        developers.maxon.net

        chuanzhenC 1 Reply Last reply Reply Quote 0
        • ferdinandF ferdinand moved this topic from Cinema 4D SDK
        • ferdinandF ferdinand moved this topic from Bugs
        • chuanzhenC Offline
          chuanzhen @ferdinand
          last edited by

          @ferdinand Thanks for your detailed explanation and the implementation of the sorting function. It's very helpful!

          相信我,可以的!

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