88from compas .datastructures import Tree
99from 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