File: MEP27.rst

package info (click to toggle)
matplotlib 3.10.1%2Bdfsg1-4
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 78,352 kB
  • sloc: python: 147,118; cpp: 62,988; objc: 1,679; ansic: 1,426; javascript: 786; makefile: 104; sh: 53
file content (221 lines) | stat: -rw-r--r-- 11,667 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
======================================
 MEP27: Decouple pyplot from backends
======================================

.. contents::
   :local:


Status
======
**Progress**

Branches and Pull requests
==========================
Main PR (including GTK3):

+ https://github.com/matplotlib/matplotlib/pull/4143

Backend specific branch diffs:

+ https://github.com/OceanWolf/matplotlib/compare/backend-refactor...OceanWolf:backend-refactor-tkagg
+ https://github.com/OceanWolf/matplotlib/compare/backend-refactor...OceanWolf:backend-refactor-qt
+ https://github.com/OceanWolf/matplotlib/compare/backend-refactor...backend-refactor-wx

Abstract
========

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:

1. ``FigureManagerBase`` and its derived classes into the core
   functionality class ``FigureManager`` and a backend specific class
   ``WindowBase`` and
2. ``ShowBase`` and its derived classes into ``Gcf.show_all`` and ``MainLoopBase``.

Detailed description
====================

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``.

1. ``FigureManagerBase`` has **three** jobs at the moment:

   1. The documentation describes it as a *Helper class for pyplot
      mode, wraps everything up into a neat bundle*
   2. But it doesn't just wrap the canvas and toolbar, it also does
      all of the windowing tasks itself.  The conflation of these two
      tasks gets seen the best in the following line:
      ``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``.
   3. Currently the backend specific subclass of ``FigureManager``
      decides when to end the mainloop.  This also seems very wrong
      as the figure should have no control over the other figures.


2. ``ShowBase`` has two jobs:

   1. It has the job of going through all figure managers registered
      in ``_pylab_helpers.Gcf`` and telling them to show themselves.
   2. And secondly it has the job of performing the backend specific
      ``mainloop`` to block the main programme and thus keep the
      figures from dying.

Implementation
==============

The description of this MEP gives us most of the solution:

1. 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``).
2. 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.
3. 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:

   1. This allows us to break up the conversion of backends into
      separate PRs as we can keep the existing ``FigureManagerBase``
      class and its dependencies intact.
   2. And this also anticipates MEP22 where the new
      ``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 |
+----------+------------+-------------+

Future compatibility
====================

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.

Backward compatibility
======================

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.               |
+-------------------------+-------------------------+-------------------------+


Alternatives
============

If there were any alternative solutions to solving the same problem,
they should be discussed here, along with a justification for the
chosen approach.

Questions
=========

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.