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.
/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()