Skip to content

Commit 04cdca0

Browse files
chore: take reviews into account
1 parent 32c2163 commit 04cdca0

11 files changed

Lines changed: 173 additions & 128 deletions

examples/viewer_lib/logic/medical_viewer_logic.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from trame_slicer.core import LayoutManager, SlicerApp
66
from trame_slicer.rca_view import register_rca_factories
77

8-
from ..ui import MedicalViewerUI, StateId
8+
from ..ui import MedicalViewerUI, SegmentEditorUI, StateId
99
from .base_logic import BaseLogic
1010
from .layout_button_logic import LayoutButtonLogic
1111
from .load_volume_logic import LoadVolumeLogic
@@ -47,7 +47,7 @@ def layout_manager(self) -> LayoutManager:
4747
return self._layout_button_logic.layout_manager
4848

4949
def set_ui(self, ui: MedicalViewerUI):
50-
self._segment_editor_logic.set_ui(ui.segment_editor_ui)
50+
self._segment_editor_logic.set_ui(ui.tool_registry[SegmentEditorUI])
5151
self._layout_button_logic.set_ui(ui.layout_button)
5252
self._markups_logic.set_ui(ui.markups_button)
5353
self._load_files_logic.set_ui(ui.load_volume_buttons)

examples/viewer_lib/logic/segmentation/segment_editor_logic.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,9 +140,9 @@ def _on_segment_display_changed(self, display_state: SegmentDisplayState) -> Non
140140
if not self._segmentation_display:
141141
return
142142

143-
self._segmentation_display.set_opacity_2d(display_state.opacity_2d)
144-
self._segmentation_display.set_opacity_3d(display_state.opacity_3d)
145-
self._segmentation_display.set_opacity_mode(display_state.display_mode)
143+
self._segmentation_display.set_opacity_2d(display_state.opacity_2d.value)
144+
self._segmentation_display.set_opacity_3d(display_state.opacity_3d.value)
145+
self._segmentation_display.set_opacity_mode(display_state.opacity_mode)
146146
self.segmentation_editor.set_surface_representation_enabled(display_state.show_3d)
147147

148148
def _on_volume_changed(self, **_kwargs) -> None:

examples/viewer_lib/logic/segmentation_app_logic.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from trame_slicer.core import LayoutManager, SlicerApp
44
from trame_slicer.rca_view import register_rca_factories
55

6-
from ..ui import SegmentationAppUI, StateId
6+
from ..ui import SegmentationAppUI, SegmentEditorUI, StateId
77
from .base_logic import BaseLogic
88
from .layout_button_logic import LayoutButtonLogic
99
from .load_volume_logic import LoadVolumeLogic
@@ -34,6 +34,6 @@ def layout_manager(self) -> LayoutManager:
3434
return self._layout_button_logic.layout_manager
3535

3636
def set_ui(self, ui: SegmentationAppUI):
37-
self._segment_editor_logic.set_ui(ui.segment_editor_ui)
37+
self._segment_editor_logic.set_ui(ui.tool_registry[SegmentEditorUI])
3838
self._layout_button_logic.set_ui(ui.layout_button)
3939
self._load_files_logic.set_ui(ui.load_volume_items_buttons)

examples/viewer_lib/ui/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
SegmentDisplayState,
1818
SegmentDisplayUI,
1919
SegmentEditorState,
20+
SegmentEditorToolbarUI,
2021
SegmentEditorUI,
2122
SegmentEditState,
2223
SegmentEditUI,
@@ -61,6 +62,7 @@
6162
"SegmentEditState",
6263
"SegmentEditUI",
6364
"SegmentEditorState",
65+
"SegmentEditorToolbarUI",
6466
"SegmentEditorUI",
6567
"SegmentList",
6668
"SegmentListState",

examples/viewer_lib/ui/medical_viewer_ui.py

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,39 @@
22

33
from trame_slicer.core import LayoutManager, SlicerApp
44

5+
from .control_button import ControlButton
56
from .flex_container import FlexContainer
67
from .layout_button import LayoutButton
78
from .load_volume_ui import LoadVolumeDiv
89
from .markups_button import MarkupsButton
910
from .mpr_interaction_button import MprInteractionButton
10-
from .segmentation import SegmentEditorUI
11+
from .segmentation import SegmentEditorToolbarUI, SegmentEditorUI
1112
from .slab_button import SlabButton
1213
from .viewer_layout import ViewerLayout
1314
from .volume_property_button import VolumePropertyButton
1415

1516

1617
class MedicalViewerUI:
1718
def __init__(self, server: Server, slicer_app: SlicerApp, layout_manager: LayoutManager):
19+
self.tool_registry = {}
1820
with ViewerLayout(server) as self.layout:
19-
self.segment_editor_ui = SegmentEditorUI()
21+
with self.layout.drawer:
22+
self._register_tool_ui(SegmentEditorUI)
23+
2024
with self.layout.toolbar, FlexContainer(fill_height=True):
2125
self.load_volume_buttons = LoadVolumeDiv()
2226
self.volume_property_button = VolumePropertyButton(server=server, slicer_app=slicer_app)
2327
self.layout_button = LayoutButton()
2428
self.markups_button = MarkupsButton()
25-
self.segment_editor_ui.build_activator(click=self.activate_tool)
29+
self._create_tool_button(
30+
icon="mdi-brush",
31+
name="segmentation panel",
32+
tool_ui_type=SegmentEditorUI,
33+
)
2634
self.slab_button = SlabButton()
2735
self.mpr_interaction_button = MprInteractionButton()
2836

29-
self.segment_editor_ui.build_toolbar_ui()
30-
31-
with self.layout.drawer:
32-
self.segment_editor_ui.build_drawer_ui()
37+
self._register_toolbar_ui(SegmentEditorToolbarUI, SegmentEditorUI)
3338

3439
with self.layout.content, FlexContainer(row=True, fill_height=True):
3540
layout_manager.initialize_layout_grid(self.layout)
@@ -42,10 +47,31 @@ def data(self):
4247
def name(self):
4348
return self.layout.typed_state.name
4449

45-
def activate_tool(self, tool_name):
46-
if self.data.is_drawer_visible and self.data.active_tool == tool_name:
47-
self.data.is_drawer_visible = False
48-
self.data.active_tool = None
49-
else:
50-
self.data.active_tool = tool_name
51-
self.data.is_drawer_visible = True
50+
def _is_tool_active(self, tool_ui_type: type):
51+
return f"{self.name.active_tool} === '{tool_ui_type.__name__}'"
52+
53+
def _is_tool_drawer_visible(self, tool_ui_type: type):
54+
return f"{self._is_tool_active(tool_ui_type)} && {self.name.is_drawer_visible}"
55+
56+
def _register_tool_ui(self, tool_ui_type: type):
57+
tool_instance = tool_ui_type(v_if=(self._is_tool_active(tool_ui_type),))
58+
self.tool_registry[tool_ui_type] = tool_instance
59+
60+
def _register_toolbar_ui(self, toolbar_ui_type: type, tool_ui_type: type):
61+
toolbar_ui_type(self.tool_registry[tool_ui_type], v_if=(self._is_tool_active(tool_ui_type),))
62+
63+
def _create_tool_button(self, name: str, icon: str | tuple, tool_ui_type: type):
64+
async def change_drawer_ui():
65+
if self.data.is_drawer_visible and self.data.active_tool == tool_ui_type.__name__:
66+
self.data.active_tool = None
67+
self.data.is_drawer_visible = False
68+
else:
69+
self.data.active_tool = tool_ui_type.__name__
70+
self.data.is_drawer_visible = True
71+
72+
ControlButton(
73+
icon=icon,
74+
name="{{ " + f"{self._is_tool_drawer_visible(tool_ui_type)} ? 'Close {name}' : 'Open {name}'" + " }}",
75+
click=change_drawer_ui,
76+
active=(self._is_tool_active(tool_ui_type),),
77+
)

examples/viewer_lib/ui/segmentation/__init__.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@
55
SegmentEditState,
66
SegmentEditUI,
77
)
8-
from .segment_editor_ui import SegmentEditorState, SegmentEditorUI
8+
from .segment_editor_ui import (
9+
SegmentEditorState,
10+
SegmentEditorToolbarUI,
11+
SegmentEditorUI,
12+
)
913
from .segment_list import SegmentList, SegmentListState
1014
from .segment_state import SegmentState
1115
from .threshold_effect_ui import ThresholdEffectUI, ThresholdState
@@ -21,6 +25,7 @@
2125
"SegmentEditState",
2226
"SegmentEditUI",
2327
"SegmentEditorState",
28+
"SegmentEditorToolbarUI",
2429
"SegmentEditorUI",
2530
"SegmentList",
2631
"SegmentListState",

examples/viewer_lib/ui/segmentation/segment_display_ui.py

Lines changed: 46 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,25 @@
44
from trame_vuetify.widgets.vuetify3 import (
55
Template,
66
VBtn,
7-
VBtnToggle,
87
VCard,
98
VCardItem,
109
VCardText,
11-
VSlider,
1210
)
1311

1412
from trame_slicer.segmentation import SegmentationOpacityEnum
1513

1614
from ..control_button import ControlButton
1715
from ..flex_container import FlexContainer
16+
from ..slider import Slider, SliderState
1817
from ..text_components import Text
1918

2019

2120
@dataclass
2221
class SegmentDisplayState:
23-
display_mode: SegmentationOpacityEnum = SegmentationOpacityEnum.BOTH
24-
opacity_2d: float = 0.5
25-
opacity_3d: float = 1.0
22+
opacity_mode: SegmentationOpacityEnum = SegmentationOpacityEnum.BOTH
23+
opacity_2d: SliderState = field(default_factory=SliderState)
24+
opacity_3d: SliderState = field(default_factory=SliderState)
2625
show_3d: bool = False
27-
display_options: list[str] = field(default_factory=lambda: ["filled", "outlined"])
2826
is_extended: bool = False
2927

3028

@@ -33,7 +31,9 @@ def __init__(self, typed_state: TypedState[SegmentDisplayState], **kwargs):
3331
super().__init__(**kwargs)
3432

3533
self._typed_state = typed_state
36-
self._typed_state.bind_changes({self._typed_state.name.display_options: self._on_display_options_changed})
34+
self._typed_state.data.opacity_2d.step = 0.01
35+
self._typed_state.data.opacity_3d.step = 0.01
36+
self._typed_state.data.opacity_3d.value = 1
3737

3838
with self:
3939
with VCardItem():
@@ -51,25 +51,32 @@ def __init__(self, typed_state: TypedState[SegmentDisplayState], **kwargs):
5151
justify="space-between",
5252
row=True,
5353
):
54-
with VBtnToggle(
55-
v_model=(self._typed_state.name.display_options,),
56-
classes="align-center",
57-
mandatory=True,
58-
multiple=True,
54+
VBtn(
55+
active=(
56+
"["
57+
f"{self._typed_state.encode(SegmentationOpacityEnum.FILL)}, {self._typed_state.encode(SegmentationOpacityEnum.BOTH)}"
58+
f"].includes({self._typed_state.name.opacity_mode})",
59+
),
60+
click=lambda: self._on_display_options_changed(SegmentationOpacityEnum.FILL),
61+
height=35,
62+
prepend_icon="mdi-circle",
5963
rounded=0,
60-
):
61-
VBtn(
62-
prepend_icon="mdi-circle",
63-
text="Filled",
64-
value="filled",
65-
height=35,
66-
)
67-
VBtn(
68-
prepend_icon="mdi-circle-outline",
69-
text="Outlined",
70-
value="outlined",
71-
height=35,
72-
)
64+
text="Filled",
65+
variant="text",
66+
)
67+
VBtn(
68+
active=(
69+
"["
70+
f"{self._typed_state.encode(SegmentationOpacityEnum.OUTLINE)}, {self._typed_state.encode(SegmentationOpacityEnum.BOTH)}"
71+
f"].includes({self._typed_state.name.opacity_mode})",
72+
),
73+
click=lambda: self._on_display_options_changed(SegmentationOpacityEnum.OUTLINE),
74+
height=35,
75+
prepend_icon="mdi-circle-outline",
76+
rounded=0,
77+
text="Outlined",
78+
variant="text",
79+
)
7380

7481
ControlButton(
7582
icon="mdi-video-3d",
@@ -79,29 +86,23 @@ def __init__(self, typed_state: TypedState[SegmentDisplayState], **kwargs):
7986
)
8087

8188
Text("Opacity", subtitle=True)
82-
VSlider(
83-
v_model=(self._typed_state.name.opacity_2d,),
84-
hide_details=True,
89+
Slider(
90+
typed_state=self._typed_state.get_sub_state(self._typed_state.name.opacity_2d),
8591
prepend_icon="mdi-video-2d",
86-
min=0.0,
87-
max=1.0,
88-
step=0.01,
8992
)
90-
VSlider(
91-
v_model=(self._typed_state.name.opacity_3d,),
92-
disabled=(f"!{self._typed_state.name.show_3d}",),
93-
hide_details=True,
93+
Slider(
94+
typed_state=self._typed_state.get_sub_state(self._typed_state.name.opacity_3d),
9495
prepend_icon="mdi-video-3d",
95-
min=0.0,
96-
max=1.0,
97-
step=0.01,
9896
)
9997

100-
def _on_display_options_changed(self, display_options):
101-
if "outlined" in display_options:
102-
if "filled" in display_options:
103-
self._typed_state.data.display_mode = SegmentationOpacityEnum.BOTH
104-
else:
105-
self._typed_state.data.display_mode = SegmentationOpacityEnum.OUTLINE
98+
def _on_display_options_changed(self, opacity_mode):
99+
if self._typed_state.data.opacity_mode == opacity_mode:
100+
return
101+
if self._typed_state.data.opacity_mode == SegmentationOpacityEnum.BOTH:
102+
self._typed_state.data.opacity_mode = (
103+
SegmentationOpacityEnum.FILL
104+
if opacity_mode == SegmentationOpacityEnum.OUTLINE
105+
else SegmentationOpacityEnum.OUTLINE
106+
)
106107
else:
107-
self._typed_state.data.display_mode = SegmentationOpacityEnum.FILL
108+
self._typed_state.data.opacity_mode = SegmentationOpacityEnum.BOTH

0 commit comments

Comments
 (0)