TreeView for ObjectData plugin

Hello!
Can't figure out how to add TreeView gui to ObjectData attributes interface.
There is some tips here, but is not clear how it can be used in python.🤷‍♂️
Here is a sample code what i want to implement.

testgen.zip

/res/description/Otestgen.h

enum {
    Otestgen = 100500,
    TG_TEST_GROUP = 1000,
    TG_TAGSGROUP_GROUP = 1001,
    TG_TAGS = 1002,
};

/res/description/Otestgen.res

CONTAINER Otestgen {
    INCLUDE Obase;
    NAME Otestgen;
    GROUP ID_OBJECTPROPERTIES {
    }
    GROUP TG_TEST_GROUP {
        DEFAULT 1;
        GROUP TG_TAGSGROUP_GROUP {
            DEFAULT 1;
            ITEMTREE TG_TAGS { }
        }
    }
}

/res/strings_us/description/Otestgen.str

STRINGTABLE Otestgen {
    Otestgen "TestGen";
    TG_TEST_GROUP "Test";
    TG_TAGSGROUP_GROUP "";
    TG_TAGS "Tags";
}

/testgen.pyp

import c4d
import os


def load_bitmap(path):
    path = os.path.join(os.path.dirname(__file__), path)
    bmp = c4d.bitmaps.BaseBitmap()
    if bmp.InitWith(path)[0] != c4d.IMAGERESULT_OK:
        bmp = None
    return bmp



class tagObject(object):
    """
    Class which represent a texture, aka an Item in our list
    """
    name = "Default"
    _checked = True  
    _selected = False

    def __init__(self, name):
        self.name = name

    @property
    def IsSelected(self):
        return self._selected

    def Select(self):
        self._selected = True

    def Deselect(self):
        self._selected = False

    def __repr__(self):
        return str(self)

    def __str__(self):
        return self.name

class ListView(c4d.gui.TreeViewFunctions):

    def __init__(self):
        self.tagList = list() # Store all objects we need to display in this list

    def IsResizeColAllowed(self, root, userdata, lColID):
        return True

    def IsTristate(self, root, userdata):
        return False

    def GetColumnWidth(self, root, userdata, obj, col, area):

        if col == ID_NAME:
            return 200

        return 80  # All have the same initial width

    def IsMoveColAllowed(self, root, userdata, lColID):
        # The user is allowed to move all columns.
        # TREEVIEW_MOVE_COLUMN must be set in the container of AddCustomGui.
        return False

    def GetFirst(self, root, userdata):
        """
        Return the first element in the hierarchy, or None if there is no element.
        """
        rValue = None if not self.tagList else self.tagList[0]
        return rValue


    def GetNext(self, root, userdata, obj):
        """
        Returns the next Object to display after arg:'obj'
        """
        rValue = None

        pList = self.tagList
        
        currentObjIndex = pList.index(obj)
        nextIndex = currentObjIndex + 1
        if nextIndex < len(pList):
            rValue = pList[nextIndex]

        return rValue

    def GetPred(self, root, userdata, obj):
        """
        Returns the previous Object to display before arg:'obj'
        """
        pList = self.tagList
        
        currentObjIndex = pList.index(obj)
        nextIndex = currentObjIndex - 1
        if nextIndex < len(pList):
            rValue = pList[nextIndex]

    def GetId(self, root, userdata, obj):
        """
        Return a unique ID for the element in the TreeView.
        """
        return hash(obj)

    def Select(self, root, userdata, obj, mode):
        """
        Called when the user selects an element.
        """
        if mode == c4d.SELECTION_NEW:
            for preset in self.tagList:
                preset.Deselect()
            obj.Select()
        elif mode == c4d.SELECTION_ADD:
            obj.Select()
        elif mode == c4d.SELECTION_SUB:
            obj.Deselect()

    def IsSelected(self, root, userdata, obj):
        """
        Returns: True if *obj* is selected, False if not.
        """
        return obj.IsSelected

    def SetCheck(self, root, userdata, obj, column, checked, msg):
        """
        Called when the user clicks on a checkbox for an object in a
        `c4d.LV_CHECKBOX` column.
        """
        if checked:
            obj.checked = True
        else:
            obj.checked = False

    def IsChecked(self, root, userdata, obj, column):
        """
        Returns: (int): Status of the checkbox in the specified *column* for *obj*.
        """
        if obj.checked:
            return c4d.LV_CHECKBOX_CHECKED | c4d.LV_CHECKBOX_ENABLED
        else:
            return c4d.LV_CHECKBOX_ENABLED

    def GetName(self, root, userdata, obj):
        """
        Returns the name to display for arg:'obj', only called for column of type LV_TREE
        """
        return str(obj) # Or obj.presetType

    def SetName(self, root, userdata, obj, name):
        """
        Returns the name to display for arg:'obj', only called for column of type LV_TREE
        """
        obj.name = name
        

    def ContextMenuCall(self, root, userdata, obj, lColumn, lCommand):
        
        """
        The user executes an entry of the context menu.

        Returns:
            (bool): True if the event was handled, False if not.
        """
        if lCommand == c4d.ID_TREEVIEW_CONTEXT_RESET:
            print ('Call Reset')
            return True

        if lCommand == c4d.ID_TREEVIEW_CONTEXT_REMOVE:
            print ('Call Remove All')
            return True


    def DoubleClick(self, root, userdata, obj, col, mouseinfo):
        """
        Called when the user double-clicks on an entry in the TreeView.

        Returns:
          (bool): True if the double-click was handled, False if the
            default action should kick in. The default action will invoke
            the rename procedure for the object, causing `SetName()` to be
            called.
        """

        return False

    def DeletePressed(self, root, userdata):
        """Called when a delete event is received."""
        
        for preset in reversed(self.tagList):
            
            if preset.IsSelected:
                self.tagList.remove(preset)
                continue

class TestGenData(c4d.plugins.ObjectData):

    PLUGIN_ID = 100500 #Example PLUgiN Id 
    PLUGIN_NAME = 'TestGen'
    PLUGIN_INFO = c4d.OBJECT_GENERATOR 
    PLUGIN_DESC = 'Otestgen'
    PLUGIN_ICON = load_bitmap('res/icons/TestGen.tiff')
    PLUGIN_DISKLEVEL = 0

    # TreeView Column IDs.
    ID_CHECKBOX = 1
    ID_NAME = 2

    @classmethod
    def Register(cls):
        return c4d.plugins.RegisterObjectPlugin(
            cls.PLUGIN_ID, cls.PLUGIN_NAME, cls, cls.PLUGIN_DESC, cls.PLUGIN_INFO,
            cls.PLUGIN_ICON, cls.PLUGIN_DISKLEVEL)

    def __init__(self):
        self.SetOptimizeCache(True)

    def Init(self, node):
        # ??????
        self.InitAttr(node, c4d.TreeViewCustomData, [res.TG_TAGS]) # How to Inint this CustomDataType attribute??
        # ??????
        return True

    def GetDDescription(self, node, description, flags):
      
        # Before adding dynamic parameters, load the parameters from the description resource
        if not description.LoadDescription(node.GetType()):
            return False

        singleID = description.GetSingleDescID()
        myTreeID = c4d.DescID(c4d.TG_TAGS)

        # ?????? 
        # This is a way i think how to add TreeView to attributes. But how to connect TreeView class object to it?
        # ??????
        if singleID is None or myTreeID.IsPartOf(singleID)[0]:
            # Add dynamic parameter
            bc = c4d.GetCustomDataTypeDefault(c4d.CUSTOMGUI_TREEVIEW)
        
            bc.SetInt32(c4d.DESC_CUSTOMGUI, c4d.CUSTOMGUI_TREEVIEW)
            bc.SetString(c4d.DESC_NAME, "Animators")
            bc.SetString(c4d.DESC_SHORT_NAME, "Animators");
            bc.SetLong(c4d.DESC_ANIMATE, c4d.DESC_ANIMATE_OFF);  
            bc.SetBool(c4d.TREEVIEW_OUTSIDE_DROP, False) 
            bc.SetBool(c4d.TREEVIEW_HAS_HEADER, True)
            bc.SetBool(c4d.TREEVIEW_BORDER, c4d.BORDER_THIN_IN)
            bc.SetBool(c4d.TREEVIEW_HAS_HEADER, False) # True if the tree view may have a header line.
            bc.SetBool(c4d.TREEVIEW_HIDE_LINES, False) # True if no lines should be drawn.
            bc.SetBool(c4d.TREEVIEW_MOVE_COLUMN, True) # True if the user can move the columns.
            bc.SetBool(c4d.TREEVIEW_RESIZE_HEADER, False) # True if the column width can be changed by the user.
            bc.SetBool(c4d.TREEVIEW_FIXED_LAYOUT, True) # True if all lines have the same height.
            bc.SetBool(c4d.TREEVIEW_ALTERNATE_BG, True) # Alternate background per line.
            bc.SetBool(c4d.TREEVIEW_CURSORKEYS, True) # True if cursor keys should be processed.
            bc.SetBool(c4d.TREEVIEW_NOENTERRENAME, False) # Suppresses the rename popup when the user presses enter.
            bc.SetBool(c4d.TREEVIEW_NO_MULTISELECT, True) 
        
            # Add TREEVIEW to the parameters in specified group
            if not description.SetParameter(myTreeID, bc, c4d.DescID(c4d.TG_TAGSGROUP_GROUP)): # group ID from above post
                return False


    def GetVirtualObjects(self, op, hh):
        return c4d.BaseObject(c4d.Ocube)

if __name__ == '__main__':
    TestGenData.Register()

Checkout my python tutorials, plugins, scripts, xpresso presets and more
https://mikeudin.net

Hi Mike this is unfortunately not possible, even in C++ for more info please look at Info for ITEMTREE/customgui_itemtree.

Cheers,
Maxime.