From a3460b2621a0e59a9163263c58982e08ac1031ae Mon Sep 17 00:00:00 2001 From: PragnyaKhandelwal Date: Sun, 29 Mar 2026 11:00:14 +0530 Subject: [PATCH 1/6] VIZ: route evoked.plot() through MNELineFigure for default path When evoked.plot() creates its own figure (axes=None), now routes through _line_figure() to instantiate MNELineFigure instead of a plain matplotlib figure. This aligns with the 2D plotting figure-class refactor direction discussed in #7751. Behavior unchanged for custom axes. Both plot_white() and plot_joint() remain out of scope here. Adds regression test to assert MNELineFigure instantiation for default path. Closes #13747 --- doc/changes/dev/13747.newfeature.rst | 1 + mne/viz/_mpl_figure.py | 2 +- mne/viz/evoked.py | 17 +++++++++++++---- mne/viz/tests/test_evoked.py | 1 + 4 files changed, 16 insertions(+), 5 deletions(-) create mode 100644 doc/changes/dev/13747.newfeature.rst diff --git a/doc/changes/dev/13747.newfeature.rst b/doc/changes/dev/13747.newfeature.rst new file mode 100644 index 00000000000..fa885e7c9a3 --- /dev/null +++ b/doc/changes/dev/13747.newfeature.rst @@ -0,0 +1 @@ +Made ``evoked.plot()`` instantiate ``MNELineFigure`` when it creates its own figure, aligning this path with the ongoing 2D plotting figure-class refactor discussed in :gh:`7751`. diff --git a/mne/viz/_mpl_figure.py b/mne/viz/_mpl_figure.py index 47a26047768..4d33091e581 100644 --- a/mne/viz/_mpl_figure.py +++ b/mne/viz/_mpl_figure.py @@ -26,7 +26,7 @@ └ MNELineFigure Interactive figure for non-scrollable data. Generated by: - spectrum.plot() - - evoked.plot() TODO Not yet implemented + - evoked.plot() - evoked.plot_white() TODO Not yet implemented - evoked.plot_joint() TODO Not yet implemented """ diff --git a/mne/viz/evoked.py b/mne/viz/evoked.py index a62d2379f03..ecd029980cd 100644 --- a/mne/viz/evoked.py +++ b/mne/viz/evoked.py @@ -452,10 +452,19 @@ def _plot_evoked( fig = None if axes is None: - fig, axes = plt.subplots(len(ch_types_used), 1, layout="constrained") - if isinstance(axes, plt.Axes): - axes = [axes] - fig.set_size_inches(6.4, 2 + len(axes)) + if plot_type == "butterfly": + from ._mpl_figure import _line_figure + + fig, axes = _line_figure( + evoked, + picks=picks, + figsize=(6.4, 2 + len(ch_types_used)), + ) + else: + fig, axes = plt.subplots(len(ch_types_used), 1, layout="constrained") + if isinstance(axes, plt.Axes): + axes = [axes] + fig.set_size_inches(6.4, 2 + len(axes)) if isinstance(axes, plt.Axes): axes = [axes] diff --git a/mne/viz/tests/test_evoked.py b/mne/viz/tests/test_evoked.py index 9071bb8971c..8698cf525d8 100644 --- a/mne/viz/tests/test_evoked.py +++ b/mne/viz/tests/test_evoked.py @@ -126,6 +126,7 @@ def test_plot_evoked(): fig = evoked.plot( proj=True, hline=[1], exclude=[], window_title="foo", time_unit="s" ) + assert fig.__class__.__name__ == "MNELineFigure" amplitudes = _get_amplitudes(fig) assert len(amplitudes) == len(default_picks) assert evoked.proj is False From 83ba4caceb4324e4af70eab2cbd4dbdb6b6fbb44 Mon Sep 17 00:00:00 2001 From: PragnyaKhandelwal Date: Sun, 29 Mar 2026 13:01:54 +0530 Subject: [PATCH 2/6] MAINT: rename towncrier fragment for PR #13795 --- doc/changes/dev/{13747.newfeature.rst => 13795.newfeature.rst} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename doc/changes/dev/{13747.newfeature.rst => 13795.newfeature.rst} (100%) diff --git a/doc/changes/dev/13747.newfeature.rst b/doc/changes/dev/13795.newfeature.rst similarity index 100% rename from doc/changes/dev/13747.newfeature.rst rename to doc/changes/dev/13795.newfeature.rst From 18724394d6719fa563e1003822bb051611938c58 Mon Sep 17 00:00:00 2001 From: PragnyaKhandelwal Date: Sun, 29 Mar 2026 13:16:38 +0530 Subject: [PATCH 3/6] FIX: keep dipole plotting on legacy figure path --- mne/viz/evoked.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mne/viz/evoked.py b/mne/viz/evoked.py index ecd029980cd..92600884fb2 100644 --- a/mne/viz/evoked.py +++ b/mne/viz/evoked.py @@ -452,7 +452,7 @@ def _plot_evoked( fig = None if axes is None: - if plot_type == "butterfly": + if plot_type == "butterfly" and hasattr(evoked, "get_channel_types"): from ._mpl_figure import _line_figure fig, axes = _line_figure( From 375c3f6179efa1cfa6381a9bebe57db29b234140 Mon Sep 17 00:00:00 2001 From: PragnyaKhandelwal Date: Fri, 3 Apr 2026 21:12:23 +0530 Subject: [PATCH 4/6] MAINT: address review comments for #13795 --- doc/changes/dev/13795.newfeature.rst | 1 - doc/changes/dev/13795.other.rst | 1 + mne/viz/evoked.py | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) delete mode 100644 doc/changes/dev/13795.newfeature.rst create mode 100644 doc/changes/dev/13795.other.rst diff --git a/doc/changes/dev/13795.newfeature.rst b/doc/changes/dev/13795.newfeature.rst deleted file mode 100644 index fa885e7c9a3..00000000000 --- a/doc/changes/dev/13795.newfeature.rst +++ /dev/null @@ -1 +0,0 @@ -Made ``evoked.plot()`` instantiate ``MNELineFigure`` when it creates its own figure, aligning this path with the ongoing 2D plotting figure-class refactor discussed in :gh:`7751`. diff --git a/doc/changes/dev/13795.other.rst b/doc/changes/dev/13795.other.rst new file mode 100644 index 00000000000..5c07b2477b8 --- /dev/null +++ b/doc/changes/dev/13795.other.rst @@ -0,0 +1 @@ +Made :meth:`evoked.plot() ` instantiate ``MNELineFigure`` when it creates its own figure, aligning this path with the ongoing 2D plotting figure-class refactor discussed in :gh:`7751`, by `Pragnya Khandelwal`_. diff --git a/mne/viz/evoked.py b/mne/viz/evoked.py index 92600884fb2..ecd029980cd 100644 --- a/mne/viz/evoked.py +++ b/mne/viz/evoked.py @@ -452,7 +452,7 @@ def _plot_evoked( fig = None if axes is None: - if plot_type == "butterfly" and hasattr(evoked, "get_channel_types"): + if plot_type == "butterfly": from ._mpl_figure import _line_figure fig, axes = _line_figure( From d4420c553eeb7508e06a06652d165b86861d7f3b Mon Sep 17 00:00:00 2001 From: PragnyaKhandelwal Date: Fri, 3 Apr 2026 23:15:05 +0530 Subject: [PATCH 5/6] MAINT: keep _line_figure backward-compatible for DipoleFixed --- mne/viz/_mpl_figure.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mne/viz/_mpl_figure.py b/mne/viz/_mpl_figure.py index 4d33091e581..ac1b66a18b3 100644 --- a/mne/viz/_mpl_figure.py +++ b/mne/viz/_mpl_figure.py @@ -2378,7 +2378,10 @@ def _line_figure(inst, axes=None, picks=None, **kwargs): # if picks is None, only show data channels allowed_ch_types = _DATA_CH_TYPES_SPLIT if picks is None else _VALID_CHANNEL_TYPES # figure out expected number of axes - ch_types = np.array(inst.get_channel_types()) + try: + ch_types = np.array(inst.get_channel_types()) + except AttributeError: + ch_types = np.array(inst.info.get_channel_types()) if picks is not None: ch_types = ch_types[picks] n_axes = len(np.intersect1d(ch_types, allowed_ch_types)) From 17f67dd32ea79d5a196d0ff7844b5a93abff8d74 Mon Sep 17 00:00:00 2001 From: PragnyaKhandelwal Date: Tue, 7 Apr 2026 21:47:13 +0530 Subject: [PATCH 6/6] DOC: touch evoked tutorial to trigger rendered docs --- tutorials/evoked/10_evoked_overview.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tutorials/evoked/10_evoked_overview.py b/tutorials/evoked/10_evoked_overview.py index b251a1f8239..52d440c7f9f 100644 --- a/tutorials/evoked/10_evoked_overview.py +++ b/tutorials/evoked/10_evoked_overview.py @@ -71,7 +71,7 @@ # # We can visualize the average evoked response for left-auditory stimuli using # the :meth:`~mne.Evoked.plot` method, which yields a butterfly plot of each -# channel type: +# channel type (interactive butterfly figure): evoked.plot()