diff --git a/doc/changes/dev/13425.newfeature.rst b/doc/changes/dev/13425.newfeature.rst new file mode 100644 index 00000000000..28aa6f4c27f --- /dev/null +++ b/doc/changes/dev/13425.newfeature.rst @@ -0,0 +1 @@ +Added `annotation_regex` parameter to :func:`mne.viz.raw.plot_raw`, automatically hiding annotations not matching the regex, by :newcontrib: `Johannes Herforth`_ diff --git a/doc/changes/names.inc b/doc/changes/names.inc index 754299f8575..82484fb5004 100644 --- a/doc/changes/names.inc +++ b/doc/changes/names.inc @@ -142,6 +142,7 @@ .. _jeythekey: https://github.com/jeythekey .. _Joan Massich: https://github.com/massich .. _Johann Benerradi: https://github.com/HanBnrd +.. _Johannes Herforth: https://github.com/DerAndereJohannes .. _Johannes Niediek: https://github.com/jniediek .. _John Samuelsson: https://github.com/johnsam7 .. _John Veillette: https://psychology.uchicago.edu/directory/john-veillette diff --git a/mne/io/base.py b/mne/io/base.py index c5808726264..29852d38863 100644 --- a/mne/io/base.py +++ b/mne/io/base.py @@ -1911,6 +1911,8 @@ def plot( color=None, bad_color="lightgray", event_color="cyan", + *, + annotation_regex=".*", scalings=None, remove_dc=True, order=None, @@ -1934,7 +1936,6 @@ def plot( time_format="float", precompute=None, use_opengl=None, - *, picks=None, theme=None, overview_mode=None, @@ -1951,22 +1952,23 @@ def plot( color, bad_color, event_color, - scalings, - remove_dc, - order, - show_options, - title, - show, - block, - highpass, - lowpass, - filtorder, - clipping, - show_first_samp, - proj, - group_by, - butterfly, - decim, + annotation_regex=annotation_regex, + scalings=scalings, + remove_dc=remove_dc, + order=order, + show_options=show_options, + title=title, + show=show, + block=block, + highpass=highpass, + lowpass=lowpass, + filtorder=filtorder, + clipping=clipping, + show_first_samp=show_first_samp, + proj=proj, + group_by=group_by, + butterfly=butterfly, + decim=decim, noise_cov=noise_cov, event_id=event_id, show_scrollbars=show_scrollbars, diff --git a/mne/viz/_figure.py b/mne/viz/_figure.py index 2872a7621e2..1c93a258dff 100644 --- a/mne/viz/_figure.py +++ b/mne/viz/_figure.py @@ -6,6 +6,7 @@ import importlib import inspect +import re from abc import ABC, abstractmethod from collections import OrderedDict from contextlib import contextmanager @@ -182,7 +183,10 @@ def _setup_annotation_colors(self): segment_colors[key] = next(color_cycle) self.mne.annotation_segment_colors = segment_colors # init a couple other annotation-related variables - self.mne.visible_annotations = {label: True for label in labels} + annot_regex = re.compile(self.mne.annotation_regex) + self.mne.visible_annotations = { + label: True if annot_regex.findall(label) else False for label in labels + } self.mne.show_hide_annotation_checkboxes = None def _update_annotation_segments(self): diff --git a/mne/viz/raw.py b/mne/viz/raw.py index 1570d6c5390..fe67fa6ddaa 100644 --- a/mne/viz/raw.py +++ b/mne/viz/raw.py @@ -37,6 +37,8 @@ def plot_raw( color=None, bad_color="lightgray", event_color="cyan", + *, + annotation_regex=".*", scalings=None, remove_dc=True, order=None, @@ -61,7 +63,6 @@ def plot_raw( precompute=None, use_opengl=None, picks=None, - *, theme=None, overview_mode=None, splash=True, @@ -99,6 +100,9 @@ def plot_raw( Color to make bad channels. %(event_color)s Defaults to ``'cyan'``. + annotation_regex : str + A regex pattern applied to each annotation's label. + Matching labels remain visible, non-matching labels are hidden. %(scalings)s remove_dc : bool If True remove DC component when plotting data. @@ -373,6 +377,7 @@ def plot_raw( event_times=event_times, event_nums=event_nums, event_id_rev=event_id_rev, + annotation_regex=annotation_regex, # preprocessing projs=projs, projs_on=projs_on, diff --git a/mne/viz/tests/test_raw.py b/mne/viz/tests/test_raw.py index c7dae919fbc..3c6c74c93d6 100644 --- a/mne/viz/tests/test_raw.py +++ b/mne/viz/tests/test_raw.py @@ -803,6 +803,23 @@ def test_plot_annotations(raw, browser_backend): fig._toggle_single_channel_annotation(ch_pick, 0) assert fig.mne.inst.annotations.ch_names[0] == (ch_pick,) + # Check if annotation filtering works - All annotations + annot = Annotations([42, 50], [1, 1], ["test", "test2"], raw.info["meas_date"]) + with pytest.warns(RuntimeWarning, match="expanding outside"): + raw.set_annotations(annot) + + fig = raw.plot() + + assert fig.mne.visible_annotations["test"] and fig.mne.visible_annotations["test2"] + + # Check if annotation filtering works - filtering annotations + # This should only make test2 visible and hide test + fig = raw.plot(annotation_regex="2$") + + assert ( + not fig.mne.visible_annotations["test"] and fig.mne.visible_annotations["test2"] + ) + @pytest.mark.parametrize("active_annot_idx", (0, 1, 2)) def test_overlapping_annotation_deletion(raw, browser_backend, active_annot_idx):