Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
f208969
MAINT: Prefer PySide6 in testing
larsoner Apr 11, 2022
8d5d455
FIX: Whoops
larsoner Apr 11, 2022
4beed05
FIX: URL [circle front]
larsoner Apr 11, 2022
0e79ab4
FIX: Try 20.04 [circle front]
larsoner Apr 11, 2022
6ecff14
FIX: Another [circle front]
larsoner Apr 11, 2022
8c90e34
FIX: Another [circle front]
larsoner Apr 11, 2022
c929116
FIX: Better [circle front]
larsoner Apr 11, 2022
8837b56
FIX: Circle [circle front]
larsoner Apr 11, 2022
6135fba
FIX: Circle [circle front]
larsoner Apr 11, 2022
0da7bac
FIX: 3.9 [circle front]
larsoner Apr 11, 2022
a318d6f
FIX: Try again [circle front]
larsoner Apr 11, 2022
d02727a
WIP: Try [circle front]
larsoner Apr 11, 2022
815475c
WIP: Try to be sure [skip azp] [skip actions] [circle front]
larsoner Apr 11, 2022
7b50d3b
FIX: Try PyQt6 on CircleCI
larsoner Apr 11, 2022
5f364bc
FIX: Try again [circle front]
larsoner Apr 11, 2022
0d97ef1
FIX: Typo [circle front]
larsoner Apr 11, 2022
4054948
Revert "FIX: Typo [circle front]"
larsoner Apr 11, 2022
89c938e
Revert "FIX: Try again [circle front]"
larsoner Apr 11, 2022
c1db784
FIX: Try again
larsoner Apr 11, 2022
993460a
Revert "FIX: Try PyQt6 on CircleCI"
larsoner Apr 11, 2022
ac22b4b
FIX: Try
larsoner Apr 11, 2022
d1b23b8
FIX: Really
larsoner Apr 11, 2022
f8706f0
FIX: Really sure
larsoner Apr 11, 2022
2a24a6c
FIX: Really REALLY sure
larsoner Apr 11, 2022
c6a2507
FIX: Okay still really sure
larsoner Apr 11, 2022
f1932e2
FIX: Missed one
larsoner Apr 11, 2022
d19af98
FIX: Come on
larsoner Apr 11, 2022
936d78b
FIX: Argh
larsoner Apr 11, 2022
50d1572
FIX: Revert
larsoner Apr 12, 2022
35c51f2
FIX: Fix tests
larsoner Apr 12, 2022
81d2428
FIX: Tsets
larsoner Apr 12, 2022
4dcad3f
TST: One more
larsoner Apr 12, 2022
dfb1a6a
FIX: Found one
larsoner Apr 12, 2022
b254cf4
FIX: Ignore
larsoner Apr 12, 2022
3cfae7d
WIP: Come on please [circle front]
larsoner Apr 12, 2022
bc5deb2
DOC: Latest [circle front]
larsoner Apr 12, 2022
69a0f5a
Update mne/viz/_3d.py
larsoner Apr 12, 2022
cf1c96d
FIX: Weakrefs [circle front]
larsoner Apr 13, 2022
97a4ad6
FIX: Better [circle front]
larsoner Apr 13, 2022
9f4125b
FIX: Give up [circle front]
larsoner Apr 13, 2022
27e5874
FIX: Give up [circle front]
larsoner Apr 13, 2022
c6739c6
FIX: Revert one
larsoner Apr 13, 2022
9e84b31
FIX: Weakness as a strength [circle front]
larsoner Apr 13, 2022
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
28 changes: 7 additions & 21 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,14 @@

version: 2.1

_xvfb: &xvfb
name: Start Xvfb virtual framebuffer
command: |
echo "export DISPLAY=:99" >> $BASH_ENV
/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -screen 0 1280x1024x24 -ac +extension GLX +render -noreset -nolisten tcp -nolisten unix

jobs:
build_docs:
parameters:
scheduled:
type: string
default: "false"
docker:
# Use 18.04 rather than 20.04 because MESA 20.0.8 on 18.04 has working
# transparency but 21.0.3 on 20.04 does not!
- image: cimg/base:stable-18.04
- image: cimg/base:stable-20.04
steps:
- restore_cache:
keys:
Expand Down Expand Up @@ -95,23 +87,20 @@ jobs:
name: Set BASH_ENV
command: |
set -e
sudo apt update -qq
sudo apt install -qq libosmesa6 libglx-mesa0 libopengl0 libglx0 libdbus-1-3 \
libxkbcommon-x11-0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 \
libxcb-render-util0 libxcb-shape0 libxcb-xfixes0 libxcb-xinerama0 \
graphviz optipng \
python3.8-venv python3-venv \
xvfb libxft2 ffmpeg
./tools/setup_xvfb.sh
sudo apt install -qq graphviz optipng python3.8-venv python3-venv libxft2 ffmpeg
python3.8 -m venv ~/python_env
echo "set -e" >> $BASH_ENV
echo "export OPENBLAS_NUM_THREADS=4" >> $BASH_ENV
echo "export XDG_RUNTIME_DIR=/tmp/runtime-circleci" >> $BASH_ENV
echo "export MNE_FULL_DATE=true" >> $BASH_ENV
source tools/get_minimal_commands.sh
echo "export MNE_3D_BACKEND=pyvistaqt" >> $BASH_ENV
echo "export MNE_3D_OPTION_MULTI_SAMPLES=1" >> $BASH_ENV
echo "export MNE_BROWSER_BACKEND=qt" >> $BASH_ENV
echo "export MNE_BROWSER_PRECOMPUTE=false" >> $BASH_ENV
echo "export PATH=~/.local/bin/:$PATH" >> $BASH_ENV
echo "export DISPLAY=:99" >> $BASH_ENV
echo "source ~/python_env/bin/activate" >> $BASH_ENV
mkdir -p ~/.local/bin
ln -s ~/python_env/bin/python ~/.local/bin/python
Expand All @@ -124,9 +113,6 @@ jobs:
command: |
neuromag2ft --version

- run:
<<: *xvfb

- run:
name: Install fonts needed for diagrams
command: |
Expand Down Expand Up @@ -160,8 +146,8 @@ jobs:
- ~/.local/bin

- run:
name: Check PyQt5
command: LD_DEBUG=libs python -c "from PyQt5.QtWidgets import QApplication, QWidget; app = QApplication([])"
name: Check Qt
command: LD_DEBUG=libs python -c "from PySide6.QtWidgets import QApplication, QWidget; app = QApplication([])"

# Load tiny cache so that ~/.mne does not need to be created below
- restore_cache:
Expand Down
38 changes: 19 additions & 19 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,9 @@ stages:
OPENBLAS_NUM_THREADS: '1'
steps:
- bash: |
sudo apt install libxkbcommon-x11-0 xvfb tcsh libxcb*
set -e
./tools/setup_xvfb.sh
sudo apt install -yq tcsh
displayName: 'Install Ubuntu dependencies'
- bash: |
source tools/get_minimal_commands.sh
Expand All @@ -105,17 +107,17 @@ stages:
architecture: 'x64'
addToPath: true
displayName: 'Get Python'
- bash: |
/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -screen 0 1400x900x24 -ac +extension GLX +render -noreset;
displayName: 'Spin up Xvfb'
- bash: |
set -e
python -m pip install --progress-bar off --upgrade pip setuptools wheel codecov
python -m pip install --progress-bar off mne-qt-browser[opengl] vtk scikit-learn pytest-error-for-skips python-picard
python -m pip install --progress-bar off mne-qt-browser[opengl] vtk scikit-learn pytest-error-for-skips python-picard PySide6 qtpy
python -m pip uninstall -yq mne
python -m pip install --progress-bar off --upgrade -e .[test]
displayName: 'Install dependencies with pip'
- script: mne sys_info -pd
- bash: |
set -e
mne sys_info -pd
mne sys_info -pd | grep "qtpy: .*{PySide6=.*}$"
displayName: Print config
- bash: source tools/get_testing_version.sh
displayName: 'Get testing version'
Expand Down Expand Up @@ -152,10 +154,7 @@ stages:
OPENBLAS_NUM_THREADS: '1'
TEST_OPTIONS: "--tb=short --cov=mne --cov-report=xml --cov-report=html --cov-append -vv mne/viz/_brain mne/viz/backends mne/viz/tests/test_evoked.py mne/gui"
steps:
- bash: |
set -e
./tools/setup_xvfb.sh
sudo apt install libegl1
- bash: ./tools/setup_xvfb.sh
displayName: 'Install Ubuntu dependencies'
- task: UsePythonVersion@0
inputs:
Expand Down Expand Up @@ -191,10 +190,10 @@ stages:
- bash: |
set -e
mne sys_info -pd
mne sys_info -pd | grep "^qtpy: .*{PyQt5=.*}$"
mne sys_info -pd | grep "qtpy: .*{PySide6=.*}$"
pytest -m "not slowtest" ${TEST_OPTIONS}
python -m pip uninstall -yq PyQt5 PyQt5-sip PyQt5-Qt5
displayName: 'PyQt5'
python -m pip uninstall -yq PySide6
displayName: 'PySide6'
- bash: |
set -e
python -m pip install PyQt6
Expand All @@ -211,14 +210,15 @@ stages:
pytest -m "not slowtest" ${TEST_OPTIONS}
python -m pip uninstall -yq PySide2
displayName: 'PySide2'
# PyQt5 leaves cruft behind, so run it last
- bash: |
set -e
python -m pip install PySide6
python -m pip install PyQt5
mne sys_info -pd
mne sys_info -pd | grep "qtpy: .*{PySide6=.*}$"
mne sys_info -pd | grep "^qtpy: .*{PyQt5=.*}$"
pytest -m "not slowtest" ${TEST_OPTIONS}
python -m pip uninstall -yq PySide6
displayName: 'PySide6'
python -m pip uninstall -yq PyQt5 PyQt5-sip PyQt5-Qt5
displayName: 'PyQt5'
# Coverage
- bash: bash <(curl -s https://codecov.io/bash)
displayName: 'Codecov'
Expand Down Expand Up @@ -257,9 +257,9 @@ stages:
PLATFORM: 'x86-64'
TEST_MODE: 'conda'
PYTHON_VERSION: '3.9'
3.7 pip:
3.9 pip:
TEST_MODE: 'pip'
PYTHON_VERSION: '3.7'
PYTHON_VERSION: '3.9'
3.10 pip pre:
TEST_MODE: 'pip-pre'
PYTHON_VERSION: '3.10'
Expand Down
2 changes: 2 additions & 0 deletions doc/changes/latest.inc
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ Bugs

- Fix bug where ``theme`` was not handled properly in :meth:`mne.io.Raw.plot` (:gh:`10487`, :gh:`10500` by `Mathieu Scheltienne`_ and `Eric Larson`_)

- Rendering issues with recent MESA releases can be avoided by setting the new environment variable``MNE_3D_OPTION_MULTI_SAMPLES=1`` or using :func:`mne.viz.set_3d_options` (:gh:`10513` by `Eric Larson`_)

- Fix behavior for the ``pyvista`` 3D renderer's ``quiver3D`` function so that default arguments plot a glyph in ``arrow`` mode (:gh:`10493` by `Alex Rockhill`_)

- Retain epochs metadata when using :func:`mne.channels.combine_channels` (:gh:`10504` by `Clemens Brunner`_)
Expand Down
18 changes: 11 additions & 7 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
os.environ['_MNE_BROWSER_NO_BLOCK'] = 'true'
os.environ['MNE_BROWSER_OVERVIEW_MODE'] = 'hidden'
os.environ['MNE_BROWSER_THEME'] = 'light'
os.environ['MNE_3D_OPTION_THEME'] = 'light'

# -- Path setup --------------------------------------------------------------

Expand Down Expand Up @@ -311,9 +312,9 @@ def __call__(self, gallery_conf, fname, when):
except ImportError:
vtkPolyData = None # noqa
try:
from mne_qt_browser._pg_figure import PyQtGraphBrowser
from mne_qt_browser._pg_figure import MNEQtBrowser
except ImportError:
PyQtGraphBrowser = None
MNEQtBrowser = None
from mne.viz.backends.renderer import backend
_Renderer = backend._Renderer if backend is not None else None
reset_warnings(gallery_conf, fname)
Expand All @@ -332,7 +333,7 @@ def __call__(self, gallery_conf, fname, when):
when = f'mne/conf.py:Resetter.__call__:{when}:{fname}'
# Support stuff like
# MNE_SKIP_INSTANCE_ASSERTIONS="Brain,Plotter,BackgroundPlotter,vtkPolyData,_Renderer" make html_dev-memory # noqa: E501
# to just test PyQtGraphBrowser
# to just test MNEQtBrowser
skips = os.getenv('MNE_SKIP_INSTANCE_ASSERTIONS', '').lower()
prefix = ''
if skips not in ('true', '1', 'all'):
Expand All @@ -349,15 +350,15 @@ def __call__(self, gallery_conf, fname, when):
_assert_no_instances(vtkPolyData, when)
if '_renderer' not in skips:
_assert_no_instances(_Renderer, when)
if PyQtGraphBrowser is not None and \
'pyqtgraphbrowser' not in skips:
if MNEQtBrowser is not None and \
'mneqtbrowser' not in skips:
# Ensure any manual fig.close() events get properly handled
from mne_qt_browser._pg_figure import QApplication
inst = QApplication.instance()
if inst is not None:
for _ in range(2):
inst.processEvents()
_assert_no_instances(PyQtGraphBrowser, when)
_assert_no_instances(MNEQtBrowser, when)
# This will overwrite some Sphinx printing but it's useful
# for memory timestamps
if os.getenv('SG_STAMP_STARTS', '').lower() == 'true':
Expand Down Expand Up @@ -394,7 +395,7 @@ def __call__(self, gallery_conf, fname, when):
import mne_qt_browser
_min_ver = _compare_version(mne_qt_browser.__version__, '>=', '0.2')
if mne.viz.get_browser_backend() == 'qt' and _min_ver:
scrapers += (mne.viz._scraper._PyQtGraphScraper(),)
scrapers += (mne.viz._scraper._MNEQtBrowserScraper(),)
except ImportError:
pass

Expand Down Expand Up @@ -892,6 +893,9 @@ def reset_warnings(gallery_conf, fname):
warnings.filterwarnings(
'ignore', message=r'numpy\.ndarray size changed.*',
category=RuntimeWarning)
warnings.filterwarnings(
'ignore', message=r'.*Setting theme=.*6 in qdarkstyle.*',
category=RuntimeWarning)

# In case we use np.set_printoptions in any tutorials, we only
# want it to affect those:
Expand Down
6 changes: 6 additions & 0 deletions doc/install/advanced.rst
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,12 @@ to force MESA to use modern OpenGL by using this before executing

Also, it's possible that different software rending backends might perform
better than others, such as using the ``llvmpipe`` backend rather than ``swr``.
In newer MESA (21+), rendering can be incorrect when using MSAA, so consider
setting:

.. code-block:: console

export MNE_3D_OPTION_MULTI_SAMPLES=1

MESA also can have trouble with full-screen antialiasing, which you can
disable with:
Expand Down
4 changes: 2 additions & 2 deletions doc/install/check_installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ MNE-Python and its dependencies. Typical output looks like this::
mne: 0.21.dev0
numpy: 1.19.0.dev0+8dfaa4a {blas=openblas, lapack=openblas}
scipy: 1.5.0.dev0+f614064
matplotlib: 3.2.1 {backend=Qt5Agg}
matplotlib: 3.2.1 {backend=QtAgg}

sklearn: 0.22.2.post1
numba: 0.49.0
Expand All @@ -37,7 +37,7 @@ MNE-Python and its dependencies. Typical output looks like this::
dipy: 1.1.1
pyvista: 0.25.2 {pyvistaqt=0.1.0}
vtk: 9.0.0
qtpy: 2.0.1 {PyQt5=5.14.1}
qtpy: 2.0.1 {PySide6=6.2.4}


.. collapse:: |hand-paper| If you get an error...
Expand Down
3 changes: 1 addition & 2 deletions mne/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -440,8 +440,7 @@ def _check_pyqtgraph(request):
pytest.skip('Requires mne_qt_browser')
else:
ver = mne_qt_browser.__version__
req_pyqt5 = _compare_version(ver, '<=', '0.2.6')
if api != 'PyQt5' and req_pyqt5:
if api != 'PyQt5' and _compare_version(ver, '<=', '0.2.6'):
pytest.skip(f'mne_qt_browser {ver} requires PyQt5, API is {api}')


Expand Down
1 change: 1 addition & 0 deletions mne/utils/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ def set_memmap_min_size(memmap_min_size):
known_config_types = (
'MNE_3D_OPTION_ANTIALIAS',
'MNE_3D_OPTION_DEPTH_PEELING',
'MNE_3D_OPTION_MULTI_SAMPLES',
'MNE_3D_OPTION_SMOOTH_SHADING',
'MNE_3D_OPTION_THEME',
'MNE_BROWSE_RAW_SIZE',
Expand Down
6 changes: 6 additions & 0 deletions mne/utils/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,12 @@ def _assert_no_instances(cls, when=''):
del r_
else:
rep = repr(r)[:100].replace('\n', ' ')
# If it's a __closure__, get more information
if rep.startswith('<cell at '):
try:
rep += f' ({repr(r.cell_contents)[:100]})'
except Exception:
pass
name = _fullname(r)
ref.append(f'{name}: {rep}')
count += 1
Expand Down
35 changes: 24 additions & 11 deletions mne/viz/_3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -3292,24 +3292,28 @@ def plot_brain_colorbar(ax, clim, colormap='auto', transparent=True,

@dataclass()
class _3d_Options:
antialias: Optional[str]
depth_peeling: Optional[str]
smooth_shading: Optional[str]
antialias: Optional[bool]
depth_peeling: Optional[bool]
smooth_shading: Optional[bool]
multi_samples: Optional[int]


_3d_options = _3d_Options(
antialias=None,
depth_peeling=None,
smooth_shading=None,
multi_samples=None,
)
_3d_default = _3d_Options(
antialias='true',
depth_peeling='true',
smooth_shading='true',
multi_samples='4',
)


def set_3d_options(antialias=None, depth_peeling=None, smooth_shading=None):
def set_3d_options(antialias=None, depth_peeling=None, smooth_shading=None, *,
multi_samples=None):
"""Set 3D rendering options.

Parameters
Expand All @@ -3332,25 +3336,34 @@ def set_3d_options(antialias=None, depth_peeling=None, smooth_shading=None):
where this type of shading is not supported or for performance
reasons. This option can also be controlled using an environment
variable, e.g., ``MNE_3D_OPTION_SMOOTH_SHADING=false``.
multi_samples : int
Number of multi-samples. Should be 1 for MESA for volumetric rendering
to work properly.

.. versionadded:: 1.1

Notes
-----
.. versionadded:: 0.21.0
"""
if antialias is not None:
_3d_options.antialias = str(bool(antialias)).lower()
_3d_options.antialias = bool(antialias)
if depth_peeling is not None:
_3d_options.depth_peeling = str(bool(depth_peeling)).lower()
_3d_options.depth_peeling = bool(depth_peeling)
if smooth_shading is not None:
_3d_options.smooth_shading = str(bool(smooth_shading)).lower()
_3d_options.smooth_shading = bool(smooth_shading)
if multi_samples is not None:
_3d_options.multi_samples = int(multi_samples)


def _get_3d_option(key):
_validate_type(key, 'str', 'key')
opt = getattr(_3d_options, key)
if opt is None:
if opt is None: # parse get_config (and defaults)
default_value = getattr(_3d_default, key)
opt = get_config(f'MNE_3D_OPTION_{key.upper()}', default_value)
opt = opt.lower()
_check_option(f'3D option {key}', opt, ('true', 'false'))
return opt == 'true'
if key == 'multi_samples':
opt = int(opt)
else:
opt = opt.lower() == 'true'
return opt
Loading