diff --git a/README.rst b/README.rst index 433c6a1d82f..806f5469e1d 100644 --- a/README.rst +++ b/README.rst @@ -88,12 +88,12 @@ For full functionality, some functions require: - `scikit-learn `__ ≥ 1.0 - `Joblib `__ ≥ 0.15 (for parallelization) - `mne-qt-browser `__ ≥ 0.1 (for fast raw data visualization) -- `Qt `__ ≥ 5.12 via one of the following bindings (for fast raw data visualization and interactive 3D visualization): +- `Qt `__ ≥ 5.15 via one of the following bindings (for fast raw data visualization and interactive 3D visualization): - `PyQt6 `__ ≥ 6.0 - `PySide6 `__ ≥ 6.0 - - `PyQt5 `__ ≥ 5.12 - - `PySide2 `__ ≥ 5.12 + - `PyQt5 `__ ≥ 5.15 + - `PySide2 `__ ≥ 5.15 - `Numba `__ ≥ 0.54.0 - `NiBabel `__ ≥ 3.2.1 diff --git a/doc/changes/devel/12491.dependency.rst b/doc/changes/devel/12491.dependency.rst new file mode 100644 index 00000000000..423082320ca --- /dev/null +++ b/doc/changes/devel/12491.dependency.rst @@ -0,0 +1 @@ +The minimum supported version of Qt bindings is 5.15, by `Eric Larson`_. diff --git a/mne/conftest.py b/mne/conftest.py index 5b19dec59c5..2d153f92f40 100644 --- a/mne/conftest.py +++ b/mne/conftest.py @@ -963,6 +963,8 @@ def pytest_sessionfinish(session, exitstatus): # get the number to print files = defaultdict(lambda: 0.0) for item in session.items: + if _phase_report_key not in item.stash: + continue report = item.stash[_phase_report_key] dur = sum(x.duration for x in report.values()) parts = Path(item.nodeid.split(":")[0]).parts diff --git a/mne/gui/tests/test_gui_api.py b/mne/gui/tests/test_gui_api.py index 004c670a5ca..ae04124dd14 100644 --- a/mne/gui/tests/test_gui_api.py +++ b/mne/gui/tests/test_gui_api.py @@ -11,10 +11,9 @@ pytest.importorskip("nibabel") -def test_gui_api(renderer_notebook, nbexec, *, n_warn=0, backend="qt"): +def test_gui_api(renderer_notebook, nbexec, *, backend="qt"): """Test GUI API.""" import contextlib - import sys import warnings import mne @@ -25,7 +24,6 @@ def test_gui_api(renderer_notebook, nbexec, *, n_warn=0, backend="qt"): except Exception: # Notebook standalone mode backend = "notebook" - n_warn = 0 # nbexec does not expose renderer_notebook so I use a # temporary variable to synchronize the tests if backend == "notebook": @@ -44,8 +42,7 @@ def test_gui_api(renderer_notebook, nbexec, *, n_warn=0, backend="qt"): with mne.utils._record_warnings() as w: renderer._window_set_theme("dark") w = [ww for ww in w if "is not yet supported" in str(ww.message)] - if sys.platform != "darwin": # sometimes this is fine - assert len(w) == n_warn, [ww.message for ww in w] + assert len(w) == 0, [ww.message for ww in w] # window without 3d plotter if backend == "qt": @@ -387,10 +384,9 @@ def _check_widget_trigger( def test_gui_api_qt(renderer_interactive_pyvistaqt): """Test GUI API with the Qt backend.""" _, api = _check_qt_version(return_api=True) - n_warn = int(api in ("PySide6", "PyQt6")) # TODO: After merging https://github.com/mne-tools/mne-python/pull/11567 # The Qt CI run started failing about 50% of the time, so let's skip this # for now. if api == "PySide6": pytest.skip("PySide6 causes segfaults on CIs sometimes") - test_gui_api(None, None, n_warn=n_warn, backend="qt") + test_gui_api(None, None, backend="qt") diff --git a/mne/viz/backends/_utils.py b/mne/viz/backends/_utils.py index 56405bc3cdb..123546db035 100644 --- a/mne/viz/backends/_utils.py +++ b/mne/viz/backends/_utils.py @@ -274,84 +274,42 @@ def _qt_detect_theme(): def _qt_get_stylesheet(theme): _validate_type(theme, ("path-like",), "theme") theme = str(theme) - orig_theme = theme - system_theme = None - stylesheet = "" - extra_msg = "" - if theme == "auto": - theme = system_theme = _qt_detect_theme() - if theme in ("dark", "light"): - if system_theme is None: - system_theme = _qt_detect_theme() - qt_version, api = _check_qt_version(return_api=True) - # On macOS, we shouldn't need to set anything when the requested theme - # matches that of the current OS state - if sys.platform == "darwin": - extra_msg = f"when in {system_theme} mode on macOS" - # But before 5.13, we need to patch some mistakes - if sys.platform == "darwin" and theme == system_theme: - if theme == "dark" and _compare_version(qt_version, "<", "5.13"): - # Taken using "Digital Color Meter" on macOS 12.2.1 looking at - # Meld, and also adapting (MIT-licensed) - # https://github.com/ColinDuquesnoy/QDarkStyleSheet/blob/master/qdarkstyle/dark/style.qss # noqa: E501 - # Something around rgb(51, 51, 51) worked as the bgcolor here, - # but it's easy enough just to set it transparent and inherit - # the bgcolor of the window (which is the same). We also take - # the separator images from QDarkStyle (MIT). - icons_path = _qt_init_icons() - stylesheet = """\ -QStatusBar { - border: 1px solid rgb(76, 76, 75); - background: transparent; -} -QStatusBar QLabel { - background: transparent; -} -QToolBar { - background-color: transparent; - border-bottom: 1px solid rgb(99, 99, 99); -} -QToolBar::separator:horizontal { - width: 16px; - image: url("%(icons_path)s/toolbar_separator_horizontal@2x.png"); -} -QToolBar::separator:vertical { - height: 16px; - image: url("%(icons_path)s/toolbar_separator_vertical@2x.png"); -} -QToolBar::handle:horizontal { - width: 16px; - image: url("%(icons_path)s/toolbar_move_horizontal@2x.png"); -} -QToolBar::handle:vertical { - height: 16px; - image: url("%(icons_path)s/toolbar_move_vertical@2x.png"); -} -""" % dict(icons_path=icons_path) + stylesheet = "" # no stylesheet + if theme in ("auto", "dark", "light"): + if theme == "auto": + return stylesheet + assert theme in ("dark", "light") + system_theme = _qt_detect_theme() + if theme == system_theme: + return stylesheet + _, api = _check_qt_version(return_api=True) + # On macOS or Qt 6, we shouldn't need to set anything when the requested + # theme matches that of the current OS state + try: + import qdarkstyle + except ModuleNotFoundError: + logger.info( + f'To use {theme} mode when in {system_theme} mode, "qdarkstyle" has' + "to be installed! You can install it with:\n" + "pip install qdarkstyle\n" + ) else: - # Here we are on non-macOS (or on macOS but our sys theme does not - # match the requested theme) - if api in ("PySide6", "PyQt6"): - if orig_theme != "auto" and not (theme == system_theme == "light"): - warn( - f"Setting theme={repr(theme)} is not yet supported " - f"for {api} in qdarkstyle, it will be ignored" - ) + if api in ("PySide6", "PyQt6") and _compare_version( + qdarkstyle.__version__, "<", "3.2.3" + ): + warn( + f"Setting theme={repr(theme)} is not supported for {api} in " + f"qdarkstyle {qdarkstyle.__version__}, it will be ignored. " + "Consider upgrading qdarkstyle to >=3.2.3." + ) else: - try: - import qdarkstyle - except ModuleNotFoundError: - logger.info( - f'To use {theme} mode{extra_msg}, "qdarkstyle" has to ' - "be installed! You can install it with:\n" - "pip install qdarkstyle\n" - ) - else: - klass = getattr( + stylesheet = qdarkstyle.load_stylesheet( + getattr( getattr(qdarkstyle, theme).palette, f"{theme.capitalize()}Palette", ) - stylesheet = qdarkstyle.load_stylesheet(klass) + ) + return stylesheet else: try: file = open(theme) @@ -363,8 +321,7 @@ def _qt_get_stylesheet(theme): else: with file as fid: stylesheet = fid.read() - - return stylesheet + return stylesheet def _should_raise_window(): diff --git a/mne/viz/backends/tests/test_utils.py b/mne/viz/backends/tests/test_utils.py index 196eb030cea..26636004026 100644 --- a/mne/viz/backends/tests/test_utils.py +++ b/mne/viz/backends/tests/test_utils.py @@ -8,7 +8,6 @@ import platform from colorsys import rgb_to_hls -from contextlib import nullcontext import numpy as np import pytest @@ -66,19 +65,7 @@ def test_theme_colors(pg_backend, theme, monkeypatch, tmp_path): monkeypatch.setattr(darkdetect, "theme", lambda: "light") raw = RawArray(np.zeros((1, 1000)), create_info(1, 1000.0, "eeg")) _, api = _check_qt_version(return_api=True) - if api in ("PyQt6", "PySide6"): - if theme == "dark": # we force darkdetect to say the sys is light - ctx = pytest.warns(RuntimeWarning, match="not yet supported") - else: - ctx = nullcontext() - return_early = True - else: - ctx = nullcontext() - return_early = False - with ctx: - fig = raw.plot(theme=theme) - if return_early: - return # we could add a ton of conditionals below, but KISS + fig = raw.plot(theme=theme) is_dark = _qt_is_dark(fig) # on Darwin these checks get complicated, so don't bother for now if platform.system() == "Darwin":