Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP, ENH: Automatic theme switching on macOS #10655

Closed
wants to merge 7 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 37 additions & 7 deletions mne/viz/backends/_qt.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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
Expand All @@ -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.
Expand All @@ -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')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this won't quite do what I'd expect -- if I pass theme='light' when instantiating a brain, I would expect it not to change to dark when the system switches. So the user-selected option (light, dark, auto) needs to be stored on instantiation, and switching done only when it's auto.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yeah good idea!

_set_window_theme(self, theme)
return super().event(ev)