1+ from typing import Callable
2+
13from nicegui import ui
24from nicegui .elements .button import Button
35from nicegui .events import ValueChangeEventArguments
@@ -140,6 +142,68 @@ def _on_reset_button_clicked(self) -> None:
140142 self ._dialog .close ()
141143
142144
145+ class FilterClassificationForm :
146+ """Dialog to select the classes whose tracks are shown.
147+
148+ The checkboxes show the current class filter selection. Without a selection,
149+ every class counts as selected, as in the customtkinter GUI.
150+ """
151+
152+ def __init__ (self , viewmodel : ViewModel ) -> None :
153+ self ._viewmodel = viewmodel
154+ self ._classes : list [str ] = []
155+ self ._selected_classes : set [str ] = set ()
156+
157+ def open (self ) -> None :
158+ self ._classes = self ._viewmodel .get_classes ()
159+ self ._selected_classes = set (self ._current_selection ())
160+ with ui .dialog () as self ._dialog , ui .card ():
161+ for classification in self ._classes :
162+ self ._build_checkbox (classification )
163+ with ui .row ():
164+ self ._build_buttons ()
165+ self ._dialog .open ()
166+
167+ def _current_selection (self ) -> list [str ]:
168+ selection = self ._viewmodel .get_class_filter_selection ()
169+ return self ._classes if selection is None else selection
170+
171+ def _build_checkbox (self , classification : str ) -> None :
172+ FormFieldCheckbox (
173+ label_text = classification ,
174+ initial_value = classification in self ._selected_classes ,
175+ on_value_change = self ._create_class_toggle (classification ),
176+ ).build ()
177+
178+ def _create_class_toggle (
179+ self , classification : str
180+ ) -> Callable [[ValueChangeEventArguments ], None ]:
181+ def toggle (event : ValueChangeEventArguments ) -> None :
182+ self ._select_class (classification , event .value )
183+
184+ return toggle
185+
186+ def _build_buttons (self ) -> None :
187+ ui .button ("Apply" , on_click = self ._on_apply_button_clicked )
188+
189+ def _select_class (self , classification : str , selected : bool ) -> None :
190+ if selected :
191+ self ._selected_classes .add (classification )
192+ else :
193+ self ._selected_classes .discard (classification )
194+
195+ def _on_apply_button_clicked (self ) -> None :
196+ self ._viewmodel .apply_filter_tracks_by_class (
197+ [
198+ classification
199+ for classification in self ._classes
200+ if classification in self ._selected_classes
201+ ]
202+ )
203+ logger ().info ("Filter tracks by classification applied" )
204+ self ._dialog .close ()
205+
206+
143207class VisualizationFiltersForm (AbstractFrameFilter , ButtonForm ):
144208
145209 def __init__ (
@@ -151,7 +215,6 @@ def __init__(
151215 self ._viewmodel = view_model
152216 self ._filter_by_date : ui .checkbox
153217 self ._filter_by_class : ui .checkbox
154- self ._filter_by_class_list : list = []
155218 self ._button_previous_second : ui .button | None = None
156219 self ._button_previous_frame : ui .button | None = None
157220 self ._button_previous_event : ui .button | None = None
@@ -180,6 +243,7 @@ def __init__(
180243 self ._filter_by_date_popup = FilterDateRangeForm (
181244 self ._resource_manager , self ._viewmodel
182245 )
246+ self ._filter_by_class_popup = FilterClassificationForm (self ._viewmodel )
183247 self ._input_seconds = FormFieldInteger (
184248 label_text = self ._resource_manager .get (
185249 VisualizationFiltersKeys .LABEL_SECONDS
@@ -320,33 +384,7 @@ def on_input_change(self, _: ValueChangeEventArguments) -> None:
320384 )
321385
322386 def open_classification_dialog (self ) -> None :
323- self ._filter_by_class_list = self ._viewmodel .get_classes ()
324- with ui .dialog () as dialog , ui .card ():
325- for classification_class in self ._filter_by_class_list :
326- FormFieldCheckbox (
327- label_text = classification_class ,
328- initial_value = classification_class in self ._selected_classes (),
329- on_value_change = lambda e : self .remove_or_add_class (
330- classification_class
331- ),
332- )
333- ui .button ("Apply" , on_click = lambda e : self .apply_new_classification (dialog ))
334- dialog .open ()
335-
336- def _selected_classes (self ) -> list [str ]:
337- selected_classes = self ._viewmodel .get_class_filter_selection ()
338- return selected_classes if selected_classes else self ._viewmodel .get_classes ()
339-
340- def apply_new_classification (self , dialog : ui .dialog ) -> None :
341- self ._viewmodel .apply_filter_tracks_by_class (self ._filter_by_class_list )
342- self ._filter_by_class_list = []
343- dialog .close ()
344-
345- def remove_or_add_class (self , class_name : str ) -> None :
346- if class_name in self ._filter_by_class_list :
347- self ._filter_by_class_list .remove (class_name )
348- else :
349- self ._filter_by_class_list .append (class_name )
387+ self ._filter_by_class_popup .open ()
350388
351389 def open_range_dialog (self ) -> None :
352390 self ._filter_by_date_popup .open ()
0 commit comments