Skip to content

Commit 9e6e288

Browse files
FIX: Guard interactive Qt plots and unavailable datasets for the browser
Tutorials and examples that call interactive Qt backends (raw.plot(), epochs.plot(), ica.plot_sources(), etc.) or depend on large datasets not bundled in JupyterLite will hang or error in Pyodide. Wrap them with sys.platform guards so they are skipped when running in the browser. Interactive Qt plots (skip on emscripten): - tutorials/intro/10_overview.py: raw.plot(), stc.plot() - tutorials/intro/15_inplace.py: original_raw.plot(), rereferenced_raw.plot() - tutorials/intro/20_events_from_raw.py: raw.copy().pick().plot(), raw.plot() - tutorials/intro/40_sensor_locations.py: mne.viz.plot_alignment() - tutorials/evoked/40_whitened.py: raw.plot(), epochs.plot() - examples/preprocessing/muscle_ica.py: all ica.plot_* calls Large datasets unavailable in the browser (raise RuntimeError on emscripten): - tutorials/io/60_ctf_bst_auditory.py: BST auditory dataset (~2.9 GB) - tutorials/io/70_reading_eyetracking_data.py: EyeLink misc dataset - examples/visualization/eyetracking_plot_heatmap.py: EyeLink dataset
1 parent e37acb3 commit 9e6e288

9 files changed

Lines changed: 89 additions & 34 deletions

File tree

examples/preprocessing/muscle_ica.py

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919

2020
# %%
2121

22+
import sys
23+
2224
import mne
2325

2426
data_path = mne.datasets.sample.data_path()
@@ -44,7 +46,8 @@
4446

4547
# %%
4648
# Remove components with postural muscle artifact using ICA
47-
ica.plot_sources(raw)
49+
if sys.platform != "emscripten":
50+
ica.plot_sources(raw)
4851

4952
# %%
5053
# By inspection, let's select out the muscle-artifact components based on
@@ -71,19 +74,22 @@
7174
# slope in log-log units; this is a very typical pattern for muscle artifact.
7275

7376
muscle_idx = [6, 7, 8, 9, 10, 11, 12, 13, 14]
74-
ica.plot_properties(raw, picks=muscle_idx, log_scale=True)
77+
if sys.platform != "emscripten":
78+
ica.plot_properties(raw, picks=muscle_idx, log_scale=True)
7579

7680
# first, remove blinks and heartbeat to compare
7781
blink_idx = [0]
7882
heartbeat_idx = [5]
7983
ica.apply(raw, exclude=blink_idx + heartbeat_idx)
80-
ica.plot_overlay(raw, exclude=muscle_idx)
84+
if sys.platform != "emscripten":
85+
ica.plot_overlay(raw, exclude=muscle_idx)
8186

8287
# %%
8388
# Finally, let's try an automated algorithm to find muscle components
8489
# and ensure that it gets the same components we did manually.
8590
muscle_idx_auto, scores = ica.find_bads_muscle(raw)
86-
ica.plot_scores(scores, exclude=muscle_idx_auto)
91+
if sys.platform != "emscripten":
92+
ica.plot_scores(scores, exclude=muscle_idx_auto)
8793
print(
8894
f"Manually found muscle artifact ICA components: {muscle_idx}\n"
8995
f"Automatically found muscle artifact ICA components: {muscle_idx_auto}"
@@ -107,10 +113,12 @@
107113
n_components=15, method="picard", max_iter="auto", random_state=97
108114
)
109115
ica.fit(raw)
110-
ica.plot_sources(raw)
116+
if sys.platform != "emscripten":
117+
ica.plot_sources(raw)
111118
muscle_idx_auto, scores = ica.find_bads_muscle(raw)
112-
ica.plot_properties(raw, picks=muscle_idx_auto, log_scale=True)
113-
ica.plot_scores(scores, exclude=muscle_idx_auto)
119+
if sys.platform != "emscripten":
120+
ica.plot_properties(raw, picks=muscle_idx_auto, log_scale=True)
121+
ica.plot_scores(scores, exclude=muscle_idx_auto)
114122

115123
print(
116124
f"Manually found muscle artifact ICA components: {muscle_idx}\n"

examples/visualization/eyetracking_plot_heatmap.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,20 @@
2626
# :ref:`example data <eyelink-dataset>`: eye-tracking data recorded from SR research's
2727
# ``'.asc'`` file format.
2828

29+
import sys
30+
2931
import matplotlib.pyplot as plt
3032

3133
import mne
3234
from mne.viz.eyetracking import plot_gaze
3335

36+
if sys.platform == "emscripten":
37+
raise RuntimeError(
38+
"This example requires the MNE EyeLink dataset, "
39+
"which is not available in the browser. Please run this example "
40+
"locally. Visit https://mne.tools for instructions."
41+
)
42+
3443
task_fpath = mne.datasets.eyelink.data_path() / "freeviewing"
3544
et_fpath = task_fpath / "sub-01_task-freeview_eyetrack.asc"
3645
stim_fpath = task_fpath / "stim" / "naturalistic.png"
@@ -82,8 +91,10 @@
8291
# start at a value greater than the darkest value in our previous heatmap, which will
8392
# make the darkest colors of the heatmap transparent.
8493

85-
cmap.set_under("k", alpha=0) # make the lowest values transparent
86-
ax = plt.subplot()
94+
cmap = cmap.with_extremes(
95+
under=(0.0, 0.0, 0.0, 0.0)
96+
) # make the lowest values transparent
97+
_, ax = plt.subplots(figsize=(6, 3.5), layout="constrained")
8798
ax.imshow(plt.imread(stim_fpath))
8899
plot_gaze(
89100
epochs["natural"],

tutorials/evoked/40_whitened.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121

2222
# %%
2323

24+
import sys
25+
2426
import mne
2527
from mne.datasets import sample
2628

@@ -52,14 +54,16 @@
5254
)
5355

5456
# butterfly mode shows the differences most clearly
55-
raw.plot(events=events, butterfly=True)
56-
raw.plot(noise_cov=noise_cov, events=events, butterfly=True)
57+
if sys.platform != "emscripten":
58+
raw.plot(events=events, butterfly=True)
59+
raw.plot(noise_cov=noise_cov, events=events, butterfly=True)
5760

5861
# %%
5962
# Epochs with whitening
6063
# ---------------------
61-
epochs.plot(events=True)
62-
epochs.plot(noise_cov=noise_cov, events=True)
64+
if sys.platform != "emscripten":
65+
epochs.plot(events=True)
66+
epochs.plot(noise_cov=noise_cov, events=True)
6367

6468
# %%
6569
# Evoked data with whitening

tutorials/intro/10_overview.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
# License: BSD-3-Clause
1919
# Copyright the MNE-Python contributors.
2020

21-
# %%
21+
import sys
2222

2323
import numpy as np
2424

@@ -81,8 +81,10 @@
8181
# sessions, `~mne.io.Raw.plot` is interactive and allows scrolling, scaling,
8282
# bad channel marking, annotations, projector toggling, etc.
8383

84+
8485
raw.compute_psd(fmax=50).plot(picks="data", exclude="bads", amplitude=False)
85-
raw.plot(duration=5, n_channels=30)
86+
if sys.platform != "emscripten":
87+
raw.plot(duration=5, n_channels=30)
8688

8789
# %%
8890
# Preprocessing
@@ -140,8 +142,9 @@
140142
"EEG 008",
141143
]
142144
chan_idxs = [raw.ch_names.index(ch) for ch in chs]
143-
orig_raw.plot(order=chan_idxs, start=12, duration=4)
144-
raw.plot(order=chan_idxs, start=12, duration=4)
145+
if sys.platform != "emscripten":
146+
orig_raw.plot(order=chan_idxs, start=12, duration=4)
147+
raw.plot(order=chan_idxs, start=12, duration=4)
145148

146149
# %%
147150
# .. _overview-tut-events-section:
@@ -400,9 +403,10 @@
400403
# path to subjects' MRI files
401404
subjects_dir = sample_data_folder / "subjects"
402405
# plot the STC
403-
stc.plot(
404-
initial_time=0.1, hemi="split", views=["lat", "med"], subjects_dir=subjects_dir
405-
)
406+
if sys.platform != "emscripten":
407+
stc.plot(
408+
initial_time=0.1, hemi="split", views=["lat", "med"], subjects_dir=subjects_dir
409+
)
406410

407411
##############################################################################
408412
# The remaining tutorials have *much more detail* on each of these topics (as

tutorials/intro/15_inplace.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222

2323
# %%
2424

25+
import sys
26+
2527
import mne
2628

2729
sample_data_folder = mne.datasets.sample.data_path()
@@ -83,9 +85,11 @@
8385
# we specified ``copy=True``:
8486

8587
# sphinx_gallery_thumbnail_number=2
86-
rereferenced_raw, ref_data = mne.set_eeg_reference(original_raw, ["EEG 003"], copy=True)
87-
fig_orig = original_raw.plot()
88-
fig_reref = rereferenced_raw.plot()
88+
rereferenced_raw, ref_data = mne.set_eeg_reference(original_raw, "EEG 003", copy=True)
89+
90+
if sys.platform != "emscripten":
91+
fig_orig = original_raw.plot()
92+
fig_reref = rereferenced_raw.plot()
8993

9094
# %%
9195
# Another example is the picking function `mne.pick_info`, which operates on

tutorials/intro/20_events_from_raw.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232

3333
# %%
3434

35+
import sys
36+
3537
import numpy as np
3638

3739
import mne
@@ -94,7 +96,8 @@
9496
# on newer systems it is more commonly ``STI101``. You can see the STIM
9597
# channels in the raw data file here:
9698

97-
raw.copy().pick(picks="stim").plot(start=3, duration=6)
99+
if sys.platform != "emscripten":
100+
raw.copy().pick(picks="stim").plot(start=3, duration=6)
98101

99102
# %%
100103
# You can see that ``STI 014`` (the summation channel) contains pulses of
@@ -265,7 +268,8 @@
265268
# Now, the annotations will appear automatically when plotting the raw data,
266269
# and will be color-coded by their label value:
267270

268-
raw.plot(start=5, duration=5)
271+
if sys.platform != "emscripten":
272+
raw.plot(start=5, duration=5)
269273

270274
# %%
271275
# .. _`chunk-duration`:

tutorials/intro/40_sensor_locations.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
# %%
1717

18+
import sys
1819
from pathlib import Path
1920

2021
import matplotlib.pyplot as plt
@@ -218,15 +219,16 @@
218219
# It is also possible to render an image of an MEG sensor helmet using 3D surface
219220
# rendering instead of matplotlib. This works by calling :func:`mne.viz.plot_alignment`:
220221

221-
fig = mne.viz.plot_alignment(
222-
sample_raw.info,
223-
dig=False,
224-
eeg=False,
225-
surfaces=[],
226-
meg=["helmet", "sensors"],
227-
coord_frame="meg",
228-
)
229-
mne.viz.set_3d_view(fig, azimuth=50, elevation=90, distance=0.5)
222+
if sys.platform != "emscripten":
223+
fig = mne.viz.plot_alignment(
224+
sample_raw.info,
225+
dig=False,
226+
eeg=False,
227+
surfaces=[],
228+
meg=["helmet", "sensors"],
229+
coord_frame="meg",
230+
)
231+
mne.viz.set_3d_view(fig, azimuth=50, elevation=90, distance=0.5)
230232

231233
# %%
232234
# Note that :func:`~mne.viz.plot_alignment` requires an `~mne.Info` object, and can also

tutorials/io/60_ctf_bst_auditory.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626

2727
# %%
2828

29+
import sys
30+
2931
import numpy as np
3032
import pandas as pd
3133

@@ -35,6 +37,13 @@
3537
from mne.io import read_raw_ctf
3638
from mne.minimum_norm import apply_inverse
3739

40+
if sys.platform == "emscripten":
41+
raise RuntimeError(
42+
"This tutorial requires the Brainstorm auditory dataset (~2.9 GB) "
43+
"which is not available in the browser. Please run this tutorial "
44+
"locally. Visit https://mne.tools for instructions."
45+
)
46+
3847
# %%
3948
# To reduce memory consumption and running time, some of the steps are
4049
# precomputed. To run everything from scratch change ``use_precomputed`` to

tutorials/io/70_reading_eyetracking_data.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,17 @@
8585
"""
8686

8787
# %%
88+
import sys
89+
8890
import mne
8991

92+
if sys.platform == "emscripten":
93+
raise RuntimeError(
94+
"This tutorial requires the MNE misc dataset with eyetracking data, "
95+
"which is not available in the browser. Please run this tutorial "
96+
"locally. Visit https://mne.tools for instructions."
97+
)
98+
9099
# %%
91100
fpath = mne.datasets.misc.data_path() / "eyetracking" / "eyelink"
92101
fname = fpath / "px_textpage_ws.asc"

0 commit comments

Comments
 (0)