Tree view boolean column bug
-
I have a problem with a tree view custom gui.
When I toggle any boolean field other than the bottom one it also toggles the top most one by itself.import c4d import weakref # Control IDs ID_TREEVIEW = 1000 ID_BTN_NEW_FOLDER = 1001 ID_BTN_DELETE = 1002 ID_PATHFIELD = 1003 # Tree column IDs ID_CHECK = 1 # Checkbox column ID_NAME = 2 # Name column #--------------------------------------------------------------------- # Basic Entity #--------------------------------------------------------------------- class Entity: def __init__(self, is_root=False, name="Folder"): self.is_root = is_root self.name = name if not is_root else "Root" self.checked = False self.opened = True self.selected = False self.parent = None def AddChild(self, child): child.parent = weakref.ref(self) self.children.append(child) def GetChildren(self): return self.children def GetParent(self): return self.parent() if self.parent else None #--------------------------------------------------------------------- # TreeView Functions #--------------------------------------------------------------------- class SimpleTreeFunctions(c4d.gui.TreeViewFunctions): def __init__(self, dlg): self._dlg = weakref.ref(dlg) # Create the global Root entity. self.root_entity = Entity(is_root=True) # Top-level entities: Root and any added Folders (as siblings). self.entities = [self.root_entity] def GetFirst(self, root, userdata): return self.entities[0] if self.entities else None def GetDown(self, root, userdata, obj): return obj.GetChildren()[0] if obj.GetChildren() else None def GetNext(self, root, userdata, obj): parent = obj.GetParent() siblings = parent.GetChildren() if parent else self.entities idx = siblings.index(obj) + 1 return siblings[idx] if idx < len(siblings) else None def GetPred(self, root, userdata, obj): parent = obj.GetParent() siblings = parent.GetChildren() if parent else self.entities idx = siblings.index(obj) - 1 return siblings[idx] if idx >= 0 else None def GetId(self, root, userdata, obj): return id(obj) def Select(self, root, userdata, obj, mode): # Clear all selections if a new selection is made. if mode == c4d.SELECTION_NEW: for e in self._AllEntities(): e.selected = False if mode in [c4d.SELECTION_NEW, c4d.SELECTION_ADD]: obj.selected = True elif mode == c4d.SELECTION_SUB: obj.selected = False self._dlg().UpdatePathField() def IsSelected(self, root, userdata, obj): return obj.selected def IsOpened(self, root, userdata, obj): return obj.opened def Open(self, root, userdata, obj, onoff): obj.opened = onoff # Toggle checkboxes. def SetCheck(self, root, userdata, obj, column, checked, msg): if column == ID_CHECK: # If the object is global Root, only change when directly clicked. if obj.is_root: obj.checked = bool(checked) else: # When toggling a Folder, ensure the Root's state is preserved. global_root = self.root_entity original = global_root.checked obj.checked = bool(checked) global_root.checked = original self._dlg()._treegui.Refresh() def IsChecked(self, root, userdata, obj, column): if obj.checked: return c4d.LV_CHECKBOX_CHECKED | c4d.LV_CHECKBOX_ENABLED return c4d.LV_CHECKBOX_ENABLED def GetName(self, root, userdata, obj): return obj.name def SetName(self, root, userdata, obj, newname): obj.name = newname self._dlg().UpdatePathField() return True def _AllEntities(self): # Recursively iterate through all entities. def recurse(lst): for ent in lst: yield ent yield from recurse(ent.GetChildren()) return recurse(self.entities) def DeletePressed(self, root, userdata): # Delete selected entities, except the global Root. to_delete = [e for e in self._AllEntities() if e.selected and not e.is_root] for e in to_delete: parent = e.GetParent() if parent: parent.GetChildren().remove(e) else: self.entities.remove(e) self._dlg().UpdatePathField() return True #--------------------------------------------------------------------- # Main Dialog: Contains the tree view, add and delete buttons, and a text field. #--------------------------------------------------------------------- class SimpleTreeDialog(c4d.gui.GeDialog): def __init__(self): self.treeData = None self._treegui = None def CreateLayout(self): self.SetTitle("Simple Tree (Folders as Siblings)") # Buttons: Add Folder and Delete Selected. self.GroupBegin(100, c4d.BFH_SCALEFIT, 2, 1) self.AddButton(ID_BTN_NEW_FOLDER, c4d.BFH_SCALEFIT, name="Add Folder") self.AddButton(ID_BTN_DELETE, c4d.BFH_SCALEFIT, name="Delete Selected") self.GroupEnd() # A text field to display a simple representation of the tree. self.AddEditText(ID_PATHFIELD, c4d.BFH_SCALEFIT) self.AddSeparatorH(0, c4d.BFH_SCALEFIT) # Create the TreeView. bc = c4d.BaseContainer() bc.SetBool(c4d.TREEVIEW_BORDER, True) self._treegui = self.AddCustomGui(ID_TREEVIEW, c4d.CUSTOMGUI_TREEVIEW, "", c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, 400, 300, bc) if not self._treegui: print("Error: Could not create TreeView") return False # Set up the tree layout: a checkbox and name column. layout = c4d.BaseContainer() layout.SetLong(ID_CHECK, c4d.LV_CHECKBOX) layout.SetLong(ID_NAME, c4d.LV_TREE) self._treegui.SetLayout(2, layout) self._treegui.SetHeaderText(ID_CHECK, "Check") self._treegui.SetHeaderText(ID_NAME, "Name") # Initialize tree data: global Root is the first (and fixed) sibling. self.treeData = SimpleTreeFunctions(self) self._treegui.SetRoot(None, self.treeData, None) self._treegui.Refresh() self.UpdatePathField() return True def Command(self, id, msg): if id == ID_BTN_NEW_FOLDER: self.AddFolder() elif id == ID_BTN_DELETE: self.treeData.DeletePressed(None, None) self._treegui.Refresh() return True def AddFolder(self): # Create a new Folder entity and add it as a sibling (top-level). new_entity = Entity(is_root=False, name="Folder") self.treeData.entities.append(new_entity) self._treegui.Refresh() self.UpdatePathField() def UpdatePathField(self): # Build a simple string showing the names of top-level entities. names = [ent.name for ent in self.treeData.entities] self.SetString(ID_PATHFIELD, " | ".join(names)) if __name__=='__main__': dlg = SimpleTreeDialog() dlg.Open(c4d.DLG_TYPE_ASYNC, pluginid=0, defaultw=500, defaulth=400)
-
Hi @Gregor-M please make sure that your script is executable before sending it to us. Since in it's current state it does raise
Traceback (most recent call last): File "scriptmanager", line 53, in GetDown File "scriptmanager", line 33, in GetChildren AttributeError: 'Entity' object has no attribute 'children'. Did you mean: 'GetChildren'?
I added self.children = [] within the __init__ of Entity and it fixed the issue but this indicate that you are working on a different version than us. But even with it I'm not able to reproduce any issue and everything is working as expected.
With that's said your else statement within the SetCheck looks very suspicious and I won't be surprised that the behavior you are experiencing is coming from this else statement.
Finally note that you can call the function TreeViewCustomGui.SetRoot to define a root. Then the root argument that is available in all functions will be pointing to it.
Cheers,
Maxime.