From 1eadbbdab61e8a167df69cca2aaff3e3b9c2486d Mon Sep 17 00:00:00 2001 From: Mohit Kumhar Date: Mon, 24 Feb 2025 23:36:00 +0530 Subject: [PATCH 01/11] enhancing help menu --- mne_qt_browser/_pg_figure.py | 96 ++++++++++++++++++++++++++++++++++-- 1 file changed, 92 insertions(+), 4 deletions(-) diff --git a/mne_qt_browser/_pg_figure.py b/mne_qt_browser/_pg_figure.py index 2a43a4cf..fd75c2c2 100644 --- a/mne_qt_browser/_pg_figure.py +++ b/mne_qt_browser/_pg_figure.py @@ -4017,10 +4017,6 @@ def __init__(self, **kwargs): asettings.triggered.connect(self._toggle_settings_fig) self.mne.toolbar.addAction(asettings) - ahelp = QAction(self._qicon("help"), "Help", parent=self) - ahelp.triggered.connect(self._toggle_help_fig) - self.mne.toolbar.addAction(ahelp) - # Set Start-Range (after all necessary elements are initialized) self.mne.plt.setXRange( self.mne.t_start, self.mne.t_start + self.mne.duration, padding=0 @@ -4234,6 +4230,98 @@ def __init__(self, **kwargs): # disable histogram of epoch PTP amplitude del self.mne.keyboard_shortcuts["h"] + + help_menu = QMenu("&Help", self) + keyboard_action = help_menu.addAction("Keyboard Shortcuts") + keyboard_action.triggered.connect(self._show_keyboard_shortcuts) + mouse_action = help_menu.addAction("Mouse Shortcuts") + mouse_action.triggered.connect(self._show_mouse_shortcuts) + + help_btn = QToolButton() + help_btn.setIcon(self._qicon("help")) + help_btn.setText("Help") + help_btn.setMenu(self._create_help_menu()) + help_btn.setPopupMode(QToolButton.InstantPopup) + self.mne.toolbar.addWidget(help_btn) + + + + + def _show_keyboard_shortcuts(self): + """Display keyboard shortcuts in a popup.""" + msg = QMessageBox(self) + msg.setWindowTitle("Keyboard Shortcuts") + + text = "Keyboard Controls:" + + msg.setText(text) + msg.exec_() + + def _show_mouse_shortcuts(self): + """Display mouse shortcuts in a popup.""" + msg = QMessageBox(self) + msg.setWindowTitle("Mouse Shortcuts") + + text = """Mouse Controls: + """ + + msg.setText(text) + msg.exec_() + + def _create_help_menu(self): + """Create help menu dynamically from HelpDialog content.""" + help_menu = QMenu("&Help", self) + + # Ensure we correctly access `keyboard_shortcuts` + keyboard_shortcuts = getattr(self.mne, "keyboard_shortcuts", None) + + if keyboard_shortcuts: + keyboard_menu = QMenu("Keyboard Shortcuts", self) + + for key, spec in keyboard_shortcuts.items(): + key_name = spec.get('alias', key) + descriptions = spec.get('description', []) + modifiers = spec.get('modifier', [None] * len(descriptions)) + + for mod, desc in zip(modifiers, descriptions): + key_combination = f"{mod} + {key_name}" if mod else key_name + shortcut_text = f"{desc}\t{key_combination}" # Align key to the right + action = QAction(shortcut_text, self) + keyboard_menu.addAction(action) + + help_menu.addMenu(keyboard_menu) + + # Mouse Controls + mouse_menu = QMenu("Mouse Controls", self) + mouse_interactions = [ + ("Left-click component name", "Mark/unmark bad channel"), + ("Left-click component data", "Mark/unmark bad segment"), + ("Left-click-and-drag on plot", "Add annotation (annotation mode)"), + ("Right-click plot", "Context menu"), + ("Scroll", "Navigate time/channels"), + ("Shift+Click", "Add/remove channels from annotations"), + ] + + for action_text, description in mouse_interactions: + shortcut_text = f"{description}\t{action_text}" + action = QAction(shortcut_text, self) + mouse_menu.addAction(action) + + help_menu.addMenu(mouse_menu) + + return help_menu + + def _hidpi_mkPen(self, *args, **kwargs): kwargs["width"] = self._pixel_ratio * kwargs.get("width", 1.0) return mkPen(*args, **kwargs) From 5f66f8eae9202a24c2fa28e306d6567cff08b498 Mon Sep 17 00:00:00 2001 From: Mohit Kumhar Date: Tue, 25 Feb 2025 23:40:57 +0530 Subject: [PATCH 02/11] enhancing help menu --- mne_qt_browser/_pg_figure.py | 96 ++++++++++++++++++++++++++++++++++-- 1 file changed, 92 insertions(+), 4 deletions(-) diff --git a/mne_qt_browser/_pg_figure.py b/mne_qt_browser/_pg_figure.py index 2a43a4cf..fd75c2c2 100644 --- a/mne_qt_browser/_pg_figure.py +++ b/mne_qt_browser/_pg_figure.py @@ -4017,10 +4017,6 @@ def __init__(self, **kwargs): asettings.triggered.connect(self._toggle_settings_fig) self.mne.toolbar.addAction(asettings) - ahelp = QAction(self._qicon("help"), "Help", parent=self) - ahelp.triggered.connect(self._toggle_help_fig) - self.mne.toolbar.addAction(ahelp) - # Set Start-Range (after all necessary elements are initialized) self.mne.plt.setXRange( self.mne.t_start, self.mne.t_start + self.mne.duration, padding=0 @@ -4234,6 +4230,98 @@ def __init__(self, **kwargs): # disable histogram of epoch PTP amplitude del self.mne.keyboard_shortcuts["h"] + + help_menu = QMenu("&Help", self) + keyboard_action = help_menu.addAction("Keyboard Shortcuts") + keyboard_action.triggered.connect(self._show_keyboard_shortcuts) + mouse_action = help_menu.addAction("Mouse Shortcuts") + mouse_action.triggered.connect(self._show_mouse_shortcuts) + + help_btn = QToolButton() + help_btn.setIcon(self._qicon("help")) + help_btn.setText("Help") + help_btn.setMenu(self._create_help_menu()) + help_btn.setPopupMode(QToolButton.InstantPopup) + self.mne.toolbar.addWidget(help_btn) + + + + + def _show_keyboard_shortcuts(self): + """Display keyboard shortcuts in a popup.""" + msg = QMessageBox(self) + msg.setWindowTitle("Keyboard Shortcuts") + + text = "Keyboard Controls:" + + msg.setText(text) + msg.exec_() + + def _show_mouse_shortcuts(self): + """Display mouse shortcuts in a popup.""" + msg = QMessageBox(self) + msg.setWindowTitle("Mouse Shortcuts") + + text = """Mouse Controls: + """ + + msg.setText(text) + msg.exec_() + + def _create_help_menu(self): + """Create help menu dynamically from HelpDialog content.""" + help_menu = QMenu("&Help", self) + + # Ensure we correctly access `keyboard_shortcuts` + keyboard_shortcuts = getattr(self.mne, "keyboard_shortcuts", None) + + if keyboard_shortcuts: + keyboard_menu = QMenu("Keyboard Shortcuts", self) + + for key, spec in keyboard_shortcuts.items(): + key_name = spec.get('alias', key) + descriptions = spec.get('description', []) + modifiers = spec.get('modifier', [None] * len(descriptions)) + + for mod, desc in zip(modifiers, descriptions): + key_combination = f"{mod} + {key_name}" if mod else key_name + shortcut_text = f"{desc}\t{key_combination}" # Align key to the right + action = QAction(shortcut_text, self) + keyboard_menu.addAction(action) + + help_menu.addMenu(keyboard_menu) + + # Mouse Controls + mouse_menu = QMenu("Mouse Controls", self) + mouse_interactions = [ + ("Left-click component name", "Mark/unmark bad channel"), + ("Left-click component data", "Mark/unmark bad segment"), + ("Left-click-and-drag on plot", "Add annotation (annotation mode)"), + ("Right-click plot", "Context menu"), + ("Scroll", "Navigate time/channels"), + ("Shift+Click", "Add/remove channels from annotations"), + ] + + for action_text, description in mouse_interactions: + shortcut_text = f"{description}\t{action_text}" + action = QAction(shortcut_text, self) + mouse_menu.addAction(action) + + help_menu.addMenu(mouse_menu) + + return help_menu + + def _hidpi_mkPen(self, *args, **kwargs): kwargs["width"] = self._pixel_ratio * kwargs.get("width", 1.0) return mkPen(*args, **kwargs) From 54729bfd7bcc6597c49085ae936bcbd1dded5238 Mon Sep 17 00:00:00 2001 From: Mohit Kumhar Date: Tue, 25 Feb 2025 23:54:01 +0530 Subject: [PATCH 03/11] enhancing help menu --- mne_qt_browser/_pg_figure.py | 163 ++++++++++++++++++----------------- 1 file changed, 82 insertions(+), 81 deletions(-) diff --git a/mne_qt_browser/_pg_figure.py b/mne_qt_browser/_pg_figure.py index fd75c2c2..52021f0b 100644 --- a/mne_qt_browser/_pg_figure.py +++ b/mne_qt_browser/_pg_figure.py @@ -2227,87 +2227,88 @@ def _update_spinbox_values(self, *args, **kwargs): _calc_chan_type_to_physical(self, ch_type, units=current_units) ) - -class HelpDialog(_BaseDialog): - """Shows all keyboard-shortcuts.""" - - def __init__(self, main, **kwargs): - super().__init__(main, title="Help", **kwargs) - - # Show all keyboard-shortcuts in a Scroll-Area - layout = QVBoxLayout() - keyboard_label = QLabel("Keyboard Shortcuts") - keyboard_label.setFont(_q_font(16, bold=True)) - layout.addWidget(keyboard_label) - - scroll_area = QScrollArea() - scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) - scroll_area.setSizePolicy( - QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding - ) - scroll_widget = QWidget() - form_layout = QFormLayout() - for key in main.mne.keyboard_shortcuts: - key_dict = main.mne.keyboard_shortcuts[key] - if "description" in key_dict: - if "alias" in key_dict: - key = key_dict["alias"] - for idx, key_des in enumerate(key_dict["description"]): - key_name = key - if "modifier" in key_dict: - mod = key_dict["modifier"][idx] - if mod is not None: - key_name = mod + " + " + key_name - form_layout.addRow(key_name, QLabel(key_des)) - scroll_widget.setLayout(form_layout) - scroll_area.setWidget(scroll_widget) - layout.addWidget(scroll_area) - - # Additional help for mouse interaction - inst = self.mne.instance_type - is_raw = inst == "raw" - is_epo = inst == "epochs" - is_ica = inst == "ica" - ch_cmp = "component" if is_ica else "channel" - ch_epo = "epoch" if is_epo else "channel" - ica_bad = "Mark/unmark component for exclusion" - lclick_data = ica_bad if is_ica else f"Mark/unmark bad {ch_epo}" - lclick_name = ica_bad if is_ica else "Mark/unmark bad channel" - ldrag = "add annotation (in annotation mode)" if is_raw else None - rclick_name = dict( - ica="Show diagnostics for component", - epochs="Show imageplot for channel", - raw="Show channel location", - )[inst] - mouse_help = [ - (f"Left-click {ch_cmp} name", lclick_name), - (f"Left-click {ch_cmp} data", lclick_data), - ("Left-click-and-drag on plot", ldrag), - ("Left-click on plot background", "Place vertical guide"), - ("Right-click on plot background", "Clear vertical guide"), - ("Right-click on channel name", rclick_name), - ] - - mouse_label = QLabel("Mouse Interaction") - mouse_label.setFont(_q_font(16, bold=True)) - layout.addWidget(mouse_label) - mouse_widget = QWidget() - mouse_layout = QFormLayout() - for interaction, description in mouse_help: - if description is not None: - mouse_layout.addRow(f"{interaction}:", QLabel(description)) - mouse_widget.setLayout(mouse_layout) - layout.addWidget(mouse_widget) - - self.setLayout(layout) - self.show() - - # Set minimum width to avoid horizontal scrolling - scroll_area.setMinimumWidth( - scroll_widget.minimumSizeHint().width() - + scroll_area.verticalScrollBar().width() - ) - self.update() +# Replaced this class with a function declared in MNEQtBrowser + +# class HelpDialog(_BaseDialog): +# """Shows all keyboard-shortcuts.""" + +# def __init__(self, main, **kwargs): +# super().__init__(main, title="Help", **kwargs) + +# # Show all keyboard-shortcuts in a Scroll-Area +# layout = QVBoxLayout() +# keyboard_label = QLabel("Keyboard Shortcuts") +# keyboard_label.setFont(_q_font(16, bold=True)) +# layout.addWidget(keyboard_label) + +# scroll_area = QScrollArea() +# scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) +# scroll_area.setSizePolicy( +# QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding +# ) +# scroll_widget = QWidget() +# form_layout = QFormLayout() +# for key in main.mne.keyboard_shortcuts: +# key_dict = main.mne.keyboard_shortcuts[key] +# if "description" in key_dict: +# if "alias" in key_dict: +# key = key_dict["alias"] +# for idx, key_des in enumerate(key_dict["description"]): +# key_name = key +# if "modifier" in key_dict: +# mod = key_dict["modifier"][idx] +# if mod is not None: +# key_name = mod + " + " + key_name +# form_layout.addRow(key_name, QLabel(key_des)) +# scroll_widget.setLayout(form_layout) +# scroll_area.setWidget(scroll_widget) +# layout.addWidget(scroll_area) + +# # Additional help for mouse interaction +# inst = self.mne.instance_type +# is_raw = inst == "raw" +# is_epo = inst == "epochs" +# is_ica = inst == "ica" +# ch_cmp = "component" if is_ica else "channel" +# ch_epo = "epoch" if is_epo else "channel" +# ica_bad = "Mark/unmark component for exclusion" +# lclick_data = ica_bad if is_ica else f"Mark/unmark bad {ch_epo}" +# lclick_name = ica_bad if is_ica else "Mark/unmark bad channel" +# ldrag = "add annotation (in annotation mode)" if is_raw else None +# rclick_name = dict( +# ica="Show diagnostics for component", +# epochs="Show imageplot for channel", +# raw="Show channel location", +# )[inst] +# mouse_help = [ +# (f"Left-click {ch_cmp} name", lclick_name), +# (f"Left-click {ch_cmp} data", lclick_data), +# ("Left-click-and-drag on plot", ldrag), +# ("Left-click on plot background", "Place vertical guide"), +# ("Right-click on plot background", "Clear vertical guide"), +# ("Right-click on channel name", rclick_name), +# ] + +# mouse_label = QLabel("Mouse Interaction") +# mouse_label.setFont(_q_font(16, bold=True)) +# layout.addWidget(mouse_label) +# mouse_widget = QWidget() +# mouse_layout = QFormLayout() +# for interaction, description in mouse_help: +# if description is not None: +# mouse_layout.addRow(f"{interaction}:", QLabel(description)) +# mouse_widget.setLayout(mouse_layout) +# layout.addWidget(mouse_widget) + +# self.setLayout(layout) +# self.show() + +# # Set minimum width to avoid horizontal scrolling +# scroll_area.setMinimumWidth( +# scroll_widget.minimumSizeHint().width() +# + scroll_area.verticalScrollBar().width() +# ) +# self.update() class ProjDialog(_BaseDialog): From d7f8c5487eac75e25482a3d030410a16dee816c7 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 25 Feb 2025 18:53:16 +0000 Subject: [PATCH 04/11] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mne_qt_browser/_pg_figure.py | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/mne_qt_browser/_pg_figure.py b/mne_qt_browser/_pg_figure.py index 52021f0b..fe086e40 100644 --- a/mne_qt_browser/_pg_figure.py +++ b/mne_qt_browser/_pg_figure.py @@ -2227,6 +2227,7 @@ def _update_spinbox_values(self, *args, **kwargs): _calc_chan_type_to_physical(self, ch_type, units=current_units) ) + # Replaced this class with a function declared in MNEQtBrowser # class HelpDialog(_BaseDialog): @@ -4231,11 +4232,10 @@ def __init__(self, **kwargs): # disable histogram of epoch PTP amplitude del self.mne.keyboard_shortcuts["h"] - help_menu = QMenu("&Help", self) keyboard_action = help_menu.addAction("Keyboard Shortcuts") keyboard_action.triggered.connect(self._show_keyboard_shortcuts) - mouse_action = help_menu.addAction("Mouse Shortcuts") + mouse_action = help_menu.addAction("Mouse Shortcuts") mouse_action.triggered.connect(self._show_mouse_shortcuts) help_btn = QToolButton() @@ -4245,20 +4245,17 @@ def __init__(self, **kwargs): help_btn.setPopupMode(QToolButton.InstantPopup) self.mne.toolbar.addWidget(help_btn) - - - def _show_keyboard_shortcuts(self): """Display keyboard shortcuts in a popup.""" msg = QMessageBox(self) msg.setWindowTitle("Keyboard Shortcuts") - + text = "Keyboard Controls:
    " for key, spec in self.mne.keyboard_shortcuts.items(): - if 'description' in spec: + if "description" in spec: text += f"
  • {', '.join(spec['description'])}
  • " text += "
" - + msg.setText(text) msg.exec_() @@ -4266,7 +4263,7 @@ def _show_mouse_shortcuts(self): """Display mouse shortcuts in a popup.""" msg = QMessageBox(self) msg.setWindowTitle("Mouse Shortcuts") - + text = """Mouse Controls:
  • Left-click channel name: Mark/unmark bad channel
  • @@ -4275,28 +4272,30 @@ def _show_mouse_shortcuts(self):
  • Drag: Create annotation (annotation mode)
  • Scroll: Navigate time/channels
""" - + msg.setText(text) msg.exec_() def _create_help_menu(self): """Create help menu dynamically from HelpDialog content.""" help_menu = QMenu("&Help", self) - + # Ensure we correctly access `keyboard_shortcuts` keyboard_shortcuts = getattr(self.mne, "keyboard_shortcuts", None) - + if keyboard_shortcuts: keyboard_menu = QMenu("Keyboard Shortcuts", self) for key, spec in keyboard_shortcuts.items(): - key_name = spec.get('alias', key) - descriptions = spec.get('description', []) - modifiers = spec.get('modifier', [None] * len(descriptions)) + key_name = spec.get("alias", key) + descriptions = spec.get("description", []) + modifiers = spec.get("modifier", [None] * len(descriptions)) for mod, desc in zip(modifiers, descriptions): key_combination = f"{mod} + {key_name}" if mod else key_name - shortcut_text = f"{desc}\t{key_combination}" # Align key to the right + shortcut_text = ( + f"{desc}\t{key_combination}" # Align key to the right + ) action = QAction(shortcut_text, self) keyboard_menu.addAction(action) @@ -4322,7 +4321,6 @@ def _create_help_menu(self): return help_menu - def _hidpi_mkPen(self, *args, **kwargs): kwargs["width"] = self._pixel_ratio * kwargs.get("width", 1.0) return mkPen(*args, **kwargs) From 4b896a823bbcc2b9e525ebcec208c0fc3b3c8aba Mon Sep 17 00:00:00 2001 From: Mohit Kumhar Date: Mon, 3 Mar 2025 14:42:07 +0530 Subject: [PATCH 05/11] removed unwanted comments --- mne_qt_browser/_pg_figure.py | 85 ------------------------------------ 1 file changed, 85 deletions(-) diff --git a/mne_qt_browser/_pg_figure.py b/mne_qt_browser/_pg_figure.py index fe086e40..fdc29a3f 100644 --- a/mne_qt_browser/_pg_figure.py +++ b/mne_qt_browser/_pg_figure.py @@ -2227,91 +2227,6 @@ def _update_spinbox_values(self, *args, **kwargs): _calc_chan_type_to_physical(self, ch_type, units=current_units) ) - -# Replaced this class with a function declared in MNEQtBrowser - -# class HelpDialog(_BaseDialog): -# """Shows all keyboard-shortcuts.""" - -# def __init__(self, main, **kwargs): -# super().__init__(main, title="Help", **kwargs) - -# # Show all keyboard-shortcuts in a Scroll-Area -# layout = QVBoxLayout() -# keyboard_label = QLabel("Keyboard Shortcuts") -# keyboard_label.setFont(_q_font(16, bold=True)) -# layout.addWidget(keyboard_label) - -# scroll_area = QScrollArea() -# scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) -# scroll_area.setSizePolicy( -# QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding -# ) -# scroll_widget = QWidget() -# form_layout = QFormLayout() -# for key in main.mne.keyboard_shortcuts: -# key_dict = main.mne.keyboard_shortcuts[key] -# if "description" in key_dict: -# if "alias" in key_dict: -# key = key_dict["alias"] -# for idx, key_des in enumerate(key_dict["description"]): -# key_name = key -# if "modifier" in key_dict: -# mod = key_dict["modifier"][idx] -# if mod is not None: -# key_name = mod + " + " + key_name -# form_layout.addRow(key_name, QLabel(key_des)) -# scroll_widget.setLayout(form_layout) -# scroll_area.setWidget(scroll_widget) -# layout.addWidget(scroll_area) - -# # Additional help for mouse interaction -# inst = self.mne.instance_type -# is_raw = inst == "raw" -# is_epo = inst == "epochs" -# is_ica = inst == "ica" -# ch_cmp = "component" if is_ica else "channel" -# ch_epo = "epoch" if is_epo else "channel" -# ica_bad = "Mark/unmark component for exclusion" -# lclick_data = ica_bad if is_ica else f"Mark/unmark bad {ch_epo}" -# lclick_name = ica_bad if is_ica else "Mark/unmark bad channel" -# ldrag = "add annotation (in annotation mode)" if is_raw else None -# rclick_name = dict( -# ica="Show diagnostics for component", -# epochs="Show imageplot for channel", -# raw="Show channel location", -# )[inst] -# mouse_help = [ -# (f"Left-click {ch_cmp} name", lclick_name), -# (f"Left-click {ch_cmp} data", lclick_data), -# ("Left-click-and-drag on plot", ldrag), -# ("Left-click on plot background", "Place vertical guide"), -# ("Right-click on plot background", "Clear vertical guide"), -# ("Right-click on channel name", rclick_name), -# ] - -# mouse_label = QLabel("Mouse Interaction") -# mouse_label.setFont(_q_font(16, bold=True)) -# layout.addWidget(mouse_label) -# mouse_widget = QWidget() -# mouse_layout = QFormLayout() -# for interaction, description in mouse_help: -# if description is not None: -# mouse_layout.addRow(f"{interaction}:", QLabel(description)) -# mouse_widget.setLayout(mouse_layout) -# layout.addWidget(mouse_widget) - -# self.setLayout(layout) -# self.show() - -# # Set minimum width to avoid horizontal scrolling -# scroll_area.setMinimumWidth( -# scroll_widget.minimumSizeHint().width() -# + scroll_area.verticalScrollBar().width() -# ) -# self.update() - - class ProjDialog(_BaseDialog): """A dialog to toggle projections.""" From dc861d733666938e0bda24274a9b7428f860791f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 3 Mar 2025 09:12:48 +0000 Subject: [PATCH 06/11] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mne_qt_browser/_pg_figure.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mne_qt_browser/_pg_figure.py b/mne_qt_browser/_pg_figure.py index fdc29a3f..fe7d8239 100644 --- a/mne_qt_browser/_pg_figure.py +++ b/mne_qt_browser/_pg_figure.py @@ -2227,6 +2227,7 @@ def _update_spinbox_values(self, *args, **kwargs): _calc_chan_type_to_physical(self, ch_type, units=current_units) ) + class ProjDialog(_BaseDialog): """A dialog to toggle projections.""" From c6746b9f18bae04765cd734d752f6d79d11a4c17 Mon Sep 17 00:00:00 2001 From: Mohit Kumhar Date: Mon, 3 Mar 2025 23:34:36 +0530 Subject: [PATCH 07/11] Remove unused function --- mne_qt_browser/_pg_figure.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/mne_qt_browser/_pg_figure.py b/mne_qt_browser/_pg_figure.py index fdc29a3f..2b9662a8 100644 --- a/mne_qt_browser/_pg_figure.py +++ b/mne_qt_browser/_pg_figure.py @@ -2227,6 +2227,7 @@ def _update_spinbox_values(self, *args, **kwargs): _calc_chan_type_to_physical(self, ch_type, units=current_units) ) + class ProjDialog(_BaseDialog): """A dialog to toggle projections.""" @@ -5137,13 +5138,6 @@ def _toggle_settings_fig(self): self.mne.fig_settings.close() self.mne.fig_settings = None - def _toggle_help_fig(self): - if self.mne.fig_help is None: - HelpDialog(self, name="fig_help") - else: - self.mne.fig_help.close() - self.mne.fig_help = None - def _set_butterfly(self, butterfly): self.mne.butterfly = butterfly self._update_picks() From 3b5ce0c57297d4d8084e03359bc65604677461b6 Mon Sep 17 00:00:00 2001 From: Mohit Kumhar Date: Tue, 4 Mar 2025 17:06:20 +0530 Subject: [PATCH 08/11] fix bugs --- mne_qt_browser/_pg_figure.py | 88 ++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/mne_qt_browser/_pg_figure.py b/mne_qt_browser/_pg_figure.py index 2b9662a8..07f8b9e3 100644 --- a/mne_qt_browser/_pg_figure.py +++ b/mne_qt_browser/_pg_figure.py @@ -2228,6 +2228,87 @@ def _update_spinbox_values(self, *args, **kwargs): ) +class HelpDialog(_BaseDialog): + """Shows all keyboard-shortcuts.""" + + def __init__(self, main, **kwargs): + super().__init__(main, title="Help", **kwargs) + + # Show all keyboard-shortcuts in a Scroll-Area + layout = QVBoxLayout() + keyboard_label = QLabel("Keyboard Shortcuts") + keyboard_label.setFont(_q_font(16, bold=True)) + layout.addWidget(keyboard_label) + + scroll_area = QScrollArea() + scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + scroll_area.setSizePolicy( + QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding + ) + scroll_widget = QWidget() + form_layout = QFormLayout() + for key in main.mne.keyboard_shortcuts: + key_dict = main.mne.keyboard_shortcuts[key] + if "description" in key_dict: + if "alias" in key_dict: + key = key_dict["alias"] + for idx, key_des in enumerate(key_dict["description"]): + key_name = key + if "modifier" in key_dict: + mod = key_dict["modifier"][idx] + if mod is not None: + key_name = mod + " + " + key_name + form_layout.addRow(key_name, QLabel(key_des)) + scroll_widget.setLayout(form_layout) + scroll_area.setWidget(scroll_widget) + layout.addWidget(scroll_area) + + # Additional help for mouse interaction + inst = self.mne.instance_type + is_raw = inst == "raw" + is_epo = inst == "epochs" + is_ica = inst == "ica" + ch_cmp = "component" if is_ica else "channel" + ch_epo = "epoch" if is_epo else "channel" + ica_bad = "Mark/unmark component for exclusion" + lclick_data = ica_bad if is_ica else f"Mark/unmark bad {ch_epo}" + lclick_name = ica_bad if is_ica else "Mark/unmark bad channel" + ldrag = "add annotation (in annotation mode)" if is_raw else None + rclick_name = dict( + ica="Show diagnostics for component", + epochs="Show imageplot for channel", + raw="Show channel location", + )[inst] + mouse_help = [ + (f"Left-click {ch_cmp} name", lclick_name), + (f"Left-click {ch_cmp} data", lclick_data), + ("Left-click-and-drag on plot", ldrag), + ("Left-click on plot background", "Place vertical guide"), + ("Right-click on plot background", "Clear vertical guide"), + ("Right-click on channel name", rclick_name), + ] + + mouse_label = QLabel("Mouse Interaction") + mouse_label.setFont(_q_font(16, bold=True)) + layout.addWidget(mouse_label) + mouse_widget = QWidget() + mouse_layout = QFormLayout() + for interaction, description in mouse_help: + if description is not None: + mouse_layout.addRow(f"{interaction}:", QLabel(description)) + mouse_widget.setLayout(mouse_layout) + layout.addWidget(mouse_widget) + + self.setLayout(layout) + self.show() + + # Set minimum width to avoid horizontal scrolling + scroll_area.setMinimumWidth( + scroll_widget.minimumSizeHint().width() + + scroll_area.verticalScrollBar().width() + ) + self.update() + class ProjDialog(_BaseDialog): """A dialog to toggle projections.""" @@ -5137,6 +5218,13 @@ def _toggle_settings_fig(self): else: self.mne.fig_settings.close() self.mne.fig_settings = None + + def _toggle_help_fig(self): + if self.mne.fig_help is None: + HelpDialog(self, name="fig_help") + else: + self.mne.fig_help.close() + self.mne.fig_help = None def _set_butterfly(self, butterfly): self.mne.butterfly = butterfly From da2ae651ca7ce699778c59e1803e9de3209d192a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 4 Mar 2025 12:05:40 +0000 Subject: [PATCH 09/11] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mne_qt_browser/_pg_figure.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mne_qt_browser/_pg_figure.py b/mne_qt_browser/_pg_figure.py index 07f8b9e3..9540e73f 100644 --- a/mne_qt_browser/_pg_figure.py +++ b/mne_qt_browser/_pg_figure.py @@ -2309,6 +2309,7 @@ def __init__(self, main, **kwargs): ) self.update() + class ProjDialog(_BaseDialog): """A dialog to toggle projections.""" @@ -5218,7 +5219,7 @@ def _toggle_settings_fig(self): else: self.mne.fig_settings.close() self.mne.fig_settings = None - + def _toggle_help_fig(self): if self.mne.fig_help is None: HelpDialog(self, name="fig_help") From 2fa6b60b2318a8ace9449cbf91ba01d7605afe51 Mon Sep 17 00:00:00 2001 From: Mohit Kumhar Date: Mon, 31 Mar 2025 21:28:09 +0530 Subject: [PATCH 10/11] added top level menu --- mne_qt_browser/_pg_figure.py | 161 +++++++++++------------ mne_qt_browser/tests/test_pg_specific.py | 33 ----- 2 files changed, 74 insertions(+), 120 deletions(-) diff --git a/mne_qt_browser/_pg_figure.py b/mne_qt_browser/_pg_figure.py index fd75c2c2..bd91aeaa 100644 --- a/mne_qt_browser/_pg_figure.py +++ b/mne_qt_browser/_pg_figure.py @@ -22,6 +22,8 @@ from pathlib import Path import numpy as np +from PyQt5.QtTest import QTest +from PyQt5.QtWidgets import QMenuBar try: from qtpy.QtCore import Qt @@ -4230,97 +4232,83 @@ def __init__(self, **kwargs): # disable histogram of epoch PTP amplitude del self.mne.keyboard_shortcuts["h"] + self.create_menus() - help_menu = QMenu("&Help", self) - keyboard_action = help_menu.addAction("Keyboard Shortcuts") - keyboard_action.triggered.connect(self._show_keyboard_shortcuts) - mouse_action = help_menu.addAction("Mouse Shortcuts") - mouse_action.triggered.connect(self._show_mouse_shortcuts) - - help_btn = QToolButton() - help_btn.setIcon(self._qicon("help")) - help_btn.setText("Help") - help_btn.setMenu(self._create_help_menu()) - help_btn.setPopupMode(QToolButton.InstantPopup) - self.mne.toolbar.addWidget(help_btn) - - - - - def _show_keyboard_shortcuts(self): - """Display keyboard shortcuts in a popup.""" - msg = QMessageBox(self) - msg.setWindowTitle("Keyboard Shortcuts") - - text = "Keyboard Controls:
    " - for key, spec in self.mne.keyboard_shortcuts.items(): - if 'description' in spec: - text += f"
  • {', '.join(spec['description'])}
  • " - text += "
" - - msg.setText(text) - msg.exec_() - - def _show_mouse_shortcuts(self): - """Display mouse shortcuts in a popup.""" - msg = QMessageBox(self) - msg.setWindowTitle("Mouse Shortcuts") - - text = """Mouse Controls: -
    -
  • Left-click channel name: Mark/unmark bad channel
  • -
  • Left-click data: Mark/unmark bad segment
  • -
  • Right-click plot: Context menu
  • -
  • Drag: Create annotation (annotation mode)
  • -
  • Scroll: Navigate time/channels
  • -
""" - - msg.setText(text) - msg.exec_() - - def _create_help_menu(self): - """Create help menu dynamically from HelpDialog content.""" - help_menu = QMenu("&Help", self) - - # Ensure we correctly access `keyboard_shortcuts` - keyboard_shortcuts = getattr(self.mne, "keyboard_shortcuts", None) - - if keyboard_shortcuts: - keyboard_menu = QMenu("Keyboard Shortcuts", self) - - for key, spec in keyboard_shortcuts.items(): - key_name = spec.get('alias', key) - descriptions = spec.get('description', []) - modifiers = spec.get('modifier', [None] * len(descriptions)) - - for mod, desc in zip(modifiers, descriptions): - key_combination = f"{mod} + {key_name}" if mod else key_name - shortcut_text = f"{desc}\t{key_combination}" # Align key to the right - action = QAction(shortcut_text, self) - keyboard_menu.addAction(action) - - help_menu.addMenu(keyboard_menu) - - # Mouse Controls - mouse_menu = QMenu("Mouse Controls", self) - mouse_interactions = [ - ("Left-click component name", "Mark/unmark bad channel"), - ("Left-click component data", "Mark/unmark bad segment"), - ("Left-click-and-drag on plot", "Add annotation (annotation mode)"), - ("Right-click plot", "Context menu"), - ("Scroll", "Navigate time/channels"), - ("Shift+Click", "Add/remove channels from annotations"), - ] + def create_menus(self): + """Creates and returns the application's menu bar with properly aligned shortcuts.""" + + if not self.menuBar(): + self.setMenuBar(QMenuBar(self)) + menu_bar = self.menuBar() + mne_python = menu_bar.addMenu("MNE-Python") + view_menu = menu_bar.addMenu("View") + help_menu = menu_bar.addMenu("Help") + scroll_menu = view_menu.addMenu("Scroll") + + def add_menu_action(menu, description, shortcut): + widget = QWidget() + layout = QHBoxLayout(widget) + layout.setContentsMargins(10, 2, 10, 2) - for action_text, description in mouse_interactions: - shortcut_text = f"{description}\t{action_text}" - action = QAction(shortcut_text, self) - mouse_menu.addAction(action) + desc_label = QLabel(description) + shortcut_label = QLabel(shortcut) - help_menu.addMenu(mouse_menu) + layout.addWidget(desc_label) + layout.addStretch() + layout.addWidget(shortcut_label) - return help_menu + action = QWidgetAction(self) + action.setDefaultWidget(widget) + menu.addAction(action) + + shortcut_data = { + mne_python: [ + ("Create annotation ", "Click + Drag"), + ("Mark/unmark bad channel ", "Left-click channel"), + ("Mark/unmark bad segment ", "Left-click data"), + ("Toggle Annotation Tool / Visibility ", "A"), + ("Toggle Butterfly ", "B"), + ("Toggle DC Correction ", "D"), + ("Toggle Events visibility ", "E"), + ("Toggle Projection Figure / all projections ", "J"), + ], + + view_menu: [ + ("Add/remove channels ", "Shift + Click"), + ("Decrease duration (¼ page) ", "Home"), + ("Decrease Scale ", "-"), + ("Decrease shown channels (1/10) ", "Page Up"), + ("Increase duration (¼ page) ", "End"), + ("Increase Scale ", "+ / ="), + ("Increase shown channels (1/10) ", "Page Down"), + ("Navigate time/channels ", "Scroll"), + ("Open context menu ", "Right-click plot"), + ("Toggle Antialiasing ", "L"), + ("Toggle Overview Bar ", "O"), + ("Toggle Time Format ", "T"), + ("Toggle Scalebars ", "S"), + ("Toggle Whitening ", "W"), + ("Toggle Crosshair ", "X"), + ("Toggle Zen Mode ", "Z"), + ], + + help_menu: [ + ("Show Help ", "?"), + ("Toggle Fullscreen ", "F11"), + ("Close ", "Escape"), + ], + + scroll_menu: [ + ("Scroll left (¼ page/full page) ", "← / →"), + ("Scroll up (full page) ", "↑"), + ("Scroll down (full page) ", "↓"), + ], + } + for menu, items in shortcut_data.items(): + for description, shortcut in items: + add_menu_action(menu, description, shortcut) + return menu_bar def _hidpi_mkPen(self, *args, **kwargs): kwargs["width"] = self._pixel_ratio * kwargs.get("width", 1.0) @@ -4336,7 +4324,6 @@ def _add_scalebars(self): range) """ self.mne.scalebars.clear() - # To keep order (np.unique sorts) ordered_types = self.mne.ch_types[self.mne.ch_order] unique_type_idxs = np.unique(ordered_types, return_index=True)[1] ch_types_ordered = [ordered_types[idx] for idx in sorted(unique_type_idxs)] diff --git a/mne_qt_browser/tests/test_pg_specific.py b/mne_qt_browser/tests/test_pg_specific.py index dd8f6288..acae65cb 100644 --- a/mne_qt_browser/tests/test_pg_specific.py +++ b/mne_qt_browser/tests/test_pg_specific.py @@ -486,28 +486,6 @@ def test_pg_settings_dialog(raw_orig, pg_backend): fig.resize(orig_window_size.width() * 2, orig_window_size.height() * 2) assert ch_sens_spinbox.value() != orig_sens - -def test_pg_help_dialog(raw_orig, pg_backend): - """Test Settings Dialog toggle on/off for pyqtgraph-backend.""" - fig = raw_orig.plot() - fig.test_mode = True - QTest.qWaitForWindowExposed(fig) - QTest.qWait(50) - assert fig.mne.fig_help is None - fig._fake_click_on_toolbar_action("Help", wait_after=500) - assert fig.mne.fig_help is not None - assert pg_backend._get_n_figs() == 2 - fig._fake_click_on_toolbar_action("Help", wait_after=500) - assert fig.mne.fig_help is None - assert pg_backend._get_n_figs() == 1 - fig._fake_click_on_toolbar_action("Help", wait_after=500) - assert fig.mne.fig_help is not None - assert pg_backend._get_n_figs() == 2 - fig._fake_click_on_toolbar_action("Help", wait_after=500) - assert fig.mne.fig_help is None - assert pg_backend._get_n_figs() == 1 - - def test_pg_toolbar_time_plus_minus(raw_orig, pg_backend): """Test time controls.""" fig = raw_orig.plot() @@ -672,17 +650,6 @@ def test_pg_toolbar_actions(raw_orig, pg_backend): assert pg_backend._get_n_figs() == 3 fig._fake_click_on_toolbar_action("Settings", wait_after=100) assert pg_backend._get_n_figs() == 2 - fig._fake_click_on_toolbar_action("Help", wait_after=200) - assert pg_backend._get_n_figs() == 3 - fig._fake_click_on_toolbar_action("Settings", wait_after=200) - assert pg_backend._get_n_figs() == 4 - fig._fake_click_on_toolbar_action(SHOW_PROJECTORS, wait_after=200) - assert pg_backend._get_n_figs() == 3 - fig._fake_click_on_toolbar_action("Settings", wait_after=100) - assert pg_backend._get_n_figs() == 2 - fig._fake_click_on_toolbar_action("Help", wait_after=100) - assert pg_backend._get_n_figs() == 1 - # LAB values taken from colorspacious on 2024/06/10 @pytest.mark.parametrize( From 5d3d99aea8c752482580e59cc0dcc145fed4fb4d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 31 Mar 2025 17:07:21 +0000 Subject: [PATCH 11/11] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mne_qt_browser/_pg_figure.py | 4 ---- mne_qt_browser/tests/test_pg_specific.py | 2 ++ 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/mne_qt_browser/_pg_figure.py b/mne_qt_browser/_pg_figure.py index bd91aeaa..9c49aa6f 100644 --- a/mne_qt_browser/_pg_figure.py +++ b/mne_qt_browser/_pg_figure.py @@ -4236,7 +4236,6 @@ def __init__(self, **kwargs): def create_menus(self): """Creates and returns the application's menu bar with properly aligned shortcuts.""" - if not self.menuBar(): self.setMenuBar(QMenuBar(self)) menu_bar = self.menuBar() @@ -4272,7 +4271,6 @@ def add_menu_action(menu, description, shortcut): ("Toggle Events visibility ", "E"), ("Toggle Projection Figure / all projections ", "J"), ], - view_menu: [ ("Add/remove channels ", "Shift + Click"), ("Decrease duration (¼ page) ", "Home"), @@ -4291,13 +4289,11 @@ def add_menu_action(menu, description, shortcut): ("Toggle Crosshair ", "X"), ("Toggle Zen Mode ", "Z"), ], - help_menu: [ ("Show Help ", "?"), ("Toggle Fullscreen ", "F11"), ("Close ", "Escape"), ], - scroll_menu: [ ("Scroll left (¼ page/full page) ", "← / →"), ("Scroll up (full page) ", "↑"), diff --git a/mne_qt_browser/tests/test_pg_specific.py b/mne_qt_browser/tests/test_pg_specific.py index acae65cb..c53af934 100644 --- a/mne_qt_browser/tests/test_pg_specific.py +++ b/mne_qt_browser/tests/test_pg_specific.py @@ -486,6 +486,7 @@ def test_pg_settings_dialog(raw_orig, pg_backend): fig.resize(orig_window_size.width() * 2, orig_window_size.height() * 2) assert ch_sens_spinbox.value() != orig_sens + def test_pg_toolbar_time_plus_minus(raw_orig, pg_backend): """Test time controls.""" fig = raw_orig.plot() @@ -651,6 +652,7 @@ def test_pg_toolbar_actions(raw_orig, pg_backend): fig._fake_click_on_toolbar_action("Settings", wait_after=100) assert pg_backend._get_n_figs() == 2 + # LAB values taken from colorspacious on 2024/06/10 @pytest.mark.parametrize( "rgb, lab",