Skip to content

Commit 25a33d2

Browse files
authored
Merge pull request #188 from jupyter-widgets/ipympl
modernize matplotlib sections of interact notebook
2 parents d528a20 + 63cb110 commit 25a33d2

File tree

6 files changed

+153
-47
lines changed

6 files changed

+153
-47
lines changed

environment.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,5 @@ dependencies:
2828
- sidecar=0.5.1
2929
- voila=0.3.5
3030
- vtk=9.1.0
31+
- pip:
32+
- mpl_interactions==0.22.0

notebooks/02.Interact/02.00-Using-Interact.ipynb

Lines changed: 112 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@
2525
{
2626
"cell_type": "code",
2727
"execution_count": null,
28-
"metadata": {},
28+
"metadata": {
29+
"tags": []
30+
},
2931
"outputs": [],
3032
"source": [
3133
"from ipywidgets import interact, interactive, fixed, interact_manual\n",
@@ -294,10 +296,12 @@
294296
{
295297
"cell_type": "code",
296298
"execution_count": null,
297-
"metadata": {},
299+
"metadata": {
300+
"tags": []
301+
},
298302
"outputs": [],
299303
"source": [
300-
"# %load solutions/reverse-text.py\n"
304+
"# %load solutions/reverse-text.py"
301305
]
302306
},
303307
{
@@ -352,49 +356,107 @@
352356
},
353357
{
354358
"cell_type": "markdown",
355-
"metadata": {},
359+
"metadata": {
360+
"tags": []
361+
},
356362
"source": [
357363
"## Basic interactive plot\n",
358364
"\n",
359-
"Though the examples so far in this notebook had very basic output, more interesting possibilities are straightforward. \n",
360365
"\n",
361366
"The function below plots a straight line whose slope and intercept are given by its arguments."
362367
]
363368
},
364369
{
365370
"cell_type": "code",
366371
"execution_count": null,
367-
"metadata": {},
372+
"metadata": {
373+
"tags": []
374+
},
368375
"outputs": [],
369376
"source": [
370-
"%matplotlib widget\n",
371377
"import matplotlib.pyplot as plt\n",
372378
"import numpy as np\n",
373379
"\n",
374380
"def f(m, b):\n",
375-
" plt.figure(2)\n",
381+
" fig = plt.figure()\n",
376382
" plt.clf()\n",
377383
" plt.grid()\n",
378384
" x = np.linspace(-10, 10, num=1000)\n",
379385
" plt.plot(x, m * x + b)\n",
380386
" plt.ylim(-5, 5)\n",
381-
" plt.show()\n"
387+
" plt.show()\n",
388+
"\n",
389+
"interact(f, m=(-2.0, 2.0), b=(-3, 3, 0.5))"
382390
]
383391
},
384392
{
385393
"cell_type": "markdown",
386394
"metadata": {},
387395
"source": [
388-
"The interactive below displays a line whose slope and intercept is set by the sliders. Note that if the variable containing the widget, `interactive_plot`, is the last thing in the cell it is displayed."
396+
"## Fully interactive plot\n",
397+
"\n",
398+
"While the above example works, it has some drawbacks:\n",
399+
"1. It is inefficient to re-run all the plotting code (the whole plot gets re-created every time -- to see this, run the code above again after running the cell below and see how the figure numbers get incremented)\n",
400+
"2. No zooming or panning\n",
401+
"3. Screen can jump when moving the sliders\n",
402+
"\n",
403+
"A better solution is to use the [ipympl](https://matplotlib.org/ipympl/) Matplotlib backend. You can activate this with the line magic: `%matplotlib ipympl`."
389404
]
390405
},
391406
{
392407
"cell_type": "code",
393408
"execution_count": null,
409+
"metadata": {
410+
"tags": []
411+
},
412+
"outputs": [],
413+
"source": [
414+
"# Activate the widget based backend.\n",
415+
"%matplotlib ipympl\n",
416+
"\n",
417+
"x = np.linspace(-10, 10, num=1000)\n",
418+
"fig, ax = plt.subplots()\n",
419+
"ax.grid()\n",
420+
"ax.set_ylim(-5, 5)\n",
421+
"# Initialize a plot object with y = x. We'll be modifying y below.\n",
422+
"# This returns a list of `.Line2D` representing the plotted data. We grab the first one -- we only have 1 series.\n",
423+
"line = ax.plot(x, x)[0]\n",
424+
"\n",
425+
"@interact(m=(-2.0, 2.0), b=(-3, 3, 0.5))\n",
426+
"def update_line(m=1, b=0.5):\n",
427+
" line.set_ydata(m * x + b)\n",
428+
" # Request a widget redraw.\n",
429+
" fig.canvas.draw_idle()"
430+
]
431+
},
432+
{
433+
"cell_type": "markdown",
394434
"metadata": {},
435+
"source": [
436+
"## mpl-interactions\n",
437+
"\n",
438+
"The [mpl-interactions](https://mpl-interactions.readthedocs.io/en/stable/) library can automate the updating of Matplotlib plots for you. Non-matplotlib keyword arguments passed to functions like `plot` will be interpreted to sliders and widget controls, similarly to `interact`, and automatically connected with the matplotlib plots."
439+
]
440+
},
441+
{
442+
"cell_type": "code",
443+
"execution_count": null,
444+
"metadata": {
445+
"tags": []
446+
},
395447
"outputs": [],
396448
"source": [
397-
"interact(f, m=(-2.0, 2.0), b=(-3, 3, 0.5))"
449+
"from mpl_interactions import ipyplot as iplt\n",
450+
"\n",
451+
"fig, ax = plt.subplots()\n",
452+
"ax.grid()\n",
453+
"ax.set_ylim(-5,5)\n",
454+
"\n",
455+
"# Define function in a way you can re-use in calculations\n",
456+
"def f(x, m, b):\n",
457+
" return m * x + b\n",
458+
" \n",
459+
"ctrls = iplt.plot(x, f, m=(-2,2), b=(-3, 3, 10))"
398460
]
399461
},
400462
{
@@ -403,39 +465,55 @@
403465
"source": [
404466
"### Exercise: Make a plot\n",
405467
"\n",
406-
"Here is a python function that, given $k$ and $p$, plots $f(x) = \\sin(k x - p)$.\n"
468+
"Here is an example of making a plot of $f(x) = \\sin(k x - p)$.\n"
407469
]
408470
},
409471
{
410472
"cell_type": "code",
411473
"execution_count": null,
412-
"metadata": {},
474+
"metadata": {
475+
"tags": []
476+
},
413477
"outputs": [],
414478
"source": [
415-
"def plot_f(k, p):\n",
416-
" plt.figure(5)\n",
417-
" plt.clf()\n",
418-
" plt.grid()\n",
419-
" x = np.linspace(0, 4 * np.pi)\n",
420-
" y = np.sin(k*x - p)\n",
421-
" plt.plot(x, y)\n",
422-
" plt.show()"
479+
"x = np.linspace(0, 4 * np.pi, 100)\n",
480+
"k = 1\n",
481+
"p = np.pi\n",
482+
"\n",
483+
"def f(x, k, p):\n",
484+
" return np.sin(k*x -p)\n",
485+
"fig, ax = plt.subplots()\n",
486+
"ax.grid()\n",
487+
"ax.plot(x, f(x, k, p))"
423488
]
424489
},
425490
{
426491
"cell_type": "markdown",
427492
"metadata": {},
428493
"source": [
429-
"Copy the above function definition and make it interactive using `interact`, so that there are sliders for the parameters $k$ and $p$, where $0.5\\leq k \\leq 2$ and $0 \\leq p \\leq 2\\pi$ (hint: use `np.pi` for $\\pi$)."
494+
"Copy the above function definition and make it interactive using `interact` or `mpl-interactions`, so that there are sliders for the parameters $k$ and $p$, where $0.5\\leq k \\leq 2$ and $0 \\leq p \\leq 2\\pi$ (hint: use `np.pi` for $\\pi$)."
430495
]
431496
},
432497
{
433498
"cell_type": "code",
434499
"execution_count": null,
435-
"metadata": {},
500+
"metadata": {
501+
"tags": []
502+
},
503+
"outputs": [],
504+
"source": [
505+
"# %load solutions/plot-function-interact.py"
506+
]
507+
},
508+
{
509+
"cell_type": "code",
510+
"execution_count": null,
511+
"metadata": {
512+
"tags": []
513+
},
436514
"outputs": [],
437515
"source": [
438-
"# %load solutions/plot-function.py"
516+
"# %load solutions/plot-function-mpl-inter.py"
439517
]
440518
},
441519
{
@@ -460,9 +538,9 @@
460538
],
461539
"metadata": {
462540
"kernelspec": {
463-
"display_name": "widgets-tutorial",
541+
"display_name": "Python 3 (ipykernel)",
464542
"language": "python",
465-
"name": "widgets-tutorial"
543+
"name": "python3"
466544
},
467545
"language_info": {
468546
"codemirror_mode": {
@@ -474,7 +552,14 @@
474552
"name": "python",
475553
"nbconvert_exporter": "python",
476554
"pygments_lexer": "ipython3",
477-
"version": "3.8.10"
555+
"version": "3.9.13"
556+
},
557+
"widgets": {
558+
"application/vnd.jupyter.widget-state+json": {
559+
"state": {},
560+
"version_major": 2,
561+
"version_minor": 0
562+
}
478563
}
479564
},
480565
"nbformat": 4,
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# with interact
2+
3+
fig, ax = plt.subplots()
4+
ax.grid()
5+
line = ax.plot(x, f(x, k, p))[0]
6+
7+
def plot_f(k, p):
8+
line.set_ydata(f(x, k, p))
9+
fig.canvas.draw_idle()
10+
11+
12+
interact(plot_f, k=(0.5, 2), p=(0, 2 * np.pi))
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
fig, ax = plt.subplots()
2+
ax.grid()
3+
ctrls = iplt.plot(x, f, k=(.5, 2), p=(0, 2*np.pi))

notebooks/02.Interact/solutions/plot-function.py

Lines changed: 0 additions & 1 deletion
This file was deleted.

notebooks/07.More-libraries/07.05-other-widget-libraries.ipynb

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@
276276
"source": [
277277
"# ipympl: The Matplotlib Jupyter Widget Backend\n",
278278
"\n",
279-
"## https://github.com/matplotlib/ipympl\n",
279+
"## https://matplotlib.org/ipympl/\n",
280280
"\n",
281281
"\n",
282282
"Enabling interaction with matplotlib charts in the Jupyter notebook and JupyterLab\n",
@@ -295,7 +295,7 @@
295295
"id": "91a33e30-803d-4945-8129-2c37f30a1d09",
296296
"metadata": {},
297297
"source": [
298-
"Enabling the `widget` backend. This requires ipympl. ipympl can be install via pip or conda."
298+
"Enabling the `ipympl` (sometimes written `widget`) backend. ipympl can be install via pip or conda."
299299
]
300300
},
301301
{
@@ -305,7 +305,7 @@
305305
"metadata": {},
306306
"outputs": [],
307307
"source": [
308-
"%matplotlib widget"
308+
"%matplotlib ipympl"
309309
]
310310
},
311311
{
@@ -325,41 +325,39 @@
325325
"id": "fc9dca28-883c-4286-af3d-a17081b089d6",
326326
"metadata": {},
327327
"source": [
328-
"When using the `widget` backend from ipympl, fig.canvas is a proper Jupyter interactive widget, which can be embedded in Layout classes like HBox and Vbox.\n",
328+
"When using the `widget` backend from ipympl, `fig.canvas` is a proper Jupyter interactive widget, which can be embedded in Layout classes like `HBox` and `VBox`.\n",
329329
"\n",
330-
"One can bound figure attributes to other widget values."
330+
"One can bind figure attributes to other widget values."
331331
]
332332
},
333333
{
334334
"cell_type": "code",
335335
"execution_count": null,
336336
"id": "9db5b3e0-317c-4b79-bb5c-36786d02895a",
337-
"metadata": {},
337+
"metadata": {
338+
"tags": []
339+
},
338340
"outputs": [],
339341
"source": [
340-
"plt.ioff()\n",
341-
"plt.clf()\n",
342-
"\n",
343-
"slider = FloatSlider(\n",
344-
" value=1.0,\n",
345-
" min=0.02,\n",
346-
" max=2.0\n",
347-
")\n",
348-
"\n",
349-
"fig1 = plt.figure(1)\n",
342+
"with plt.ioff():\n",
343+
" fig, ax = plt.subplots()\n",
350344
"\n",
351345
"x1 = np.linspace(0, 20, 500)\n",
352346
"\n",
353347
"lines = plt.plot(x1, np.sin(slider.value * x1))\n",
354348
"\n",
355349
"def update_lines(change):\n",
356350
" lines[0].set_data(x1, np.sin(change.new * x1))\n",
357-
" fig1.canvas.draw()\n",
358-
" fig1.canvas.flush_events()\n",
351+
" fig.canvas.draw()\n",
359352
"\n",
353+
"slider = FloatSlider(\n",
354+
" value=1.0,\n",
355+
" min=0.02,\n",
356+
" max=2.0\n",
357+
")\n",
360358
"slider.observe(update_lines, names='value')\n",
361359
"\n",
362-
"VBox([slider, fig1.canvas])"
360+
"VBox([slider, fig.canvas])"
363361
]
364362
},
365363
{
@@ -451,6 +449,13 @@
451449
"nbconvert_exporter": "python",
452450
"pygments_lexer": "ipython3",
453451
"version": "3.9.13"
452+
},
453+
"widgets": {
454+
"application/vnd.jupyter.widget-state+json": {
455+
"state": {},
456+
"version_major": 2,
457+
"version_minor": 0
458+
}
454459
}
455460
},
456461
"nbformat": 4,

0 commit comments

Comments
 (0)