Skip to content

Commit d3bee2c

Browse files
committed
update treeform
1 parent 6c7c8ee commit d3bee2c

File tree

2 files changed

+89
-55
lines changed

2 files changed

+89
-55
lines changed

scripts/treeform.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,11 @@
2727

2828

2929
treeform = Treeform()
30-
viewer.ui.sidebar.widget.addWidget(treeform)
30+
viewer.ui.sidebar.add(treeform)
3131

3232
def update_treeform(form, node):
3333
treeform.update_from_dict({"name": node.name, "objtype": node.__class__, "item": node.item, "settings": node.settings})
3434

35-
viewer.ui.sidebar.sceneform.callback = update_treeform
35+
viewer.ui.sidebar.sceneform.action = update_treeform
3636

3737
viewer.show()

src/compas_viewer/components/treeform.py

Lines changed: 87 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -8,61 +8,60 @@
88
from compas.datastructures import Tree
99
from compas.datastructures import TreeNode
1010

11+
from .component import Component
1112

12-
class Treeform(QTreeWidget):
13+
14+
class Treeform(Component):
1315
"""
14-
Class for displaying tree-like data.
15-
Treeform is an abstract class that could be placed in either the viewport or the sidedock.
16+
A component for displaying hierarchical tree-like data in a tree widget.
17+
18+
This component provides a flexible way to visualize tree structures with
19+
customizable columns and background colors. It supports selection callbacks
20+
and can be used in various UI layouts.
1621
1722
Parameters
1823
----------
19-
tree : :class:`compas.datastructures.Tree`
20-
The tree to be displayed. An typical example is the scene
21-
object tree: :attr:`compas_viewer.viewer.Viewer._tree`.
22-
columns : dict[str, callable]
23-
A dictionary of column names and their corresponding attributes.
24-
Example: ``{"Name": (lambda o: o.name), "Object": (lambda o: o)}``
24+
tree : Tree, optional
25+
The tree data structure to display. Defaults to an empty tree.
26+
columns : dict[str, Callable], optional
27+
Dictionary mapping column names to functions that extract values from tree nodes.
28+
Defaults to {"Name": lambda node: node.name, "Value": lambda node: node.attributes.get("value", "")}.
2529
show_headers : bool, optional
26-
Show the header of the tree.
27-
Defaults to ``True``.
30+
Whether to show column headers. Defaults to True.
2831
stretch : int, optional
29-
Stretch factor of the tree in the grid layout.
30-
Defaults to ``2``.
31-
backgrounds : dict[str, callable], optional
32-
A dictionary of column names and their corresponding color.
33-
Example: ``{"Object-Color": (lambda o: o.surfacecolor)}``
32+
Stretch factor for the tree widget in grid layouts. Defaults to 2.
33+
backgrounds : dict[str, Callable], optional
34+
Dictionary mapping column names to functions that return background colors.
35+
action : Callable, optional
36+
Function to call when tree items are selected. Receives the selected node as argument.
3437
3538
Attributes
3639
----------
37-
tree : :class:`compas.datastructures.Tree`
38-
The tree to be displayed.
39-
40-
See Also
41-
--------
42-
:class:`compas.datastructures.Tree`
43-
:class:`compas.datastructures.tree.TreeNode`
44-
:class:`compas_viewer.layout.SidedockLayout`
45-
46-
References
47-
----------
48-
:PySide6:`PySide6/QtWidgets/QTreeWidget`
40+
widget : QTreeWidget
41+
The Qt tree widget for displaying the data.
42+
tree : Tree
43+
The tree data structure being displayed.
44+
columns : dict[str, Callable]
45+
Column definitions for the tree display.
46+
stretch : int
47+
Stretch factor for layout purposes.
48+
action : Callable or None
49+
Selection action function.
4950
5051
Examples
5152
--------
52-
.. code-block:: python
53-
54-
from compas_viewer import Viewer
55-
56-
viewer = Viewer()
57-
58-
for i in range(10):
59-
for j in range(10):
60-
sp = viewer.scene.add(Sphere(0.1, Frame([i, j, 0], [1, 0, 0], [0, 1, 0])), name=f"Sphere_{i}_{j}")
61-
62-
viewer.layout.sidedock.add_element(Treeform(viewer._tree, {"Name": (lambda o: o.object.name), "Object": (lambda o: o.object)}))
63-
64-
viewer.show()
65-
53+
>>> # Create a simple tree form
54+
>>> treeform = Treeform()
55+
>>> treeform.update()
56+
57+
>>> # Create with custom columns
58+
>>> columns = {"Name": lambda node: node.name, "Type": lambda node: type(node).__name__}
59+
>>> treeform = Treeform(columns=columns)
60+
61+
>>> # Create with selection action
62+
>>> def on_select(node):
63+
... print(f"Selected: {node.name}")
64+
>>> treeform = Treeform(action=on_select)
6665
"""
6766

6867
def __init__(
@@ -72,30 +71,38 @@ def __init__(
7271
show_headers: bool = True,
7372
stretch: int = 2,
7473
backgrounds: Optional[dict[str, Callable]] = None,
75-
callback: Optional[Callable] = None,
74+
action: Optional[Callable] = None,
7675
):
7776
super().__init__()
77+
self.widget = QTreeWidget()
78+
7879
self.columns = columns or {"Name": lambda node: node.name, "Value": lambda node: node.attributes.get("value", "")}
79-
self.setColumnCount(len(self.columns))
80-
self.setHeaderLabels(list(self.columns.keys()))
81-
self.setHeaderHidden(not show_headers)
80+
self.widget.setColumnCount(len(self.columns))
81+
self.widget.setHeaderLabels(list(self.columns.keys()))
82+
self.widget.setHeaderHidden(not show_headers)
8283
self.stretch = stretch
8384
self._backgrounds = backgrounds
8485

8586
self.tree = tree or Tree()
86-
self.callback = callback
87-
self.itemSelectionChanged.connect(self.on_item_selection_changed)
87+
self.action = action
88+
self.widget.itemSelectionChanged.connect(self.on_item_selection_changed)
8889

8990
def update(self):
90-
self.clear()
91+
"""Update the tree widget display with the current tree data.
92+
93+
This method clears the existing tree widget items and rebuilds the display
94+
based on the current tree structure, applying column mappings and background
95+
colors as configured.
96+
"""
97+
self.widget.clear()
9198
for node in self.tree.traverse("breadthfirst"):
9299
if node.is_root:
93100
continue
94101

95102
strings = [str(c(node)) for _, c in self.columns.items()]
96103

97104
if node.parent.is_root: # type: ignore
98-
node.attributes["widget_item"] = QTreeWidgetItem(self, strings) # type: ignore
105+
node.attributes["widget_item"] = QTreeWidgetItem(self.widget, strings) # type: ignore
99106
else:
100107
node.attributes["widget_item"] = QTreeWidgetItem(
101108
node.parent.attributes["widget_item"],
@@ -109,6 +116,18 @@ def update(self):
109116
node.attributes["widget_item"].setBackground(list(self.columns.keys()).index(col), QColor(*background(node).rgb255))
110117

111118
def tree_from_dict(self, data):
119+
"""Create a tree structure from a dictionary.
120+
121+
Parameters
122+
----------
123+
data : dict
124+
Dictionary containing the hierarchical data to convert to a tree.
125+
126+
Returns
127+
-------
128+
Tree
129+
A new Tree object representing the dictionary structure.
130+
"""
112131
tree = Tree()
113132
root = TreeNode("Root")
114133
tree.add(root)
@@ -133,10 +152,25 @@ def add_children(key, data, parent):
133152
return tree
134153

135154
def update_from_dict(self, data):
155+
"""Update the tree display from a dictionary structure.
156+
157+
This is a convenience method that converts dictionary data to a tree
158+
and updates the display in one step.
159+
160+
Parameters
161+
----------
162+
data : dict
163+
Dictionary containing the hierarchical data to display.
164+
"""
136165
self.tree = self.tree_from_dict(data)
137166
self.update()
138167

139168
def on_item_selection_changed(self):
140-
for item in self.selectedItems():
141-
if self.callback:
142-
self.callback(item.node)
169+
"""Handle tree item selection changes.
170+
171+
This method is called when the selection in the tree widget changes.
172+
It calls the action function (if provided) with the selected node as argument.
173+
"""
174+
for item in self.widget.selectedItems():
175+
if self.action:
176+
self.action(item.node)

0 commit comments

Comments
 (0)