Skip to content

Conversation

@mohit-malpote
Copy link

Here is a perfect description for your Pull Request (PR) based on the fix you implemented, filling out the expected MNE-Python layout:

Reference issue (if any)
Fixes #13492.

What does this implement/fix?
This PR fixes a plotting regression where the Y-axis spine (the vertical line) was drawn behind the plotted data lines, causing it to be obscured, especially by the Global Field Power (GFP) or channel traces in butterfly plots.

The fix involves explicitly setting a high Matplotlib zorder for the left axis spine in mne.viz._plot_lines:

A single line was added: ax.spines['left'].set_zorder(10)

This ensures the spine is always rendered on top of the data lines, which typically have a default zorder of 1-3.

@scott-huberty
Copy link
Contributor

scott-huberty commented Nov 22, 2025

Hey @mohit-malpote I dont think this fixes the issue? :

Screenshot 2025-11-21 at 9 00 33 PM

I ran this tutorial: https://mne.tools/stable/auto_tutorials/evoked/30_eeg_erp.html#sphx-glr-auto-tutorials-evoked-30-eeg-erp-py

By running this command from the mne-python repository root directory (after checking out this PR's branch):

ipython -i tutorials/evoked/30_eeg_erp.py

# Put back the y limits as fill_betweenx messes them up
ax.set_ylim(this_ylim)

ax.spines["left"].set_zorder(10)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think something like this should work.

ax.spines[:].set_zorder(max(l.get_zorder() for l in ax.get_lines()) + 1)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok @mistraube i will try to do that

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried ur solution but the result was the same

image

@mohit-malpote mohit-malpote force-pushed the fix/zorder-yaxis-plot-evoked-13492 branch from 1934fbf to ee31dd3 Compare November 22, 2025 16:24
@mistraube
Copy link
Contributor

I have made a final push to the branch using a fixed, high z-order (50) for the left axis spine, as the dynamic z-order (i.e. solution of @mistraube ) calculation was also unsuccessful.

The plot still fails the visual check on my end (the spine remains hidden behind the traces). This indicates that a final axis styling function, likely run outside of the primary plotting loop, is overriding the zorder property.

Is your screenshot from the try with fixed zorder 50? Would make sence to me since only some (of the 59) channels are plotted above the axis.

For me this works:

ax.spines[:].set_zorder(max(l.get_zorder() for l in ax.get_lines()) + 1)
Bildschirmfoto_20251122_190245

@mohit-malpote mohit-malpote force-pushed the fix/zorder-yaxis-plot-evoked-13492 branch from e60e1fc to d560363 Compare November 23, 2025 05:19
@mohit-malpote
Copy link
Author

mohit-malpote commented Nov 23, 2025

I have verified the fix locally against the tutorial, and it is now working correctly! The vertical Y-axis spine is clearly visible on top of all the data traces.

I have pushed the final commit implementing the robust z-order fix.

Thanks again to @mistraube for providing the key insight for the dynamic zorder calculation!

Screenshot from 2025-11-23 10-53-30

Copy link
Contributor

@scott-huberty scott-huberty left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @mohit-malpote - If you create an account on https://circleci.com (you can sign in with your GitHub account), then those failing CI's might pass. Right now they are failing because as a precaution, we don't let CircleCI CI's run if the user opening the PR does not have a CircleCI account.

We also need you to add a change log entry. Check out our contributing guide to learn how to do this!

Comment on lines +811 to +812
ax.spines[:].set_zorder(max(l.get_zorder() for l in ax.get_lines()) + 1)

Copy link
Contributor

@scott-huberty scott-huberty Nov 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
ax.spines[:].set_zorder(max(l.get_zorder() for l in ax.get_lines()) + 1)
# Ensure the axis spines are drawn above all Line2D artists
max_zorder = max((line.get_zorder() for line in ax.get_lines()), default=0) + 1
ax.spines[:].set_zorder(max_zorder)
  1. Locally, Ruff complains about l being an ambiguous variable name (and I agree).
  2. I split @mistraube 's suggestion into 2 lines to make the code a little more verbose, and added a code comment as hint to other devs.
  3. the max(..., default=0) is just a little defensive programming to guard against a case where there are no Line2D artists to iterate through, which could throw an error. We may never actually ever hit that case in the wild, but better safe than sorry.

@mohit-malpote is it clear for you why @mistraube 's approach is preferable to hard coding a z-order like 10 or even 50? The upshot is that each of these Line2D artists correspond to one channel on the plot. A single, hardcoded z-order won't work for all use-cases (Some EEG nets have 256 or more channels!).. So computing the maximum z-order dynamically is the way to go.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

y-axis spine z-order too low in plot_evoked

3 participants