| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| backend_name = backend_registry.resolve_gui_or_backend(newbackend)[0] | ||
| print(backend_name) | ||
| mod = importlib.import_module('matplotlib.backends.backend_' + backend_name) | ||
| if hasattr(mod, "Backend"): | ||
| orig_class = mod.Backend | ||
|
|
||
| else: | ||
| class orig_class(matplotlib.backend_bases._Backend): | ||
| locals().update(vars(mod)) |
There was a problem hiding this comment.
This is where I hacked, because mpl_gui was not consistent with the changes made by @ianthomas23 to the backend registry. I somewhat suspect a lot of this file could change, but am uncomfortable enough with all of this that I haven't tried yet. I can try, but maybe it's an easy skim by folks who understand this better.
Sorry, something went wrong.
|
Introducing a new entry point into the library is the most significant change ever done to the library - IMHO this is worth a major version bump to matplotlib 4. We must make sure that we are doing this correctly. Some general thoughts:
While I'm generally a proponent of incremental changes, I think we need to have a big picture here. I don't think adding mpl_gui alone to the core library will solve this. I'm ok with adding it if we see a technical need to better evaluate it's suitability. But I would cleary mark it as experimental. Maybe even some more catious wording, as I anticipate that the eventual solution will have a different API. It's rather "You may try it but it will most likely change" than "We think this is the correct solution, but reserve the right to change if we have overlooked something. On a side-note: It might be that there is an incremental API-backward compatible alternative approach. One could try to make pyplot lazier. That would need to go in two directions:
These are no easy topics, and some limitations of pyplot will still remain, but before changing the way every user interacts with matplotlib, we should at least have considered whether there are less invasive options and how they compare. |
Sorry, something went wrong.
|
@jklymak The latest commit in my branch at https://github.com/ianthomas23/matplotlib/commits/mpl-gui/ changes this to use the backend_registry for resolving and loading backends. If you are happy with it you can take it from there, or I could push to this branch if you'd prefer. The else block at https://github.com/jklymak/matplotlib/blob/11e357af31af63a965c064895d886c788f752378/lib/matplotlib/mpl_gui/_manage_backend.py#L130 might not be valid any more. It looks like it is intended to allow passing an actual Backend class rather than a string backend name which I don't believe is currently supported in pyplot.switch_backend so it may or may not work. We'd probably have to make extra changes to support ipython and jupyter backends. I can look at those in due course. There is some discussion about the failure of tk-based backends at matplotlib/mpl-gui#1. |
Sorry, something went wrong.
|
@ianthomas23 Thanks for the changes - pushed to this branch (BTW, I've not properly set the authoring bits on any of these, but should do so when done). |
Sorry, something went wrong.
|
I think this is a more incremental change than needing to bump to version 4 or anything like that. The user-facing API is exactly the same as the pyplot API, except it's not in pyplot, and it does not hold a registry of figures by default. (I'm actually not clear why @tacaswell added a registry, but it is not used by default). The only other change is setting the backend, which I agree we should make sure we get right, and frankly is a little confusing. For sure this should be marked experimental. Putting at the top level is an interesting idea. OTOH I think that could happen after a soft roll out a level down? I would advocate for moving this to the main library so it actually gets worked on and devs can start using it day-to-day. Currently it is not compatible with matplotlib main branch and hasn't really been looked at in 2 years.
You need to access fig to add colorbars and to save the figure; it's rare that I do not need fig. If you wanted a new method that didn't return the figure, I'd argue that should get a new name and is somewhat orthogonal to this PR. Overall I do not consider this to be changing the API at all, but just stripping the figures and axes out of the confusing pyplot registry and allowing us to have a "non-pyplot" experience but still have a GUI. |
Sorry, something went wrong.
|
This may be a case where you want to set the git commit details so you avoid the blame not get the credit 🤣 |
Sorry, something went wrong.
|
Sorry, something went wrong.
Is a very breaking change, even if mg has exactly the same API as plt. It will not invalidate existing code, but it will out-date basically every code and example. While people don't have to follow the recommendation and can still use pyplot, their code will not adhere to current best practices anymore, we'll have a mixture of old/new way and eventually people should upgrade to be in line with the recommended usage. There's no technical breakage, but a social/constistency pressure to change your code. I want to do this mess only once to move away from pyplot (and still slightly question whether it's worth it). I don't want to come up with yet a better way to use matplotlib in 2 or 3 years and revise the recommendation again. I therefore say it's ok to add mpl_gui to the main library, but should not recommend its usage until we're really sure this is the API of the future. "You can try it, but it will likely change". |
Sorry, something went wrong.
A typical example is fig, ax = plt.subplots() # do stuff on ax plt.show() and fig is never used (check our examples how often that happens. I'm not weeded to ax = subplots() plus ax.figure - there other approaches as well. I just claim it's somewhat cumbersome to always have the two concepts as separate variables. Note that, e.g. neither bokeh nor plotly force you to define and carry around two variables. They have different interface concepts - and I mean interface the logical structure of Figure and Axes will remain, but we may want to create / access them differently. This of course is a separte topic and PR. But when moving away from pyplot (which means people have to touch their code), it is reasonable to recheck whether we can have a better interface. As said above, I don't want to force people to change their way of working more than once concering the high-level API. |
Sorry, something went wrong.
|
I would advocate that the API for mpl_gui and pyplot should be as close as possible, and should not drift apart. If that is the plan, then all we are doing is gently suggesting folks change import matplotlib.pyplot as plt to import matplotlib.gui as mgui (or just use mpl.figure()) if they want a stateless experience. |
Sorry, something went wrong.
|
I've understand this and it's a valid approach. Though I think we may give away optimization potential if we just copy the pyplot API (does supporting a stateless way-of-working justify a new entry point into the library / is that separation necessary?). - If the API stays, my current reverse attempt in #29849 is to make the state less intrusive. That would allow a quasi-stateless usage of pyplot, which has the big advantage that people don't have to change the imports. They could use pyplot as if it was stateful or stateless as they see fit. |
Sorry, something went wrong.
|
I think the advantage is a clear separation of concerns. pyplot, and keeping track of figures really doesn't have anything to do with creating and showing them. Indeed, a suggested follow up would be to have pyplot just use mpl_gui (or whatever we call it) to create the figures, and pyplot would be higher level and just controls the registry and state tracking. This change also has clear documentation advantages - the creation of the gui (or non-interactive backend) can be explained properly, separately from the state-based workflow. People looking at example code and asking "what does this pyplot module do"? are met with a whole bunch of stuff 98% of which is not relevant. Asking "what does this mpl_gui module do?" we can just say that it creates figures and attaches them to GUIs. |
Sorry, something went wrong.
I totally agree on a technical level, and yes, internally pyplot should likely be using something like mpl_gui. But I disagree for the API: What is better for the user than separated concerns? - No concerns. Users don't care about "attaching to GUIs" or "state tracking". The just want to create a figure, add content, save it and/or show it. If we can make this just work by rewiring in the background as needed, that's much better better than untangling and teaching the the differences between different approaches. - That's also part of my reservation wrt. mpl_gui: It's a specific interface/workflow for creating figures that are attached to a GUI. But what if you don't need a GUI? Do you go back to pyplot? Or do we teach the third approach using standalone Figure()? |
Sorry, something went wrong.
I'd argue you still have the existing documentation problem - "Q: What is this pyplot?" "A: It's a whole bunch of stuff, but we recommend you ignore 99% of it". (I'd also argue that untangling things is generally a good idea. )
mpl_gui.figure() doesn't attach to a gui on creation. Backend resolution doesn't happen until mpl_gui.show() (https://github.com/matplotlib/matplotlib/pull/29836/files#diff-07c2c7f1663e7eaea6b68718dc16f155be84e25e64b73faa629b35d0be7134f6). So the way mpl_gui is currently: import matplotlib.gui as mgui
fig, axs = mgui.subplot_mosaic([['a', 'b'], ['c', 'c']])
fig.savefig('boo.pdf')does not spinup a GUI - its just a passthrough to Figure() Another approach is to move everything in _creation.py to the top level and then only use mpl_gui if needed: import matplotlib as mpl
import matplotlib.gui as mgui
fig, axs = mpl.subplot_mosaic([['a', 'b'], ['c', 'c']])
...
mgui.show(fig)The only issue there is that we now need two imports. |
Sorry, something went wrong.
There is no way you can define the documentation problem away. Even if we recommend something new, pyplot itself and it's API are here to stay. This also means your documentation problem persists and is only slightly rephrased. Instead of the above, you can say "It's a whole bunch of stuff, but we recmommend you to ignore 100% of it and use this other interface." And the question "What is this pyplot" or "Why do you have pyplot and this other thin mpl_gui" will come stronger when you introduce mpl_gui as a dedicated interface. Right now people have mostly accepted that pyplot is the entry point for creating plots, even if they don't understand all the technical implications behind this. As said, introducing mpl_gui is a valid approach, but it is a major disruption. And therefore, it's equally valid to check which other approaches exist and what advantages disadvantages they have.
Ok, accepted. It's just a naming thing and mpl_gui has a broader scope than GUIs, so we should name it differently.
Then make fig.show() cleverer, trivially def show(self):
mgui.show(self)
(it may be slightly more complicated than this, but AFAICS nothing that cannot be done). |
Sorry, something went wrong.
I think an extra import just to show things will frustrate most of our users and outright infuriate the crankier ones and so it really should be done if possible.
Did that for a PyCon tutorial and it's in a lot of ways cleaner b/c it's pure OO. I'd probably do it as a tutorial of "here's all the nice things you get by using this interface"
Fully expect most users to stick w/ the pyplot interface unless things stop working, so having this be in it's own namespace while experimental and eventually shimming mpl_gui under pyplot is likely the best bet for getting folks to actually use it if it's the better technical choice. |
Sorry, something went wrong.
|
I agree that making fig.show() do the right thing seems like a nice approach and could avoid the extra import. Whether we can do that cleanly would take some experimentation. |
Sorry, something went wrong.
Then you may like creating your Figure fully OO and only registering with pyplot to show it 😀: #29855 |
Sorry, something went wrong.
|
Just a comment from the peanut gallery: plt.show(fig) or fig.show() have been discussed elsewhere; there are some issues with blockingness if you have more than one figure (you often want to block on the last show only). The approach you propose in the other PR (plt.figure(fig); plt.show()) has the advantage that registering multiple figures and showing them all at once "should just work". (Well, perhaps an API like plt.figures([fig1, fig2]) would be nice too but that's less important.) |
Sorry, something went wrong.
|
plt.show(fig) could be expanded to support plt.show([fig1, fig2]) and thus prevent the blocking issue. The idea of fig.show() basically is to make showing not depend on an external context, but attach it to the figure to be shown, but that reasonable only works as long as you show single figures. You could have a fig.show(block=False) with patterns fig1 = Figure() ... fig1.show(block=False) fig2 = Figure() ... fig2.show() and for data in datasets:
fig = Figure()
...
fig.show(block=False)
fig.show() # re-show the last figure and block
It's all not very nice (a) because of the asymmetry "not block on the last", (b) because the method would affect other instances. I'd rule that out. Funamentally, if you want to show multiple figures at once, that function has to come from namespace outside the figure instance. |
Sorry, something went wrong.
|
Ok now that this came up, I'm pretty sure that this is why @tacaswell added a registry to mpl_gui. I think you have the same issue with Jupyter needing to be able to show the figures that were created in a cell, and Jupyter doesn't require you to use plt.show(). This is all disturbingly like the existing pyplot. I'd still argue it is nicer to separate it from pyplot. |
Sorry, something went wrong.
|
Btw I wouldn't be strongly against explicitly asking users to call fig.show(). The blocking asymmetry is a bit of a nuisance for interactive use but not a problem for notebook use. For interactive/script use showing the last figure a second time with a block isn't terrible. Nor is fig.show(list_of_figs) at the end of a loop. But they are relatively large breaks from the current behaviour |
Sorry, something went wrong.
Speaking of which, Jupyter relies on the pyplot state to show the figures at the end of the cell. If we move away from pyplot, we need a story how to handle the Jupyter use case. |
Sorry, something went wrong.
|
I admit that I don't understand at all what Jupyter and ipympl do under the hood, but import matplotlib.mpl_gui as mg %matplotlib ipympl fig, ax = mg.subplots() ax.plot(range(10)) fig, ax = mg.subplots() ax.plot(range(20)) work fine with this PR. import matplotlib.mpl_gui as mg %matplotlib inline ... does not, but it's just NonGuiException: FigureCanvasAgg is non-interactive, and thus cannot be shown so I think it is just something that needs to be set properly in mpl_gui._promotion.py |
Sorry, something went wrong.
|
The integration is done through https://github.com/ipython/matplotlib-inline. Not sure how accurate it still is, but ipython/ipython#12190 gives some idea on the mechanics in the background. |
Sorry, something went wrong.
|
Just throwing it out there, but a possible name I've had in mind is
"figlet".
…On Wed, Apr 2, 2025 at 8:15 PM Tim Hoffmann ***@***.***> wrote:
The integration is done through
https://github.com/ipython/matplotlib-inline.
Not how accurate it still is, but ipython/ipython#12190
<ipython/ipython#12190> gives some idea on the
mechanics in the background.
—
Reply to this email directly, view it on GitHub
<#29836 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AACHF6ESD2J2BMZGRGLYMNL2XR4TXAVCNFSM6AAAAAB2C75WH6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDONZUGAZDKOBRGU>
.
You are receiving this because you are subscribed to this thread.Message
ID: ***@***.***>
[image: timhoffm]*timhoffm* left a comment (matplotlib/matplotlib#29836)
<#29836 (comment)>
The integration is done through
https://github.com/ipython/matplotlib-inline.
Not how accurate it still is, but ipython/ipython#12190
<ipython/ipython#12190> gives some idea on the
mechanics in the background.
—
Reply to this email directly, view it on GitHub
<#29836 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AACHF6ESD2J2BMZGRGLYMNL2XR4TXAVCNFSM6AAAAAB2C75WH6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDONZUGAZDKOBRGU>
.
You are receiving this because you are subscribed to this thread.Message
ID: ***@***.***>
|
Sorry, something went wrong.
|
Throwing in some more loosely related ideas: (1) It's also an option to have an abbreviated submodule name so that on could do from matplotlib import mg instead of import matplotlib.mpl_gui as mg. This saves some typing (I personally find import matplotlib.pyplot as plt always annoyingly long). It's still reasonably clear for usage because the import comes from the matplotlib namespace. (2) An option for the "grouped" show issue could be: Use fig.show() to blocking show single figures. Use a context manager to blocking show multiple figures with collective_show(): fig1.show() ... fig2.show() (3) Not sure whether it's helpful for the management of state, but a module itself can be made a context manager. So in theory we could do with plt: or with matplotlib:. |
Sorry, something went wrong.
|
OK, here is my stab at our options. They are somewhat annoyingly byzantine but so are our ways of using the library: Notes mpl_guiCurrent statefig, ax = plt.subplots() # and friends
Pros:
Cons:
fig = matplotlib.figure.Figure()
Pros:
Cons:
Proposed changesmpl_gui
Pros:
Cons:
mpl_gui.registry
Pros:
Cons:
mpl_gui.FigureContextThis is between the two extremes, where there is no global registry, but a registry is maintained for a series of plots within a context: with mg.FigureContext() as fc:
fig, ax = fc.subplots()
fig, ax = fc.subplot_mosaic('AA\nBC')
fig = fc.figure
makes three figures and shows them in a blocking manner, and then removes the registry on completion. Top-level import?The question arose as to whether these should be top level imports eg fig, ax = mpl.subplots() A choice would need to be made as to whether that is the registry or non-registry version of the new interface. |
Sorry, something went wrong.
|
Thanks for summarizing. Would it make sense to create a formal enhancement proposal with this? I feel there are a lot of aspects to consider that are hard to keep track of in discussions. It would make sense to collaboratively define
Closing is only necessary if you don't show. More precisely: blocking show deregisters on window close. It seems the show-at-end-of-cell also deregisters with matplotlib inline. So even if you're primary intention in a notebook is only savefig, unless you take extra effort, the figure is shown at the end of the cell and then cleaned up. Therefore, collecting unclosed figures is not a serious problem in notebooks.
Is the current state but promotion could be implemented #29855
I assume, if we really want we can make fig.show() call into pyplot/mpl_gui to make this work. My impression of mpl_gui is: We start with a clean approach, but then realize that this has limitations (mg.show() doesn't know what figures to show) and therefore we add more API mpl_gui.registry / mpl_gui.FigureContext to reintroduce global state, which is closer again to pyplot. It may also help to distinguish the kinds of global state: There the "current figure" implicit state. That alone could be simple to manage - Imagine a pyplot-like interface that only supports one figure at a time. That would allow simple plt.plot(); plt.title(); plt.show() operations without holding explicit object references. Whenever a new figure is created that gets "current" but the previous one is no longer tracked. Such an interface does not suffer from memory buildup. The problem comes in with long-lived global state through collecting multple figures for a collective show(). mpl_gui.registry / mpl_gui.FigureContext solve this through introducing additional explicit API. Fundamentally, we could attach this API also to pyplot and let pyplot not track figures indefinitely by default. Wether that's a good idea in terms of breaking backward compatibility for some use cases would need to be discussed. But introducing the whole new mpl_gui API, including possibly at least one of mpl_gui.registry / mpl_gui.FigureContext is also a major impact in terms of larger API surface and relearning for all usage scenarios. |
Sorry, something went wrong.
Thats for inline only. ipympl keeps the figure around. |
Sorry, something went wrong.
What would be the best venue for this? Happy to move somewhere else. We haven't done a MEP for over a decade I don't think. I'm not sure what the best way to discuss something is, other than quoting and replying. Could put the note above into a markdown file that folks could comment on individual parts which may be the same thing as putting in a MEP |
Sorry, something went wrong.
Ok good to know. These details need to be spelled out to get a complete picture. In fact, I suspect ipympl is like a non-blocking show and by design you must have all figures of the notebook around to keep them responsive. Though it might be possible to deallocate figures when the respective output cell is cleared (feels similar to a "window close"). Possibly that's on the ipympl side to implement.
That sounds reasonable. Given the complexity of the topic I would like to collaboratively create a document on it, which in the end should contain our shared perspective. It's easier to use PRs with suggestions compared to a discussion thread. I was thinking MEP because that's a reasonable place but I'm fine with any file on any repo/branch. |
Sorry, something went wrong.
|
The notes above are now at matplotlib/mpl-gui#21 |
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
This hoists @tacaswell mpl-gui (https://github.com/matplotlib/mpl-gui) into the main library.
Why versus a standalone library? First there are some hacks that I suspect could be simplified substantially if it was part of main librabr. Second, it would be great to move away from pyplot over all and this would allow us to do so in the documentation.