Tree View¶
a collapsible hierarchy you can select from and edit.
▶ Run in browserTags: ui tree hierarchy signals
TreeView draws a hierarchy of TreeItems, one row per visible item, indented
by depth. An item is plain data: TreeItem("World") holds a text, its
children, an expanded flag (True by default) and an optional data payload
for your own objects. parent.add_child(item) hangs one item under another and
returns it, so a branch can be built and kept in one line. Hand the top item to
TreeView(root=...) and every expanded branch below it is shown.
Clicking the arrow in front of a branch collapses or expands it and fires
item_collapsed or item_expanded; clicking anywhere else on a row selects
that item, highlights it and fires item_selected(item). The status line here
listens to that signal, and the view’s selected attribute is the selected
item (or None) whenever you need to ask.
Add Node hangs a new child off the selected item (or the root when nothing is
selected) and expands it so the new row is visible; Remove Selected takes the
selected item out of its parent (the root has none, so it stays). The view does
not watch its items: it flattens them into rows once and reuses that list every
frame, so after editing the graph in code (adding, removing, renaming, or
setting expanded) call the view’s refresh() to put the change on screen.
The panel is anchored to the window with anchor=Anchor.FULL_RECT, so it
follows every resize, and the VBoxContainer inside it stacks the title, the
tree, the status line and the button row, stretching each row to its width
(the buttons inside that row keep their own size). The column’s sizing is
SizingMode.EXPAND, which shares its spare height out by each row’s
stretch_ratio; every row but the tree sets that to 0, so the tree takes the
height the other rows leave.
Source¶
1# /// simvx
2# tags = ["ui", "tree", "hierarchy", "signals"]
3# web = { width = 800, height = 600, responsive = true }
4# ///
5"""Tree View: a collapsible hierarchy you can select from and edit.
6
7`TreeView` draws a hierarchy of `TreeItem`s, one row per visible item, indented
8by depth. An item is plain data: `TreeItem("World")` holds a `text`, its
9`children`, an `expanded` flag (True by default) and an optional `data` payload
10for your own objects. `parent.add_child(item)` hangs one item under another and
11returns it, so a branch can be built and kept in one line. Hand the top item to
12`TreeView(root=...)` and every expanded branch below it is shown.
13
14Clicking the arrow in front of a branch collapses or expands it and fires
15`item_collapsed` or `item_expanded`; clicking anywhere else on a row selects
16that item, highlights it and fires `item_selected(item)`. The status line here
17listens to that signal, and the view's `selected` attribute is the selected
18item (or `None`) whenever you need to ask.
19
20Add Node hangs a new child off the selected item (or the root when nothing is
21selected) and expands it so the new row is visible; Remove Selected takes the
22selected item out of its parent (the root has none, so it stays). The view does
23not watch its items: it flattens them into rows once and reuses that list every
24frame, so after editing the graph in code (adding, removing, renaming, or
25setting `expanded`) call the view's `refresh()` to put the change on screen.
26
27The panel is anchored to the window with `anchor=Anchor.FULL_RECT`, so it
28follows every resize, and the `VBoxContainer` inside it stacks the title, the
29tree, the status line and the button row, stretching each row to its width
30(the buttons inside that row keep their own size). The column's `sizing` is
31`SizingMode.EXPAND`, which shares its spare height out by each row's
32`stretch_ratio`; every row but the tree sets that to 0, so the tree takes the
33height the other rows leave.
34"""
35
36from simvx.core import (
37 Anchor,
38 Button,
39 Colour,
40 HBoxContainer,
41 Label,
42 Node,
43 Panel,
44 SizingMode,
45 TreeItem,
46 TreeView,
47 VBoxContainer,
48)
49from simvx.graphics import App
50
51
52class TreeDemo(Node):
53 def on_ready(self):
54 self.added = 0
55 root = TreeItem("Root")
56 world = root.add_child(TreeItem("World"))
57 for name in ("Player (Node3D)", "Enemy1 (Node3D)", "Enemy2 (Node3D)"):
58 world.add_child(TreeItem(name))
59 ui = root.add_child(TreeItem("UI"))
60 ui.add_child(TreeItem("HUD"))
61 ui.add_child(TreeItem("Menu"))
62 root.add_child(TreeItem("Camera"))
63
64 panel = self.add_child(Panel(anchor=Anchor.FULL_RECT, inset=(30, 20)))
65 panel.bg_colour = Colour.hex("#1A1A2E")
66 column = panel.add_child(VBoxContainer(anchor=Anchor.FULL_RECT, inset=10, separation=10))
67 column.sizing = SizingMode.EXPAND
68
69 title = column.add_child(Label("Scene Tree Inspector", font_size=18, alignment="centre", stretch_ratio=0))
70 title.text_colour = Colour.hex("#E94560")
71 self.view = column.add_child(TreeView(root=root, name="SceneTree"))
72 self.view.select_colour = Colour.hex("#0F3460")
73 self.view.item_selected.connect(self.show_selection)
74 self.status = column.add_child(Label("Selected: (none)", stretch_ratio=0))
75 self.status.text_colour = Colour.CYAN
76
77 buttons = [Button("Add Node", on_press=self.add_node), Button("Remove Selected", on_press=self.remove_selected)]
78 column.add_child(HBoxContainer(children=buttons, separation=10, stretch_ratio=0))
79
80 def show_selection(self, item: TreeItem | None):
81 self.status.text = f"Selected: {item.text if item else '(none)'}"
82
83 def add_node(self):
84 parent = self.view.selected or self.view.root
85 self.added += 1
86 parent.add_child(TreeItem(f"NewNode{self.added}"))
87 parent.expanded = True
88 self.view.refresh()
89
90 def remove_selected(self):
91 item = self.view.selected
92 if item and item.parent: # the root has no parent and stays
93 item.parent.remove_child(item)
94 self.view.selected = None
95 self.show_selection(None)
96 self.view.refresh()
97
98
99if __name__ == "__main__":
100 App(title="Tree View", width=800, height=600).run(TreeDemo())