diff --git a/mne/viz/backends/_qt.py b/mne/viz/backends/_qt.py index 3df98daa317..310c23128f3 100644 --- a/mne/viz/backends/_qt.py +++ b/mne/viz/backends/_qt.py @@ -11,7 +11,7 @@ import pyvista from pyvistaqt.plotting import FileDialog, MainWindow -from qtpy.QtCore import Qt, Signal, QLocale, QObject +from qtpy.QtCore import Qt, Signal, QLocale, QObject, QEvent from qtpy.QtGui import QIcon, QCursor from qtpy.QtWidgets import (QComboBox, QDockWidget, QDoubleSpinBox, QGroupBox, QHBoxLayout, QLabel, QToolButton, QMenuBar, @@ -723,12 +723,7 @@ def _window_set_theme(self, theme=None): else: default_theme = theme theme = get_config('MNE_3D_OPTION_THEME', default_theme) - stylesheet = _qt_get_stylesheet(theme) - self._window.setStyleSheet(stylesheet) - if _qt_is_dark(self._window): - QIcon.setThemeName('dark') - else: - QIcon.setThemeName('light') + _set_window_theme(self._window, theme) class _QtWidgetList(_AbstractWidgetList): @@ -921,6 +916,21 @@ def _create_dock_widget(window, name, area, *, max_width=None): return dock, dock_layout +def _set_window_theme(window, theme): + stylesheet = _qt_get_stylesheet(theme) + # The setStyleSheet() function triggers a PaletteChange event so we + # need to filter out the newly created one to avoid ending in an + # infinite loop. + event_filter = _DiscardEventFilter('PaletteChange') + window.installEventFilter(event_filter) + window.setStyleSheet(stylesheet) + window.removeEventFilter(event_filter) + if _qt_is_dark(window): + QIcon.setThemeName('dark') + else: + QIcon.setThemeName('light') + + @contextmanager def _testing_context(interactive): from . import renderer @@ -942,6 +952,19 @@ def _testing_context(interactive): renderer.MNE_3D_BACKEND_INTERACTIVE = orig_interactive +class _DiscardEventFilter(QObject): + def __init__(self, event_name): + super().__init__() + self._event_type = getattr(QEvent, event_name) + + def eventFilter(self, obj, ev): + if ev.type() == self._event_type: + ev.accept() + return True + else: + return False + + # In theory we should be able to do this later (e.g., in _pyvista.py when # initializing), but at least on Qt6 this has to be done earlier. So let's do # it immediately upon instantiation of the QMainWindow class. @@ -952,3 +975,10 @@ class _MNEMainWindow(MainWindow): def __init__(self, parent=None, title=None, size=None): super().__init__(parent, title, size) self.setAttribute(Qt.WA_ShowWithoutActivating, True) + + def event(self, ev): + """Catch system events.""" + if ev.type() == QEvent.PaletteChange: # detect theme switches + theme = get_config('MNE_3D_OPTION_THEME', 'auto') + _set_window_theme(self, theme) + return super().event(ev)