1
1
.. _plotting-guide-interactive :
2
2
3
- ************************************************
4
- Interactive Figures and Asynchronous Programming
5
- ************************************************
6
-
7
- One of the most powerful uses of matplotlib is interactive
8
- figures. At the most basic matplotlib has the ability to zoom and pan
9
- a figure to inspect your data, however there is also a full mouse and
10
- keyboard event handling system to enable building sophisticated interactive
3
+ .. currentmodule :: matplotlib
4
+
5
+
6
+ ==================================================
7
+ Interactive Figures and Asynchronous Programming
8
+ ==================================================
9
+
10
+ Matplotlib supports rich interactive figures. At the most basic
11
+ matplotlib has the ability to zoom and pan a figure to inspect your
12
+ data 'baked in', but this is backed by a full mouse and keyboard event
13
+ handling system to enable users to build sophisticated interactive
11
14
graphs.
12
15
13
- This page is meant to be a rapid introduction to the relevant details
14
- of integrating the matplotlib with a GUI event loop. For further
15
- details see `Interactive Applications using Matplotlib
16
+ This is meant to be a rapid introduction to the relevant details of
17
+ integrating the matplotlib with a GUI event loop. For further details
18
+ see `Interactive Applications using Matplotlib
16
19
<http://www.amazon.com/Interactive-Applications-using-Matplotlib-Benjamin/dp/1783988843> `__.
17
20
18
21
Fundamentally, all user interaction (and networking) is implemented as
19
- an infinite loop waiting for events from the OS and then doing
20
- something about it. For example, a minimal Read Evaluate Print Loop
21
- (REPL) is ::
22
+ an infinite loop waiting for events from the user (via the OS) and
23
+ then doing something about it. For example, a minimal Read Evaluate
24
+ Print Loop (REPL) is ::
22
25
23
26
exec_count = 0
24
27
while True:
@@ -33,108 +36,167 @@ exception!), but is representative of the event loops that underlie
33
36
all terminals, GUIs, and servers [#f1 ]_. In general the *Read * step is
34
37
waiting on some sort of I/O, be it user input from a keyboard or mouse
35
38
or the network while the *Evaluate * and *Print * are responsible for
36
- interpreting the input and then doing something about it.
37
-
38
- In practice most users do not work directly with these loops, and
39
- instead framework that provides a mechanism to register callbacks
40
- [#2 ]_. This allows users to write reactive, event-driven, programs
41
- without having to delve into the nity-grity [#f3 ]_ details of I/O.
42
- Examples include ``observe `` and friends in `traitlets `, the
43
- ``Signal `` / ``Slot `` framework in Qt and the analogs in Gtk / Tk /
44
- Wx, the request functions in web frameworks, or Matplotlib's
45
- native :ref: `event handling system <event-handling-tutorial >`.
46
-
47
-
39
+ interpreting the input and then **doing ** something about it.
40
+
41
+ In practice users do not work directly with these loops and instead
42
+ use a framework that provides a mechanism to register callbacks [#2 ]_.
43
+ This allows users to write reactive, event-driven, programs without
44
+ having to delve into the nity-grity [#f3 ]_ details of I/O. Examples
45
+ include ``observe `` and friends in `traitlets `, the ``Signal `` /
46
+ ``Slot `` framework in Qt (and the analogs in other GUI frameworks),
47
+ the 'request' functions in web frameworks, and Matplotlib's native
48
+ :ref: `event handling system <event-handling-tutorial >`.
49
+
50
+ All GUI frameworks (Qt, Wx, Gtk, tk, QSX, or web) have some method of
51
+ capturing user interactions and passing them back to the application,
52
+ although the exact details vary between frameworks. Matplotlib has a
53
+ 'backend' (see :ref: `what-is-a-backend `) for each GUI framework which
54
+ converts the native UI events into Matplotlib events. This allows
55
+ Matplotlib user to write GUI-independent interactive figures.
48
56
49
57
references to trackdown:
50
- - link to cpython REPL loop
58
+ - link to cpython REPL loop (pythonrun.c::PyRunInteractiveLoopFlags)
59
+ - link to IPython repl loop (Ipython.terminal.interactiveshell.py :: TerminalInteractiveShell.mainloop
60
+ and Ipython.terminal.interactiveshell.py :: TerminalInteractiveShell.interact)
51
61
- curio / trio / asycio / twisted / tornado event loops
52
62
- Beazly talk or two on asyncio
53
63
64
+ Command Prompt Integration
65
+ ==========================
54
66
55
- GUI to Matplotlib Bridge
56
- ------------------------
67
+ Integrating a GUI window and a CLI introduces a conflict: there are
68
+ two infinite loops that want to be waiting for user input in the same
69
+ process. In order for both the prompt and the GUI widows to be responsive
70
+ we need a method to allow the loops to 'timeshare'.
57
71
58
- When using
72
+ 1. let the GUI main loop block the python process
73
+ 2. intermittently run the GUI loop
59
74
60
75
61
- Command Prompt
62
- --------------
76
+ Blocking
77
+ --------
63
78
64
- To handle asynchronous user input every GUI framework has an event
65
- loop. At the most basic this is a stack that can have events to be
66
- processed. In order for the GUI to be responsive this loop must be
67
- run. To manage this in python there are two basic methods:
79
+ The simplest "integration" is to start the GUI event loop in
80
+ 'blocking' mode and take over the CLI. While the GUI event loop is
81
+ running you can not enter new commands into the prompt (your terminal
82
+ may show the charters entered into stdin, but they will not be
83
+ processed by python), but the figure windows will be responsive. Once
84
+ the event loop is stopped (leaving any still open figure windows
85
+ non-responsive) you will be able to use the prompt again. Re-starting
86
+ the event will make any open figure responsive again.
68
87
69
- 1. let the GUI main loop block the python process
70
- 2. intermittently run the GUI loop for a period of time
71
88
89
+ To start the event loop until all open figures are closed us
90
+ `pyplot.show(block=True) `. To start the event loop for a fixed amount
91
+ of time use `pyplot.pause `.
92
+
93
+ Without using ``pyplot `` you can start and stop the event loops via
94
+ ``fig.canvas.start_event_loop `` and ``fig.canvas.stop_event_loop ``.
95
+
96
+
97
+ .. warning
98
+
99
+ By using `Figure.show` it is possible to display a figure on the
100
+ screen with out starting the event loop and not being in
101
+ interactive mode. This will likely result in a non-responsive
102
+ figure and may not even display the rendered plot.
103
+
104
+
105
+
106
+ .. autosummary ::
107
+ :template: autosummary.rst
108
+ :nosignatures:
109
+
110
+ pyplot.show
111
+ pyplot.pause
112
+
113
+ backend_bases.FigureCanvasBase.start_event_loop
114
+ backend_bases.FigureCanvasBase.stop_event_loop
115
+
116
+ figure.Figure.show
72
117
73
- Blocking
74
- ********
75
118
119
+ Explicitly
120
+ ----------
121
+
122
+ If you have open windows (either due to a `plt.pause ` timing out or
123
+ from calling `figure.Figure.show `) that have pending UI events (mouse
124
+ clicks, button presses, or draws) you can explicitly process them by
125
+ calling ``fig.canvas.flush_events() ``. This will not run the GUI
126
+ event loop, but instead synchronously processes all UI events current
127
+ waiting to be processed. The exact behavior is backend-dependent but
128
+ typically events on all figure are processed and only events waiting
129
+ to be processed (not those added during processing) will be handled.
130
+
131
+ .. autosummary ::
132
+ :template: autosummary.rst
133
+ :nosignatures:
134
+
135
+ backend_bases.FigureCanvasBase.flush_events
76
136
77
137
Interactive
78
- ***********
138
+ -----------
139
+
140
+ While running the GUI event loop in a blocking mode or explicitly
141
+ handling UI events is useful, we can do better! We really want to be
142
+ able to have a usable prompt **and ** interactive figure windows. We
143
+ can do this using the concept of 'input hook' (see :ref: `below
144
+ <Eventloop integration mechanism>` for implementation details) that
145
+ allows the GUI event loop to run and process events while the prompt
146
+ is waiting for the user to type (even for an extremely fast typist, a
147
+ vast majority of the time the prompt is simple idle waiting for human
148
+ finders to move). This effectively gives us a simultaneously GUI
149
+ windows and prompt.
150
+
151
+ This time-share technique only allows the event loop to run while
152
+ python is otherwise idle and waiting for user input. If you want the
153
+ GUI to be responsive during long running code it is necessary to
154
+ periodically flush the GUI event queue as described :ref: `above
155
+ <Explicitly>`. In this case it is your code, not the REPL, which is
156
+ blocking process so you need to handle the time-share manually.
157
+ Conversely, a very slow figure draw will block the prompt until it
158
+ finishes.
159
+
160
+ Full embedding
161
+ ==============
162
+
163
+ It is also possible to go the other direction and fully embed figures
164
+ it a rich native application. Matplotlib provides classes which can
165
+ be directly embedded in GUI applications (this is how the built-in
166
+ windows are implemented!). See :ref: `user_interfaces ` for more details
167
+ on how to do this.
168
+
79
169
80
170
Scripts
81
- -------
171
+ =======
82
172
83
- - if you want always reactive figures while the script runs, you have to
84
- call `flush_event `
85
- - if you want to have reactive figures that block the script until they are closed (ex for
86
- collecting user input before continuing use
173
+ There are several use-cases for using interactive figures in scripts:
87
174
175
+ - progress updates as a long running script progresses
176
+ - capture user input to steer the script
177
+ - streaming updates from a data source
88
178
89
- Full embedding
90
- --------------
179
+ In the first case, it is the same as :ref: ` above
180
+ <Explicitly>` where you explicitly call ::
91
181
92
- - just let the underlying GUI event loop handle eve
182
+ fig.canvas.flush_events()
93
183
94
- Web
95
- ---
184
+ periodically to allow the event loop to process UI and draw events and
185
+ ::
96
186
97
- The Weeds
98
- =========
187
+ fig.canvas.draw_idle()
99
188
189
+ when you have updated the contents of the figure.
100
190
101
- The GUI event loop
102
- ------------------
191
+ The more frequently you call ``flush_events `` the more responsive your
192
+ figure will feel but at the cost of spending more resource on the
193
+ visualization and less on your computation.
103
194
195
+ The second case is very much like :ref: `Blocking ` above. By using ``plt.show(block=True) `` or
104
196
105
- The python capi provides a hook, `PyOS_InputHook `, to register a
106
- function to be run "The function will be called when Python's
107
- interpreter prompt is about to become idle and wait for user input
108
- from the terminal.". This hook can be used to integrate a second
109
- event loop (the GUI event loop) with the python input prompt loop.
110
- Such hooks are usually included with the python bindings for GUI
111
- toolkits and may be registered on import. IPython also includes hooks
112
- for all of the GUI frameworks supported by matplotlib. The hook
113
- functions typically exhaust all pending events on the GUI event queue,
114
- run the main loop for a short fixed amount of time, or run the event
115
- loop until a key is pressed on stdin.
116
-
117
- matplotlib does not currently do any management of `PyOS_InputHook `
118
- due to the wide range of ways that matplotlib is used. This
119
- management is left to the code using matplotlib. Interactive figures,
120
- even with matplotlib in 'interactive mode', may not work in the
121
- vanilla python repl if an appropriate `PyOS_InputHook ` is not
122
- registered. We suggest using ``IPython ``, which in addition to
123
- improving the command line, ensures that such a `PyOS_InputHook `
124
- function is registered for you GUI backend of choice.
125
-
126
- A drawback of relying on `PyOS_InputHook ` is that the GUI event loop
127
- is only processing events while python is otherwise idle and waiting
128
- for user input. If you want the GUI to be responsive during long
129
- running code it is necessary to periodically flush the GUI event
130
- queue. To achieve this, almost all of the of the GUI-based ``Canvas ``
131
- classes provide a `flush_event ` method. By periodically calling this
132
- method the GUI will be updated and appear to be responsive.
133
-
134
- In both cases, to schedule a re-draw of the figure at some point in
135
- the future use ``fig.canvas.draw_idle() ``. This will defer the actual
136
- rendering of the figure until the GUI is ready to update its
137
- on-screen representation.
197
+ The third case you will have to integrate updating the ``Aritist ``
198
+ instances, calling ``draw_idle ``, and the GUI event loop with your
199
+ data I/O.
138
200
139
201
Stale Artists
140
202
=============
@@ -150,14 +212,100 @@ figure.
150
212
151
213
152
214
Interactive Mode
153
- ----------------
215
+ ================
216
+
217
+
218
+ Draw Idle
219
+ =========
220
+
221
+ In almost all cases, we recommend using
222
+ `backend_bases.FigureCanvasBae.draw_idle ` over
223
+ `backend_bases.FigureCanvasBae.draw `. ``draw `` forces a rendering of
224
+ the figure where as ``draw_idle `` schedules a rendering the next time
225
+ the GUI window is going to re-paint the screen. This improves
226
+ performance by only rendering pixels that will be shown on the screen. If
227
+ you want to be sure that the screen is updated as soon as possible do ::
228
+
229
+ fig.canvas.draw_idle()
230
+ fig.canvas.flush_events()
231
+
232
+
233
+
234
+ .. autosummary ::
235
+ :template: autosummary.rst
236
+ :nosignatures:
237
+
238
+ backend_bases.FigureCanvasBase.draw
239
+ backend_bases.FigureCanvasBase.draw_idle
240
+ backend_bases.FigureCanvasBase.flush_events
241
+
242
+ Threading
243
+ =========
244
+
245
+ Unfortunately, most GUI frameworks require that all updates to the
246
+ screen happen on the main thread which makes pushing periodic updates
247
+ to the window to a background thread problematic. Although it seems
248
+ backwards, it is easier to push your computations to a background
249
+ thread and periodically update the figure from the main thread.
250
+
251
+ In general Matplotlib is not thread safe, but the consequences of
252
+ drawing while updating artists from another thread should not be worse
253
+ than a failed draw. This should not be fatal and so long as the
254
+ Artists end up consistent the figure can eventually be drawn cleanly.
255
+
256
+ Web
257
+ ===
258
+
259
+
260
+
261
+ The Weeds
262
+ =========
263
+
264
+
265
+ Eventloop integration mechanism
266
+ -------------------------------
267
+
268
+ CPython / readline
269
+ ~~~~~~~~~~~~~~~~~~
270
+
271
+ The python capi provides a hook, `PyOS_InputHook `, to register a
272
+ function to be run "The function will be called when Python's
273
+ interpreter prompt is about to become idle and wait for user input
274
+ from the terminal.". This hook can be used to integrate a second
275
+ event loop (the GUI event loop) with the python input prompt loop.
276
+ The hook functions typically exhaust all pending events on the GUI
277
+ event queue, run the main loop for a short fixed amount of time, or
278
+ run the event loop until a key is pressed on stdin.
279
+
280
+
281
+ Matplotlib does not currently do any management of `PyOS_InputHook `
282
+ due to the wide range of ways that matplotlib is used. This
283
+ management is left to the code using Matplotlib. Interactive figures,
284
+ even with matplotlib in 'interactive mode', may not work in the
285
+ vanilla python repl if an appropriate `PyOS_InputHook ` is not
286
+ registered.
287
+
288
+ Input hooks, and helpers to install them, are usually included with
289
+ the python bindings for GUI toolkits and may be registered on import.
290
+ IPython also ships input hook functions for all of the GUI frameworks
291
+ Matplotlib supports which can be installed via ``%matplotlib ``. This
292
+ is the recommended method of integrating Matplotlib and a prompt.
293
+
294
+
295
+
296
+ IPython / prompt toolkit
297
+ ~~~~~~~~~~~~~~~~~~~~~~~~
154
298
299
+ With IPython >= 5.0 IPython has changed from using cpython's readline
300
+ based prompt to a ``prompt_toolkit `` based prompt. ``prompt_toolkit ``
301
+ has the same conceptual input hook, which is feed into pt via the
302
+ :meth: `IPython.terminal.interactiveshell.TerminalInteractiveShell.inputhook `
303
+ method. The source for the prompt_toolkit input hooks lives at
304
+ :mod: `IPython.terminal.pt_inputhooks `
155
305
156
- Blitting
157
- ========
158
306
159
307
160
- .. ruberic :: Fotenotes
308
+ .. rubric :: Fotenotes
161
309
162
310
.. [#f1 ] A limitation of this design is that you can only wait for one
163
311
input, if there is a need to multiplex between multiple sources
0 commit comments