Skip to content

Commit cb455bc

Browse files
committed
NF: Tutorial for the tab_ui provided.
1 parent dc52229 commit cb455bc

4 files changed

Lines changed: 291 additions & 17 deletions

File tree

docs/examples/_valid_examples.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ files = [
5050
"viz_sliders.py",
5151
"viz_button.py",
5252
"viz_playback_panel.py",
53+
"viz_tab_ui.py",
5354
]
5455

5556
[animation]

docs/examples/viz_tab_ui.py

Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
1+
"""
2+
=====
3+
TabUI
4+
=====
5+
6+
This example shows how to use :class:`fury.ui.TabUI` to organize multiple
7+
2D UI panels inside the same window.
8+
9+
Left click on a tab header to show its content. Left click on the active tab
10+
again to hide that tab's content. Right click on a tab header to collapse the
11+
tab UI. The whole widget can be dragged from the tab header or panel
12+
background.
13+
"""
14+
15+
##############################################################################
16+
# First, a bunch of imports.
17+
18+
from fury.ui import Disk2D, LineSlider2D, Rectangle2D, TabUI, TextBlock2D
19+
from fury.window import Scene, ShowManager
20+
21+
22+
##############################################################################
23+
# Creating a Scene.
24+
25+
scene = Scene()
26+
27+
28+
##############################################################################
29+
# Create a TabUI with three tabs. The first tab is visible on startup.
30+
31+
tab_ui = TabUI(
32+
position=(80, 80),
33+
size=(420, 320),
34+
nb_tabs=3,
35+
startup_tab_id=0,
36+
draggable=True,
37+
active_color=(0.95, 0.95, 1.0),
38+
inactive_color=(0.45, 0.48, 0.56),
39+
)
40+
41+
tab_ui.tabs[0].title = "Shapes"
42+
tab_ui.tabs[1].title = "Slider"
43+
tab_ui.tabs[2].title = "Notes"
44+
45+
for tab in tab_ui.tabs:
46+
tab.title_font_size = 16
47+
48+
49+
##############################################################################
50+
# Add content to the first tab.
51+
52+
rect = Rectangle2D(size=(90, 70), color=(0.9, 0.25, 0.35))
53+
disk = Disk2D(outer_radius=45, color=(0.2, 0.75, 0.95))
54+
caption = TextBlock2D(
55+
text="The first tab contains simple 2D shapes.",
56+
size=(340, 40),
57+
color=(1, 1, 1),
58+
font_size=16,
59+
)
60+
61+
tab_ui.add_element(0, rect, (45, 75))
62+
tab_ui.add_element(0, disk, (285, 110), anchor="center")
63+
tab_ui.add_element(0, caption, (40, 205))
64+
65+
66+
##############################################################################
67+
# Add content to the second tab.
68+
69+
slider_label = TextBlock2D(
70+
text="A LineSlider2D can live inside a tab content panel.",
71+
size=(350, 40),
72+
color=(1, 1, 1),
73+
font_size=16,
74+
)
75+
slider = LineSlider2D(
76+
initial_value=35,
77+
min_value=0,
78+
max_value=100,
79+
length=300,
80+
orientation="horizontal",
81+
text_template="Value: {value:.0f}",
82+
)
83+
84+
tab_ui.add_element(1, slider_label, (35, 65))
85+
tab_ui.add_element(1, slider, (55, 150))
86+
87+
88+
##############################################################################
89+
# Add content to the third tab.
90+
91+
notes = TextBlock2D(
92+
text=(
93+
"TabUI groups several UI panels behind tab headers.\n"
94+
"Use TabUI.add_element(tab_index, element, coords)\n"
95+
"to place UI components in a specific tab."
96+
),
97+
size=(360, 150),
98+
color=(1, 1, 1),
99+
font_size=15,
100+
)
101+
102+
tab_ui.add_element(2, notes, (35, 70))
103+
104+
105+
##############################################################################
106+
# Add a status text outside the TabUI. The callbacks update this text when the
107+
# active tab changes or the TabUI is collapsed.
108+
109+
status = TextBlock2D(
110+
text="Active tab: Shapes",
111+
position=(80, 430),
112+
size=(420, 35),
113+
color=(1, 1, 1),
114+
font_size=16,
115+
)
116+
117+
118+
def update_status(ui):
119+
"""Update status text after selecting a tab."""
120+
if ui.active_tab_idx is None:
121+
status.message = "TabUI collapsed"
122+
else:
123+
status.message = f"Active tab: {ui.tabs[ui.active_tab_idx].title}"
124+
125+
126+
def collapse_status(ui):
127+
"""Update status text after collapsing the TabUI."""
128+
status.message = "TabUI collapsed"
129+
130+
131+
tab_ui.on_change = update_status
132+
tab_ui.on_collapse = collapse_status
133+
134+
135+
##############################################################################
136+
# TabUI can also use an accordion layout. In this mode each tab title spans the
137+
# widget width. The active tab expands below its title while the other titles
138+
# remain visible below the expanded content.
139+
140+
accordion_tab_ui = TabUI(
141+
position=(560, 80),
142+
size=(340, 320),
143+
nb_tabs=3,
144+
startup_tab_id=0,
145+
tab_bar_pos="accordion",
146+
draggable=True,
147+
active_color=(0.95, 0.95, 1.0),
148+
inactive_color=(0.45, 0.48, 0.56),
149+
)
150+
151+
accordion_tab_ui.tabs[0].title = "Info"
152+
accordion_tab_ui.tabs[1].title = "Color"
153+
accordion_tab_ui.tabs[2].title = "Help"
154+
155+
for tab in accordion_tab_ui.tabs:
156+
tab.title_font_size = 15
157+
158+
accordion_info = TextBlock2D(
159+
text="The active tab expands below its title.",
160+
size=(280, 80),
161+
color=(1, 1, 1),
162+
font_size=15,
163+
)
164+
accordion_square = Rectangle2D(size=(90, 90), color=(0.2, 0.8, 0.45))
165+
accordion_help = TextBlock2D(
166+
text="Other tab titles stay visible\nbelow the expanded content.",
167+
size=(260, 90),
168+
color=(1, 1, 1),
169+
font_size=15,
170+
)
171+
172+
accordion_tab_ui.add_element(0, accordion_info, (30, 45))
173+
accordion_tab_ui.add_element(1, accordion_square, (125, 55))
174+
accordion_tab_ui.add_element(2, accordion_help, (40, 45))
175+
176+
accordion_status = TextBlock2D(
177+
text="Accordion active tab: Info",
178+
position=(560, 430),
179+
size=(340, 35),
180+
color=(1, 1, 1),
181+
font_size=16,
182+
)
183+
184+
185+
def update_accordion_status(ui):
186+
"""Update status text after selecting an accordion tab."""
187+
if ui.active_tab_idx is None:
188+
accordion_status.message = "Accordion TabUI collapsed"
189+
else:
190+
title = ui.tabs[ui.active_tab_idx].title
191+
accordion_status.message = f"Accordion active tab: {title}"
192+
193+
194+
def collapse_accordion_status(ui):
195+
"""Update status text after collapsing the accordion TabUI."""
196+
accordion_status.message = "Accordion TabUI collapsed"
197+
198+
199+
accordion_tab_ui.on_change = update_accordion_status
200+
accordion_tab_ui.on_collapse = collapse_accordion_status
201+
202+
203+
##############################################################################
204+
# The tab bar can also be placed at the bottom of the widget.
205+
206+
bottom_tab_ui = TabUI(
207+
position=(80, 500),
208+
size=(420, 300),
209+
nb_tabs=2,
210+
startup_tab_id=0,
211+
tab_bar_pos="bottom",
212+
draggable=True,
213+
active_color=(0.95, 0.95, 1.0),
214+
inactive_color=(0.45, 0.48, 0.56),
215+
)
216+
217+
bottom_tab_ui.tabs[0].title = "Bottom Tab 1"
218+
bottom_tab_ui.tabs[1].title = "Bottom Tab 2"
219+
220+
for tab in bottom_tab_ui.tabs:
221+
tab.title_font_size = 15
222+
223+
bottom_text = TextBlock2D(
224+
text="This TabUI uses tab_bar_pos='bottom'.",
225+
size=(330, 45),
226+
color=(1, 1, 1),
227+
font_size=15,
228+
)
229+
bottom_rect = Rectangle2D(size=(120, 80), color=(0.9, 0.65, 0.2))
230+
231+
bottom_tab_ui.add_element(0, bottom_text, (40, 55))
232+
bottom_tab_ui.add_element(1, bottom_rect, (145, 65))
233+
234+
bottom_status = TextBlock2D(
235+
text="Bottom active tab: Bottom Tab 1",
236+
position=(80, 820),
237+
size=(420, 35),
238+
color=(1, 1, 1),
239+
font_size=16,
240+
)
241+
242+
243+
def update_bottom_status(ui):
244+
"""Update status text after selecting a bottom tab."""
245+
if ui.active_tab_idx is None:
246+
bottom_status.message = "Bottom TabUI collapsed"
247+
else:
248+
title = ui.tabs[ui.active_tab_idx].title
249+
bottom_status.message = f"Bottom active tab: {title}"
250+
251+
252+
def collapse_bottom_status(ui):
253+
"""Update status text after collapsing the bottom TabUI."""
254+
bottom_status.message = "Bottom TabUI collapsed"
255+
256+
257+
bottom_tab_ui.on_change = update_bottom_status
258+
bottom_tab_ui.on_collapse = collapse_bottom_status
259+
260+
261+
##############################################################################
262+
# Now that all elements have been initialized, add them to the scene.
263+
264+
scene.add(tab_ui)
265+
scene.add(status)
266+
scene.add(accordion_tab_ui)
267+
scene.add(accordion_status)
268+
scene.add(bottom_tab_ui)
269+
scene.add(bottom_status)
270+
271+
272+
##############################################################################
273+
# Starting the ShowManager.
274+
275+
current_size = (980, 900)
276+
show_manager = ShowManager(
277+
scene=scene,
278+
size=current_size,
279+
title="FURY TabUI Example",
280+
)
281+
show_manager.start()

fury/ui/containers.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -974,19 +974,17 @@ def _setup_tab_callbacks(self, idx, tab_panel):
974974
Tab panel receiving selection, collapse, and optional drag
975975
callbacks.
976976
"""
977-
tab_panel.text_block.on_right_mouse_button_clicked = (
978-
lambda event: self.collapse_tab_ui()
977+
tab_panel.text_block.on_right_mouse_button_clicked = lambda event: (
978+
self.collapse_tab_ui()
979979
)
980-
tab_panel.panel.background.on_right_mouse_button_clicked = (
981-
lambda event: self.collapse_tab_ui()
980+
tab_panel.panel.background.on_right_mouse_button_clicked = lambda event: (
981+
self.collapse_tab_ui()
982982
)
983983

984984
if self.draggable:
985985
for element in [tab_panel.panel.background, tab_panel.text_block]:
986-
element.on_left_mouse_button_pressed = (
987-
lambda event, tab_idx=idx: self.left_button_pressed(
988-
event, tab_idx
989-
)
986+
element.on_left_mouse_button_pressed = lambda event, tab_idx=idx: (
987+
self.left_button_pressed(event, tab_idx)
990988
)
991989
element.on_left_mouse_button_dragged = self.left_button_dragged
992990
element.on_left_mouse_button_released = lambda event: None

fury/ui/tests/test_containers.py

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -240,9 +240,7 @@ def test_tab_panel2d_properties_and_content():
240240

241241
def test_tab_ui_initialization_and_selection():
242242
"""Test TabUI initialization, element visibility, and tab selection."""
243-
tab_ui = ui.TabUI(
244-
position=(50, 50), size=(300, 300), nb_tabs=3, startup_tab_id=1
245-
)
243+
tab_ui = ui.TabUI(position=(50, 50), size=(300, 300), nb_tabs=3, startup_tab_id=1)
246244

247245
npt.assert_equal(tab_ui.size, (300, 300))
248246
npt.assert_equal(tab_ui.nb_tabs, 3)
@@ -298,13 +296,9 @@ def test_tab_ui_layout_and_invalid_index():
298296
npt.assert_equal(tab_ui_top.tabs[0].size, (100, 30))
299297
npt.assert_equal(tab_ui_top.tabs[0].content_panel.size, (300, 270))
300298
npt.assert_array_equal(tab_ui_top.tabs[0].get_position(), (50, 50))
301-
npt.assert_array_equal(
302-
tab_ui_top.tabs[0].content_panel.get_position(), (50, 80)
303-
)
299+
npt.assert_array_equal(tab_ui_top.tabs[0].content_panel.get_position(), (50, 80))
304300

305-
npt.assert_array_equal(
306-
tab_ui_bottom.tabs[0].content_panel.get_position(), (50, 50)
307-
)
301+
npt.assert_array_equal(tab_ui_bottom.tabs[0].content_panel.get_position(), (50, 50))
308302
npt.assert_array_equal(tab_ui_bottom.tabs[0].get_position(), (50, 320))
309303

310304
npt.assert_equal(tab_ui_accordion.tabs[0].size, (300, 30))

0 commit comments

Comments
 (0)