Skip to content
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

BUG: modifications through matplotlib engine cannot be properly displayed #4774

Closed
xshaokun opened this issue Jan 1, 2024 · 5 comments · Fixed by #4776
Closed

BUG: modifications through matplotlib engine cannot be properly displayed #4774

xshaokun opened this issue Jan 1, 2024 · 5 comments · Fixed by #4776
Milestone

Comments

@xshaokun
Copy link

xshaokun commented Jan 1, 2024

Bug report

Bug summary

The Modifications through Matplotlib engine cannot be properly displayed.

Taking the following code for example, the expected modifications can only be shown by the containing matplotlib figure object like fig.savefig("sloshing.png").

Code for reproduction

adapted from docs (also broken there)

import numpy as np

import yt

# Load the dataset.
ds = yt.load("GasSloshing/sloshing_nomag2_hdf5_plt_cnt_0150")

# Create a slice object
slc = yt.SlicePlot(ds, "x", ("gas", "density"), width=(800.0, "kpc"))

# Get a reference to the matplotlib axes object for the plot
ax = slc.plots[("gas", "density")].axes

# Let's adjust the x axis tick labels
for label in ax.xaxis.get_ticklabels():
    label.set_color("red")
    label.set_fontsize(16)

# Get a reference to the matplotlib figure object for the plot
fig = slc.plots[("gas", "density")].figure

# And create a mini-panel of a gaussian histogram inside the plot
rect = (0.2, 0.2, 0.2, 0.2)
new_ax = fig.add_axes(rect)

n, bins, patches = new_ax.hist(
    np.random.randn(1000) + 20, 50, facecolor="black", edgecolor="black"
)

# Make sure its visible
new_ax.tick_params(colors="white")

# And label it
la = new_ax.set_xlabel("Dinosaurs per furlong")
la.set_color("white")

slc.save()

Actual outcome

iTerm2 1b3woO sloshing_nomag2_hdf5_plt_cnt_0150_Slice_x_density

Expected outcome

The changes of the x-axis tick labels
iTerm2 h1XCIV sloshing

Version Information

  • Operating System: MacOS 14.1.1 and Red Hat Enterprise Linux Server release 7.8 (Maipo)
  • Python Version: 3.9
  • yt version: 4.2.1 and 4.3.0
@neutrinoceros
Copy link
Member

hi @xshaokun , thanks for noticing !

I think what's happening is that the image (and most of the plot) is lazy-generated at some point ™️ between calls to MPL API and .save(), which invalidates everything done by the script.
It's trivially fixed by forcing rendering at a controlled point ahead of MPL API calls with slc.render(), though I wonder why this wasn't necessary when this example was written.

@xshaokun
Copy link
Author

xshaokun commented Jan 2, 2024

Thank @neutrinoceros for your reply!

It's trivially fixed by forcing rendering at a controlled point ahead of MPL API calls with slc.render(), though I wonder why this wasn't necessary when this example was written.

It can produce an expected output in docs. However, if some plot window methods are called, it will break again. For example, methods like slc.set_origin("lower-center-window") or slc.annotate_timestamp() will make slc.save() and fig.savefig("sloshing.png") giving different results. Output from the former only applied the modifications by plot_window methods, while output from the latter only applied the modifications by matplotlib engine, even if slc.render() is used (actually it will overwrite the modifications from matplotlib engine).

It seems like I was modifying different objects with different approaches. Does it make sense?

@xshaokun
Copy link
Author

xshaokun commented Jan 2, 2024

Ah I missed what you said "ahead of MPL API". Now I got it how it works, thanks! @neutrinoceros

@neutrinoceros
Copy link
Member

I think what you described is an unfortunate but known limitation of yt's callback system: in general, using yt API to modify a plot has, by design, no immediate effect, and most computations and IO operations are delayed until the figure is rendered (in a REPL, or when render or save are called). This system helps a lot with performance, however it works by redrawing the entire figure each time a render is needed, which means that any modification done directly with MPL (bypassing yt) will be erased on the next render.
I never could find a nice way to better support this usage, but if we could better integrate with MPL API, it'd make part of yt's completely redundant and lead to great simplifications.

@xshaokun
Copy link
Author

xshaokun commented Jan 2, 2024

I'm okay with the current design as soon as I know what's happening inside the box. I have to dive into the source code to figure out that render will erase old axes, which should be told explicitly in docs so that users can use MPL API properly. Many thanks for your help!

@neutrinoceros neutrinoceros added this to the 4.3.1 milestone Jan 2, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants