MEP27: Decouple pyplot from backends#
Status#
Progress
Branches and Pull requests#
Main PR (including GTK3):
Backend specific branch diffs:
Abstract#
This MEP refactors the backends to give a more structured andconsistent API, removing generic code and consolidate existing code.To do this we propose splitting:
FigureManagerBase
and its derived classes into the corefunctionality classFigureManager
and a backend specific classWindowBase
andShowBase
and its derived classes intoGcf.show_all
andMainLoopBase
.
Detailed description#
This MEP aims to consolidate the backends API into one single uniformAPI, removing generic code out of the backend (which includes_pylab_helpers
andGcf
), and push code to a more appropriatelevel in matplotlib. With this we automatically removeinconsistencies that appear in the backends, such asFigureManagerBase.resize(w,h)
which sometimes sets the canvas,and other times set the entire window to the dimensions given,depending on the backend.
Two main places for generic code appear in the classes derived fromFigureManagerBase
andShowBase
.
FigureManagerBase
hasthree jobs at the moment:The documentation describes it as aHelper class for pyplotmode, wraps everything up into a neat bundle
But it doesn't just wrap the canvas and toolbar, it also doesall of the windowing tasks itself. The conflation of these twotasks gets seen the best in the following line:
self.set_window_title("Figure%d"%num)
This combinesbackend specific codeself.set_window_title(title)
withmatplotlib generic codetitle="Figure%d"%num
.Currently the backend specific subclass of
FigureManager
decides when to end the mainloop. This also seems very wrongas the figure should have no control over the other figures.
ShowBase
has two jobs:It has the job of going through all figure managers registeredin
_pylab_helpers.Gcf
and telling them to show themselves.And secondly it has the job of performing the backend specific
mainloop
to block the main programme and thus keep thefigures from dying.
Implementation#
The description of this MEP gives us most of the solution:
To remove the windowing aspect out of
FigureManagerBase
lettingit simply wrap this new class along with the other backend classes.Create a newWindowBase
class that can handle thisfunctionality, with pass-through methods (:arrow_right:) toWindowBase
. Classes that subclassWindowBase
should alsosubclass the GUI specific window class to ensure backwardcompatibility (manager.window==manager.window
).Refactor the mainloop of
ShowBase
intoMainLoopBase
, whichencapsulates the end of the loop as well. We give an instance ofMainLoop
toFigureManager
as a key unlock the exit method(requiring all keys returned before the loop can die). Note thisopens the possibility for multiple backends to run concurrently.Now that
FigureManagerBase
has no backend specifics in it, torename it toFigureManager
, and move to a new filebackend_managers.py
noting that:This allows us to break up the conversion of backends intoseparate PRs as we can keep the existing
FigureManagerBase
class and its dependencies intact.And this also anticipates MEP22 where the new
NavigationBase
has morphed into a backend independentToolManager
.
FigureManagerBase(canvas, num) | FigureManager(figure, num) |
| Notes |
---|---|---|---|
show | show | ||
destroy | calls destroy on allcomponents | destroy | |
full_screen_toggle | handles logic | set_fullscreen | |
resize | resize | ||
key_press | key_press | ||
get_window_title | get_window_title | ||
set_window_title | set_window_title | ||
_get_toolbar | A common method to allsubclasses of FigureManagerBase | ||
set_default_size | |||
add_element_to_window |
ShowBase | MainLoopBase | Notes |
---|---|---|
mainloop | begin | |
end | Gets calledautomagicallywhen no moreinstances ofthe subclassexist | |
__call__ | Method movedtoGcf.show_all |
Future compatibility#
As eluded to above when discussing MEP 22, this refactor makes it easyto add in new generic features. At the moment, MEP 22 has to makeugly hacks to each class extending fromFigureManagerBase
. Withthis code, this only needs to get made in the singleFigureManager
class. This also makes the later deprecation ofNavigationToolbar2
very straightforward, only needing to touch thesingleFigureManager
class
MEP 23 makes for another use case where this refactored code will comein very handy.
Backward compatibility#
As we leave all backend code intact, only adding missing methods toexisting classes, this should work seamlessly for all use cases. Theonly difference will lie for backends that usedFigureManager.resize
to resize the canvas and not the window, dueto the standardisation of the API.
I would envision that the classes made obsolete by this refactor getdeprecated and removed on the same timetable asNavigationToolbar2
, also note that the change in call signature totheFigureCanvasWx
constructor, while backward compatible, I thinkthe old (imho ugly style) signature should get deprecated and removedin the same manner as everything else.
backend | manager.resize(w,h) | Extra |
---|---|---|
gtk3 | window | |
Tk | canvas | |
Qt | window | |
Wx | canvas | FigureManagerWx had |
Alternatives#
If there were any alternative solutions to solving the same problem,they should be discussed here, along with a justification for thechosen approach.
Questions#
Mdehoon: Can you elaborate on how to run multiple backendsconcurrently?
OceanWolf: @mdehoon, as I say, not for this MEP, but I see this MEPopens it up as a future possibility. Basically theMainLoopBase
class acts a per backend Gcf, in this MEP it tracks the number offigures open per backend, and manages the mainloops for thosebackends. It closes the backend specific mainloop when it detectsthat no figures remain open for that backend. Because of this Iimagine that with only a small amount of tweaking that we can dofull-multi-backend matplotlib. No idea yet why one would want to, butI leave the possibility there in MainLoopBase. With all thebackend-code specifics refactored out ofFigureManager
also aidsin this, one manager to rule them (the backends) all.
Mdehoon: @OceanWolf, OK, thanks for the explanation. Having a uniformAPI for the backends is very important for the maintainability ofmatplotlib. I think this MEP is a step in the right direction.