From 25809b862f3dcd742a2afbf28349c1da0db41d0e Mon Sep 17 00:00:00 2001 From: Guillaume Favelier Date: Fri, 4 Feb 2022 17:20:55 +0100 Subject: [PATCH 1/8] update --- mne/gui/_coreg.py | 44 +++++++++++++++++++++++++---------- mne/gui/tests/test_gui_api.py | 3 ++- mne/viz/backends/_abstract.py | 2 +- mne/viz/backends/_notebook.py | 28 +++++++++++++++++----- mne/viz/backends/_qt.py | 2 +- 5 files changed, 58 insertions(+), 21 deletions(-) diff --git a/mne/gui/_coreg.py b/mne/gui/_coreg.py index 76d24035428..7e091d2866f 100644 --- a/mne/gui/_coreg.py +++ b/mne/gui/_coreg.py @@ -1290,10 +1290,17 @@ def _update_fiducials_label(self): ) def _configure_dock(self): + if self._renderer._kind == 'notebook': + collapse = True # collapsible and collapsed + else: + collapse = None # not collapsible self._renderer._dock_initialize( name="Input", area="left", max_width="350px" ) - mri_subject_layout = self._renderer._dock_add_group_box("MRI Subject") + mri_subject_layout = self._renderer._dock_add_group_box( + name="MRI Subject", + collapse=collapse, + ) self._widgets["subjects_dir"] = self._renderer._dock_add_file_button( name="subjects_dir", desc="Load", @@ -1316,7 +1323,8 @@ def _configure_dock(self): ) mri_fiducials_layout = self._renderer._dock_add_group_box( - "MRI Fiducials" + name="MRI Fiducials", + collapse=collapse, ) # Add MRI fiducials I/O widgets self._widgets['mri_fiducials_label'] = self._renderer._dock_add_label( @@ -1387,8 +1395,10 @@ def _configure_dock(self): self._renderer._layout_add_widget( mri_fiducials_layout, fiducial_coords_layout) - dig_source_layout = \ - self._renderer._dock_add_group_box("Info source with digitization") + dig_source_layout = self._renderer._dock_add_group_box( + name="Info source with digitization", + collapse=collapse, + ) self._widgets["info_file"] = self._renderer._dock_add_file_button( name="info_file", desc="Load", @@ -1431,8 +1441,10 @@ def _configure_dock(self): ) self._renderer._layout_add_widget(dig_source_layout, omit_hsp_layout) - view_options_layout = \ - self._renderer._dock_add_group_box("View Options") + view_options_layout = self._renderer._dock_add_group_box( + name="View Options", + collapse=collapse, + ) self._widgets["helmet"] = self._renderer._dock_add_check_box( name="Show MEG helmet", value=self._helmet, @@ -1461,8 +1473,10 @@ def _configure_dock(self): self._renderer._dock_initialize( name="Parameters", area="right", max_width="350px" ) - mri_scaling_layout = \ - self._renderer._dock_add_group_box(name="MRI Scaling") + mri_scaling_layout = self._renderer._dock_add_group_box( + name="MRI Scaling", + collapse=collapse, + ) self._widgets["scaling_mode"] = self._renderer._dock_add_combo_box( name="Scaling Mode", value=self._defaults["scale_mode"], @@ -1529,7 +1543,9 @@ def _configure_dock(self): self._renderer._layout_add_widget( mri_scaling_layout, subject_to_layout) param_layout = self._renderer._dock_add_group_box( - "Translation (t) and Rotation (r)") + name="Translation (t) and Rotation (r)", + collapse=collapse, + ) for coord in coords: coord_layout = self._renderer._dock_add_layout(vertical=False) for mode, mode_name in (("t", "Translation"), ("r", "Rotation")): @@ -1571,7 +1587,9 @@ def _configure_dock(self): ) self._renderer._layout_add_widget(param_layout, fit_layout) trans_layout = self._renderer._dock_add_group_box( - "HEAD <> MRI Transform") + name="HEAD <> MRI Transform", + collapse=collapse, + ) save_trans_layout = self._renderer._dock_add_layout(vertical=False) self._widgets["save_trans"] = self._renderer._dock_add_file_button( name="save_trans", @@ -1602,8 +1620,10 @@ def _configure_dock(self): ) self._renderer._layout_add_widget(trans_layout, save_trans_layout) - fitting_options_layout = \ - self._renderer._dock_add_group_box("Fitting Options") + fitting_options_layout = self._renderer._dock_add_group_box( + name="Fitting Options", + collapse=collapse, + ) self._widgets["fit_label"] = self._renderer._dock_add_label( value="", layout=fitting_options_layout, diff --git a/mne/gui/tests/test_gui_api.py b/mne/gui/tests/test_gui_api.py index 9c920a6ac52..03d3af6baab 100644 --- a/mne/gui/tests/test_gui_api.py +++ b/mne/gui/tests/test_gui_api.py @@ -184,7 +184,8 @@ def _check_widget_trigger(widget, mock, before, after, call_count=True, renderer._dock_initialize(name='', area='right') renderer._dock_named_layout(name='') - renderer._dock_add_group_box(name='') + for collapse in (None, True, False): + renderer._dock_add_group_box(name='', collapse=collapse) renderer._dock_add_stretch() renderer._dock_add_layout() renderer._dock_finalize() diff --git a/mne/viz/backends/_abstract.py b/mne/viz/backends/_abstract.py index 71c7d26e65b..e55b7be060a 100644 --- a/mne/viz/backends/_abstract.py +++ b/mne/viz/backends/_abstract.py @@ -571,7 +571,7 @@ def _dock_add_radio_buttons(self, value, rng, callback, *, vertical=True, pass @abstractmethod - def _dock_add_group_box(self, name, *, layout=None): + def _dock_add_group_box(self, name, *, collapse=None, layout=None): pass @abstractmethod diff --git a/mne/viz/backends/_notebook.py b/mne/viz/backends/_notebook.py index 92e1d5608d9..7b7c3e7156c 100644 --- a/mne/viz/backends/_notebook.py +++ b/mne/viz/backends/_notebook.py @@ -9,7 +9,7 @@ from IPython.display import display from ipywidgets import (Button, Dropdown, FloatSlider, BoundedFloatText, HBox, IntSlider, IntText, Text, VBox, IntProgress, Play, - Checkbox, RadioButtons, jsdlink) + Checkbox, RadioButtons, Accordion, jsdlink) from ._abstract import (_AbstractDock, _AbstractToolBar, _AbstractMenuBar, _AbstractStatusBar, _AbstractLayout, _AbstractWidget, @@ -27,9 +27,14 @@ def _layout_add_widget(self, layout, widget, stretch=0): widget.layout.margin = "2px 0px 2px 0px" if not isinstance(widget, Play): widget.layout.min_width = "0px" - children = list(layout.children) - children.append(widget) - layout.children = tuple(children) + if isinstance(layout, Accordion): + children = list(layout._vbox.children) + children.append(widget) + layout._vbox.children = tuple(children) + else: + children = list(layout.children) + children.append(widget) + layout.children = tuple(children) # Fix columns if self._layout_max_width is not None and isinstance(widget, HBox): children = widget.children @@ -165,9 +170,20 @@ def _dock_add_radio_buttons(self, value, rng, callback, *, vertical=True, self._layout_add_widget(layout, widget) return _IpyWidgetList(widget) - def _dock_add_group_box(self, name, *, layout=None): + def _dock_add_group_box(self, name, *, collapse=None, layout=None): layout = self._dock_layout if layout is None else layout - hlayout = VBox() + if collapse is None: + hlayout = VBox([Text(name)]) + else: + assert isinstance(collapse, bool) + vbox = VBox() + hlayout = Accordion([vbox]) + hlayout.set_title(0, name) + if collapse: + hlayout.selected_index = None + else: + hlayout.selected_index = 0 + hlayout._vbox = vbox self._layout_add_widget(layout, hlayout) return hlayout diff --git a/mne/viz/backends/_qt.py b/mne/viz/backends/_qt.py index ace163d3414..b5332f9a21f 100644 --- a/mne/viz/backends/_qt.py +++ b/mne/viz/backends/_qt.py @@ -205,7 +205,7 @@ def func(button): self._layout_add_widget(layout, group_layout) return _QtWidgetList(group) - def _dock_add_group_box(self, name, *, layout=None): + def _dock_add_group_box(self, name, *, collapse=None, layout=None): layout = self._dock_layout if layout is None else layout hlayout = QVBoxLayout() widget = QGroupBox(name) From 6f44268ffd5a84547ed5a830dce869264decc1cb Mon Sep 17 00:00:00 2001 From: Guillaume Favelier Date: Mon, 7 Feb 2022 10:44:17 +0100 Subject: [PATCH 2/8] object_fit --- mne/viz/backends/_notebook.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mne/viz/backends/_notebook.py b/mne/viz/backends/_notebook.py index 966c60b4650..b24b505543f 100644 --- a/mne/viz/backends/_notebook.py +++ b/mne/viz/backends/_notebook.py @@ -540,6 +540,7 @@ def show(self): viewer = self.plotter.show( jupyter_backend="ipyvtklink", return_viewer=True) viewer.layout.width = None # unlock the fixed layout + viewer.layout.object_fit = 'contain' rendering_row = list() if self._docks is not None and "left" in self._docks: rendering_row.append(self._docks["left"][0]) From e111b563bfd5f7134bcb4b222e03463193c66c3a Mon Sep 17 00:00:00 2001 From: Guillaume Favelier Date: Mon, 7 Feb 2022 10:45:51 +0100 Subject: [PATCH 3/8] comment --- mne/viz/backends/_notebook.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mne/viz/backends/_notebook.py b/mne/viz/backends/_notebook.py index b24b505543f..50fd7c101aa 100644 --- a/mne/viz/backends/_notebook.py +++ b/mne/viz/backends/_notebook.py @@ -540,7 +540,7 @@ def show(self): viewer = self.plotter.show( jupyter_backend="ipyvtklink", return_viewer=True) viewer.layout.width = None # unlock the fixed layout - viewer.layout.object_fit = 'contain' + viewer.layout.object_fit = 'contain' # respect the aspect ratio rendering_row = list() if self._docks is not None and "left" in self._docks: rendering_row.append(self._docks["left"][0]) From 9c34ba25df3ad6563d2abc6a02b5324082093144 Mon Sep 17 00:00:00 2001 From: Guillaume Favelier Date: Mon, 7 Feb 2022 17:26:22 +0100 Subject: [PATCH 4/8] refactor --- mne/viz/backends/_notebook.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/mne/viz/backends/_notebook.py b/mne/viz/backends/_notebook.py index 50fd7c101aa..713194781d9 100644 --- a/mne/viz/backends/_notebook.py +++ b/mne/viz/backends/_notebook.py @@ -28,13 +28,12 @@ def _layout_add_widget(self, layout, widget, stretch=0): if not isinstance(widget, Play): widget.layout.min_width = "0px" if isinstance(layout, Accordion): - children = list(layout._vbox.children) - children.append(widget) - layout._vbox.children = tuple(children) + box = layout.children[0] else: - children = list(layout.children) - children.append(widget) - layout.children = tuple(children) + box = layout + children = list(box.children) + children.append(widget) + box.children = tuple(children) # Fix columns if self._layout_max_width is not None and isinstance(widget, HBox): children = widget.children @@ -183,7 +182,6 @@ def _dock_add_group_box(self, name, *, collapse=None, layout=None): hlayout.selected_index = None else: hlayout.selected_index = 0 - hlayout._vbox = vbox self._layout_add_widget(layout, hlayout) return hlayout From 7020ccb3af6b61cb0efc6b2a669a5b5e27d306f8 Mon Sep 17 00:00:00 2001 From: Guillaume Favelier Date: Mon, 7 Feb 2022 17:28:19 +0100 Subject: [PATCH 5/8] fix --- mne/viz/backends/_notebook.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mne/viz/backends/_notebook.py b/mne/viz/backends/_notebook.py index 713194781d9..831b4ca290a 100644 --- a/mne/viz/backends/_notebook.py +++ b/mne/viz/backends/_notebook.py @@ -516,7 +516,7 @@ def _update(self): if self.figure.display is not None: self.figure.display.update_canvas() - def _create_default_tool_bar(self): + def _display_default_tool_bar(self): self._tool_bar_load_icons() self._tool_bar_initialize() self._tool_bar_add_file_button( @@ -524,6 +524,7 @@ def _create_default_tool_bar(self): desc="Take a screenshot", func=self.screenshot, ) + display(self._tool_bar) def show(self): # menu bar @@ -533,7 +534,7 @@ def show(self): if self._tool_bar is not None: display(self._tool_bar) else: - self._create_default_tool_bar() + self._display_default_tool_bar() # viewer viewer = self.plotter.show( jupyter_backend="ipyvtklink", return_viewer=True) From 6e1887290978edbc1b5f1b2c594f2f636dc9b853 Mon Sep 17 00:00:00 2001 From: Guillaume Favelier Date: Mon, 7 Feb 2022 17:32:04 +0100 Subject: [PATCH 6/8] enhance --- mne/viz/backends/_notebook.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mne/viz/backends/_notebook.py b/mne/viz/backends/_notebook.py index 831b4ca290a..c61d240ef9f 100644 --- a/mne/viz/backends/_notebook.py +++ b/mne/viz/backends/_notebook.py @@ -172,7 +172,7 @@ def _dock_add_radio_buttons(self, value, rng, callback, *, vertical=True, def _dock_add_group_box(self, name, *, collapse=None, layout=None): layout = self._dock_layout if layout is None else layout if collapse is None: - hlayout = VBox([Text(name)]) + hlayout = VBox([HTML("" + name + "")]) else: assert isinstance(collapse, bool) vbox = VBox() From 5014f41601b75148779881888354d8676a2497e5 Mon Sep 17 00:00:00 2001 From: Guillaume Favelier Date: Tue, 8 Feb 2022 15:48:55 +0100 Subject: [PATCH 7/8] hack --- mne/gui/_coreg.py | 10 +++++++++- mne/viz/backends/_notebook.py | 22 ++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/mne/gui/_coreg.py b/mne/gui/_coreg.py index 7e091d2866f..3b499aff4c7 100644 --- a/mne/gui/_coreg.py +++ b/mne/gui/_coreg.py @@ -726,7 +726,15 @@ def _on_button_press(self, vtk_picker, event): def _on_button_release(self, vtk_picker, event): if self._mouse_no_mvt > 0: - x, y = vtk_picker.GetEventPosition() + if self._renderer._kind == "notebook": + w, h = self._renderer._window_get_size() + event = self._renderer.figure.display.logged_events[-1] + x = event["offsetX"] + offset_y = round(event["clientY"] - event["boundingRectTop"]) + scale_y = h / self._renderer._canvas_height + y = h - round(offset_y * scale_y) + else: + x, y = vtk_picker.GetEventPosition() # XXX: internal plotter/renderer should not be exposed plotter = self._renderer.figure.plotter picked_renderer = self._renderer.figure.plotter.renderer diff --git a/mne/viz/backends/_notebook.py b/mne/viz/backends/_notebook.py index c61d240ef9f..bf221e33c96 100644 --- a/mne/viz/backends/_notebook.py +++ b/mne/viz/backends/_notebook.py @@ -10,6 +10,7 @@ from ipywidgets import (Button, Dropdown, FloatSlider, BoundedFloatText, HBox, IntSlider, IntText, Text, VBox, IntProgress, Play, Checkbox, RadioButtons, HTML, Accordion, jsdlink) +from ipyevents import Event from ._abstract import (_AbstractDock, _AbstractToolBar, _AbstractMenuBar, _AbstractStatusBar, _AbstractLayout, _AbstractWidget, @@ -509,6 +510,8 @@ def __init__(self, *args, **kwargs): self._menu_bar = None self._tool_bar = None self._status_bar = None + self._event_manager = Event() + self._canvas_height = None kwargs["notebook"] = True super().__init__(*args, **kwargs) @@ -526,6 +529,23 @@ def _display_default_tool_bar(self): ) display(self._tool_bar) + def _dom_event_callback(self, event): + if "boundingRectHeight" in event and self._canvas_height is None: + self._canvas_height = event["boundingRectHeight"] + + def _configure_event_manager(self, source): + allowed_events = [ + "mouseenter", + "mouseleave", + "mousedown", + "mouseup", + "mousemove", + ] + self._event_manager.watched_events = allowed_events + self._event_manager.source = source + self._event_manager.on_dom_event( + self._dom_event_callback) + def show(self): # menu bar if self._menu_bar is not None: @@ -540,6 +560,8 @@ def show(self): jupyter_backend="ipyvtklink", return_viewer=True) viewer.layout.width = None # unlock the fixed layout viewer.layout.object_fit = 'contain' # respect the aspect ratio + viewer.layout.object_position = 'top' + self._configure_event_manager(viewer) rendering_row = list() if self._docks is not None and "left" in self._docks: rendering_row.append(self._docks["left"][0]) From 204cfe6f0501957fc2a873f0c61c16b46a0febf4 Mon Sep 17 00:00:00 2001 From: Guillaume Favelier Date: Wed, 9 Feb 2022 15:54:01 +0100 Subject: [PATCH 8/8] revert --- mne/gui/_coreg.py | 10 +--------- mne/viz/backends/_notebook.py | 23 ----------------------- 2 files changed, 1 insertion(+), 32 deletions(-) diff --git a/mne/gui/_coreg.py b/mne/gui/_coreg.py index 3b499aff4c7..7e091d2866f 100644 --- a/mne/gui/_coreg.py +++ b/mne/gui/_coreg.py @@ -726,15 +726,7 @@ def _on_button_press(self, vtk_picker, event): def _on_button_release(self, vtk_picker, event): if self._mouse_no_mvt > 0: - if self._renderer._kind == "notebook": - w, h = self._renderer._window_get_size() - event = self._renderer.figure.display.logged_events[-1] - x = event["offsetX"] - offset_y = round(event["clientY"] - event["boundingRectTop"]) - scale_y = h / self._renderer._canvas_height - y = h - round(offset_y * scale_y) - else: - x, y = vtk_picker.GetEventPosition() + x, y = vtk_picker.GetEventPosition() # XXX: internal plotter/renderer should not be exposed plotter = self._renderer.figure.plotter picked_renderer = self._renderer.figure.plotter.renderer diff --git a/mne/viz/backends/_notebook.py b/mne/viz/backends/_notebook.py index bf221e33c96..cb1d39f457c 100644 --- a/mne/viz/backends/_notebook.py +++ b/mne/viz/backends/_notebook.py @@ -10,7 +10,6 @@ from ipywidgets import (Button, Dropdown, FloatSlider, BoundedFloatText, HBox, IntSlider, IntText, Text, VBox, IntProgress, Play, Checkbox, RadioButtons, HTML, Accordion, jsdlink) -from ipyevents import Event from ._abstract import (_AbstractDock, _AbstractToolBar, _AbstractMenuBar, _AbstractStatusBar, _AbstractLayout, _AbstractWidget, @@ -510,8 +509,6 @@ def __init__(self, *args, **kwargs): self._menu_bar = None self._tool_bar = None self._status_bar = None - self._event_manager = Event() - self._canvas_height = None kwargs["notebook"] = True super().__init__(*args, **kwargs) @@ -529,23 +526,6 @@ def _display_default_tool_bar(self): ) display(self._tool_bar) - def _dom_event_callback(self, event): - if "boundingRectHeight" in event and self._canvas_height is None: - self._canvas_height = event["boundingRectHeight"] - - def _configure_event_manager(self, source): - allowed_events = [ - "mouseenter", - "mouseleave", - "mousedown", - "mouseup", - "mousemove", - ] - self._event_manager.watched_events = allowed_events - self._event_manager.source = source - self._event_manager.on_dom_event( - self._dom_event_callback) - def show(self): # menu bar if self._menu_bar is not None: @@ -559,9 +539,6 @@ def show(self): viewer = self.plotter.show( jupyter_backend="ipyvtklink", return_viewer=True) viewer.layout.width = None # unlock the fixed layout - viewer.layout.object_fit = 'contain' # respect the aspect ratio - viewer.layout.object_position = 'top' - self._configure_event_manager(viewer) rendering_row = list() if self._docks is not None and "left" in self._docks: rendering_row.append(self._docks["left"][0])