doc/devel/MEP/MEP27.rst
.. contents:: :local:
Progress
Main PR (including GTK3):
Backend specific branch diffs:
This MEP refactors the backends to give a more structured and consistent API, removing generic code and consolidate existing code. To do this we propose splitting:
FigureManagerBase and its derived classes into the core
functionality class FigureManager and a backend specific class
WindowBase andShowBase and its derived classes into Gcf.show_all and MainLoopBase.This MEP aims to consolidate the backends API into one single uniform
API, removing generic code out of the backend (which includes
_pylab_helpers and Gcf), and push code to a more appropriate
level in matplotlib. With this we automatically remove
inconsistencies that appear in the backends, such as
FigureManagerBase.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 from
FigureManagerBase and ShowBase.
FigureManagerBase has three jobs at the moment:
self.set_window_title("Figure %d" % num) This combines
backend specific code self.set_window_title(title) with
matplotlib generic code title = "Figure %d" % num.FigureManager
decides when to end the mainloop. This also seems very wrong
as the figure should have no control over the other figures.ShowBase has two jobs:
_pylab_helpers.Gcf and telling them to show themselves.mainloop to block the main programme and thus keep the
figures from dying.The description of this MEP gives us most of the solution:
To remove the windowing aspect out of FigureManagerBase letting
it simply wrap this new class along with the other backend classes.
Create a new WindowBase class that can handle this
functionality, with pass-through methods (:arrow_right:) to
WindowBase. Classes that subclass WindowBase should also
subclass the GUI specific window class to ensure backward
compatibility (manager.window == manager.window).
Refactor the mainloop of ShowBase into MainLoopBase, which
encapsulates the end of the loop as well. We give an instance of
MainLoop to FigureManager as a key unlock the exit method
(requiring all keys returned before the loop can die). Note this
opens the possibility for multiple backends to run concurrently.
Now that FigureManagerBase has no backend specifics in it, to
rename it to FigureManager, and move to a new file
backend_managers.py noting that:
FigureManagerBase
class and its dependencies intact.NavigationBase has morphed into a backend independent
ToolManager.+--------------------------------------+------------------------------+---------------------+--------------------------------+
|FigureManagerBase(canvas, num) |FigureManager(figure, num) |WindowBase(title)|Notes |
| | | | |
+======================================+==============================+=====================+================================+
|show | |show | |
+--------------------------------------+------------------------------+---------------------+--------------------------------+
|destroy |calls destroy on all |destroy | |
| |components | | |
+--------------------------------------+------------------------------+---------------------+--------------------------------+
|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 all |
| | | |subclasses of FigureManagerBase |
+--------------------------------------+------------------------------+---------------------+--------------------------------+
| | |set_default_size | |
+--------------------------------------+------------------------------+---------------------+--------------------------------+
| | |add_element_to_window| |
+--------------------------------------+------------------------------+---------------------+--------------------------------+
+----------+------------+-------------+ |ShowBase |MainLoopBase|Notes | +==========+============+=============+ |mainloop |begin | | +----------+------------+-------------+ | |end |Gets called | | | |automagically| | | |when no more | | | |instances of | | | |the subclass | | | |exist | +----------+------------+-------------+ |call | |Method moved | | | |to | | | |Gcf.show_all | +----------+------------+-------------+
As eluded to above when discussing MEP 22, this refactor makes it easy
to add in new generic features. At the moment, MEP 22 has to make
ugly hacks to each class extending from FigureManagerBase. With
this code, this only needs to get made in the single FigureManager
class. This also makes the later deprecation of
NavigationToolbar2 very straightforward, only needing to touch the
single FigureManager class
MEP 23 makes for another use case where this refactored code will come in very handy.
As we leave all backend code intact, only adding missing methods to
existing classes, this should work seamlessly for all use cases. The
only difference will lie for backends that used
FigureManager.resize to resize the canvas and not the window, due
to the standardisation of the API.
I would envision that the classes made obsolete by this refactor get
deprecated and removed on the same timetable as
NavigationToolbar2, also note that the change in call signature to
the FigureCanvasWx constructor, while backward compatible, I think
the old (imho ugly style) signature should get deprecated and removed
in the same manner as everything else.
+-------------------------+-------------------------+-------------------------+
|backend |manager.resize(w,h) |Extra |
+=========================+=========================+=========================+
|gtk3 |window | |
+-------------------------+-------------------------+-------------------------+
|Tk |canvas | |
+-------------------------+-------------------------+-------------------------+
|Qt |window | |
+-------------------------+-------------------------+-------------------------+
|Wx |canvas |FigureManagerWx had |
| | |frame as an alias to |
| | |window, so this also |
| | |breaks BC. |
+-------------------------+-------------------------+-------------------------+
If there were any alternative solutions to solving the same problem, they should be discussed here, along with a justification for the chosen approach.
Mdehoon: Can you elaborate on how to run multiple backends concurrently?
OceanWolf: @mdehoon, as I say, not for this MEP, but I see this MEP
opens it up as a future possibility. Basically the MainLoopBase
class acts a per backend Gcf, in this MEP it tracks the number of
figures open per backend, and manages the mainloops for those
backends. It closes the backend specific mainloop when it detects
that no figures remain open for that backend. Because of this I
imagine that with only a small amount of tweaking that we can do
full-multi-backend matplotlib. No idea yet why one would want to, but
I leave the possibility there in MainLoopBase. With all the
backend-code specifics refactored out of FigureManager also aids
in this, one manager to rule them (the backends) all.
Mdehoon: @OceanWolf, OK, thanks for the explanation. Having a uniform API for the backends is very important for the maintainability of matplotlib. I think this MEP is a step in the right direction.