Skip to content

[Bug]: Pyplot can no longer set axes properties #27953

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
crisluengo opened this issue Mar 20, 2024 · 10 comments · Fixed by #27955
Closed

[Bug]: Pyplot can no longer set axes properties #27953

crisluengo opened this issue Mar 20, 2024 · 10 comments · Fixed by #27955
Milestone

Comments

@crisluengo
Copy link

crisluengo commented Mar 20, 2024

Bug summary

Since Matplotlib 3.8.1, it no longer is possible to set axes properties, neither using Pyplot free functions nor Axis object methods.

This persists in the current latest release 3.8.3, and is not observed in 3.8.0.

Code for reproduction

import matplotlib.pyplot as plt
import numpy as np
plt.plot(np.random.randn(100))
plt.xlabel("x label")
plt.show()

Actual outcome

The "x label" text is not shown. The result is identical to just calling plt.plot(...) and plt.show().

Expected outcome

The "x label" text is shown.

Additional information

The above example applies to all functions that set axes properties.

I have tried in many different ways to create a figure with axes, none of them resulted in me being able to set properties.

Operating system

macOS 14.3.1 (aarch64)

Matplotlib Version

3.8.1

Matplotlib Backend

MacOSX

Python version

3.11.8

Jupyter version

No response

Installation

pip

@ksunden
Copy link
Member

ksunden commented Mar 20, 2024

test

This appears to be working to me, the xlabel is shown at the bottom.

@crisluengo
Copy link
Author

@ksunden Are you on macOS with an Apple Silicon chip?

I have two such machines (one personal one from work) and this issue is seen on both.

@ksunden
Copy link
Member

ksunden commented Mar 20, 2024

Ahh... I see what is happening...

The properties are set, but a draw has not been triggered...

So if you resize the window, or interact with the data it shows up, but just not immediately...

We can look into it for a more permanent fix, but workarounds include (partly including as information for whoever digs in further into this, which might be myself):

  • Using interactive mode as on (plt.ion())
  • Calling plt.draw() prior to plt.show()
  • Interacting with the figure in any way that causes a draw
  • Saving the figure (including via the ui elements)

All of these cause the label to show up properly, and depending on usecase, may be enough... but we should in fact fix it working without additional action.

It is tied to the MacOS backend I'm pretty sure. @greglucas any particular thoughts?

@ksunden
Copy link
Member

ksunden commented Mar 20, 2024

(Also using a backend other than MacOS e.g. QtAgg or TkAgg)

@greglucas
Copy link
Contributor

I can't reproduce this on my intel mac, so it might be another M1 specific issue.

Looking at the diff between 3.8.0 and 3.8.1 the only updates to the macosx backend were in the interrupt handling.
v3.8.0...v3.8.1#diff-fa0b9840f8945ba1ad40628be14617bd8d2f06a3e8c1e2791e5d8e60799488e0

#27221
#26970

Maybe we are sending the draw request before the interrupts are set up to interrupt the show? Do we also get the issue on latest main?

@ksunden
Copy link
Member

ksunden commented Mar 20, 2024

Yes, we do see it on main.

3.8.1 had segfaults on resize, too (#27262).

In general, interactions with both CPython and MacOS itself have not been kind to the 3.8 series of Matplotlib MacOS backend (Also seen reports of problems in Java as well...)

@ksunden
Copy link
Member

ksunden commented Mar 20, 2024

Okay, I think it is relevant here that this only applies in a REPL, not running from a file

@tacaswell
Copy link
Member

My guess here is that draw_idle is working such that the first time it is called it actually draws and the second time it holds onto that cached image which is what it then actually puts on the screen.

@ksunden
Copy link
Member

ksunden commented Mar 20, 2024

Stale is set to True when it is shown, but nothing actually triggers a second draw to happen:

Adding

if self.canvas.figure.stale:
    self.canvas.draw_idle()

to the show method here seems to fix it.

I'm slightly weary of how far I'm reaching in to get the stale info, could be convinced to draw unconditionally.

In looking over this with @QuLogic it seems like perhaps there is a wider-spread potential for similar problems, but we are saved by most gui toolkits doing resize events when shown (which indirectly cause draws). MacOS seems to only give a resize event when the window is created, not shown. For now, I'm inclined to put those lines in MacOS specifically and leave ourselves with this comment chain and a summary on the PR for if we see similar problems in the future. One thought was to put it in the backend_bases version of show, but many (including macos) do not seem to currently call super so that won't actually fix the problem.

Other fun facts about this:
We do not get the problem in ipython when run as a single cell, but do get it if we run each item in their own cell.

@tacaswell
Copy link
Member

We do not get the problem in ipython when run as a single cell, but do get it if we run each item in their own cell.

This makes sense because when you run it in seperate cells the UI event loop gets to run and processes the resize-on-create on its side and then push it through our stack and then you call show (to get the stale image). When you run it in one the UI event loop does not get run until all of our code has run and hence we are saved.

@QuLogic QuLogic added this to the v3.8.4 milestone Apr 3, 2024
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 a pull request may close this issue.

5 participants