Umfassendes Beispiel#

# Enabling the `widget` backend.
# This requires jupyter-matplotlib a.k.a. ipympl.
# ipympl can be install via pip or conda.
%matplotlib widget

import matplotlib.pyplot as plt
import numpy as np
# Testing matplotlib interactions with a simple plot
fig = plt.figure()
plt.plot(np.sin(np.linspace(0, 20, 100)));
# Always hide the toolbar
fig.canvas.toolbar_visible = False
# Put it back to its default
fig.canvas.toolbar_visible = 'fade-in-fade-out'
# Change the toolbar position
fig.canvas.toolbar_position = 'top'
# Hide the Figure name at the top of the figure
fig.canvas.header_visible = False
# Hide the footer
fig.canvas.footer_visible = False
# Disable the resizing feature
fig.canvas.resizable = False
# If true then scrolling while the mouse is over the canvas will not move the entire notebook
fig.canvas.capture_scroll = True

Sie können auch display für fig.canvas aufrufen, um das interaktive Diagramm überall im Notebook anzuzeigen.

fig.canvas.toolbar_visible = True
display(fig.canvas)

Oder Sie können display(fig) verwenden, um das aktuelle Diagramm als PNG einzubetten.

display(fig)

3D-Darstellung#

from mpl_toolkits.mplot3d import axes3d

fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')

# Grab some test data.
X, Y, Z = axes3d.get_test_data(0.05)

# Plot a basic wireframe.
ax.plot_wireframe(X, Y, Z, rstride=10, cstride=10)

plt.show()

Subplots#

# A more complex example from the matplotlib gallery
np.random.seed(0)

n_bins = 10
x = np.random.randn(1000, 3)

fig, axes = plt.subplots(nrows=2, ncols=2)
ax0, ax1, ax2, ax3 = axes.flatten()

colors = ['red', 'tan', 'lime']
ax0.hist(x, n_bins, density=1, histtype='bar', color=colors, label=colors)
ax0.legend(prop={'size': 10})
ax0.set_title('bars with legend')

ax1.hist(x, n_bins, density=1, histtype='bar', stacked=True)
ax1.set_title('stacked bar')

ax2.hist(x, n_bins, histtype='step', stacked=True, fill=False)
ax2.set_title('stack step (unfilled)')

# Make a multiple-histogram of data-sets with different length.
x_multi = [np.random.randn(n) for n in [10000, 5000, 2000]]
ax3.hist(x_multi, n_bins, histtype='bar')
ax3.set_title('different sample sizes')

fig.tight_layout()
plt.show()
fig.canvas.toolbar_position = 'right'
fig.canvas.toolbar_visible = False

Interaktionen mit anderen Widgets und Layouting#

Wenn Sie die Grafik in ein Layout anderer Widgets einbetten möchten, sollten Sie plt.ioff() aufrufen, bevor Sie die Grafik erstellen, da plt.figure() andernfalls automatisch und außerhalb Ihres Layouts eine Anzeige der Leinwand auslöst.

Ohne Verwendung von ioff#

Hier wird die Grafik zweimal angezeigt. Der Button tut nichts, er wurde nur als Beispiel für das Layouting platziert.

import ipywidgets as widgets

# ensure we are interactive mode 
# this is default but if this notebook is executed out of order it may have been turned off
plt.ion()

fig = plt.figure()
ax = fig.gca()
ax.imshow(Z)

widgets.AppLayout(
    center=fig.canvas,
    footer=widgets.Button(icon='check'),
    pane_heights=[0, 6, 1]
)

Behebung der doppelten Anzeige mit ioff#

Wenn wir sicherstellen, dass der interaktive Modus beim Erstellen der Grafik deaktiviert ist, wird die Grafik nur dort angezeigt, wo wir sie haben möchten.

Dazu können Sie plt.ioff() als Kontextmanager verwenden.

with plt.ioff():
    fig = plt.figure()

ax = fig.gca()
ax.imshow(Z)

widgets.AppLayout(
    center=fig.canvas,
    footer=widgets.Button(icon='check'),
    pane_heights=[0, 6, 1]
)

Interaktion mit anderen Widgets#

Ändern eines Liniendiagramms mit einem Schieberegler#

# When using the `widget` backend from ipympl,
# fig.canvas is a proper Jupyter interactive widget, which can be embedded in
# an ipywidgets layout. See https://ipywidgets.readthedocs.io/en/stable/examples/Layout%20Templates.html

# One can bound figure attributes to other widget values.
from ipywidgets import AppLayout, FloatSlider

plt.ioff()

slider = FloatSlider(
    orientation='horizontal',
    description='Factor:',
    value=1.0,
    min=0.02,
    max=2.0
)

slider.layout.margin = '0px 30% 0px 30%'
slider.layout.width = '40%'

fig = plt.figure()
fig.canvas.header_visible = False
fig.canvas.layout.min_height = '400px'
plt.title('Plotting: y=sin({} * x)'.format(slider.value))

x = np.linspace(0, 20, 500)

lines = plt.plot(x, np.sin(slider.value * x))

def update_lines(change):
    plt.title('Plotting: y=sin({} * x)'.format(change.new))
    lines[0].set_data(x, np.sin(change.new * x))
    fig.canvas.draw()
    fig.canvas.flush_events()

slider.observe(update_lines, names='value')

AppLayout(
    center=fig.canvas,
    footer=slider,
    pane_heights=[0, 6, 1]
)

Aktualisieren von Bilddaten auf performante Weise#

Zwei nützliche Tricks zur Leistungssteigerung bei der Aktualisierung eines mit Matplotlib angezeigten Bildes sind:

  1. Verwenden Sie die Methode set_data anstelle des Aufrufs von imshow

  2. Vorberechnen und dann Indizieren des Arrays

# precomputing all images
x = np.linspace(0,np.pi,200)
y = np.linspace(0,10,200)
X,Y = np.meshgrid(x,y)
parameter = np.linspace(-5,5)
example_image_stack = np.sin(X)[None,:,:]+np.exp(np.cos(Y[None,:,:]*parameter[:,None,None]))
with plt.ioff():
    fig = plt.figure()
im = plt.imshow(example_image_stack[0])

def update(change):
    im.set_data(example_image_stack[change['new']])
    fig.canvas.draw_idle()
    
    
slider = widgets.IntSlider(value=0, min=0, max=len(parameter)-1)
slider.observe(update, names='value')
widgets.VBox([slider, fig.canvas])

Debuggen von Widget-Updates und Matplotlib-Callbacks#

Wenn in der update-Funktion ein Fehler auftritt, wird dieser nicht immer im Notebook angezeigt, was das Debugging erschweren kann. Dieses Problem besteht auch für Matplotlib-Callbacks bei Benutzerereignissen wie Mausbewegungen, siehe z. B. Issue. Es gibt zwei Möglichkeiten, die Ausgabe zu sehen:

  1. In JupyterLab wird die Ausgabe in der Log-Konsole angezeigt (Ansicht > Log-Konsole anzeigen)

  2. Verwendung von ipywidgets.Output

Hier ist ein Beispiel für die Verwendung eines Output-Widgets, um Fehler in der Update-Funktion des vorherigen Beispiels abzufangen. Um Fehler zu provozieren, haben wir die Grenzen des Schiebereglers geändert, damit Fehler außerhalb des Bereichs auftreten.

Von: slider = widgets.IntSlider(value=0, min=0, max=len(parameter)-1)

Zu: slider = widgets.IntSlider(value=0, min=0, max=len(parameter)+10)

Wenn Sie den Schieberegler ganz nach rechts schieben, sollten Sie Fehler im Output-Widget sehen.

with plt.ioff():
    fig = plt.figure()
im = plt.imshow(example_image_stack[0])

out = widgets.Output()
@out.capture()
def update(change):
    with out:
        if change['name'] == 'value':
            im.set_data(example_image_stack[change['new']])
            fig.canvas.draw_idle
    
    
slider = widgets.IntSlider(value=0, min=0, max=len(parameter)+10)
slider.observe(update)
display(widgets.VBox([slider, fig.canvas]))
display(out)