mpl-gui Dokumentation#
Motivation#
Dieses Projekt ist ein Prototypraum zur Überarbeitung der GUI-Event-Loop-Management-Tools, die Matplotlib in pyplot bereitstellt.
Das pyplot-Modul erfüllt derzeit zwei kritische, aber nicht verwandte Funktionen:
Bereitstellung einer zustandsbehafteten impliziten API, die MATLAB ähnelt / von MATLAB inspiriert wurde.
Verwaltung der Interaktion zwischen Matplotlib und der GUI-Event-Loop, einschließlich der Aufrechterhaltung von Figuren.
Obwohl dies bei der Arbeit an der Eingabeaufforderung sehr praktisch sein kann, kann die zustandsbehaftete API zu fragilen Codes führen, die auf verwirrende Weise vom globalen Zustand abhängen, insbesondere wenn sie in Bibliotheks-Code verwendet werden. Auf der anderen Seite leistet matplotlib.pyplot sehr gute Arbeit, um dem Benutzer zu verbergen, dass er eine GUI-Anwendung entwickelt und zusammen mit IPython viele Details der parallelen Ausführung einer GUI-Anwendung mit Python übernimmt.
Beispiele#
Wenn Sie sicher sein wollen, dass dieser Code nicht heimlich von pyplot abhängt, führen Sie Folgendes aus:
import sys
sys.modules['matplotlib.pyplot'] = None
was verhindert, dass pyplot importiert wird!
Anzeigen#
Der Kern der API ist show
import mpl_gui as mg
from matplotlib.figure import Figure
fig1 = Figure(label='A Label!')
fig2 = Figure()
mg.show([fig1, fig2])
welches sowohl Figuren anzeigt und blockiert, bis sie geschlossen werden. Als Teil des "Anzeigevorgangs" werden die korrekten GUI-Objekte erstellt, auf dem Bildschirm angezeigt und die Event-Loop des Host-GUI-Frameworks wird ausgeführt.
Blockierend (oder nicht)#
Ähnlich wie bei plt.ion und plt.ioff stellen wir mg.ion() und mg.ioff() bereit, die identische Semantik haben. Somit
import mpl_gui as mg
from matplotlib.figure import Figure
mg.ion()
print(mg.is_interactive())
fig = Figure()
mg.show([fig]) # will not block
mg.ioff()
print(mg.is_interactive())
mg.show([fig]) # will block!
Wie bei plt.show können Sie das blockierende Verhalten von mg.show über das Schlüsselwortargument block explizit steuern.
import mpl_gui as mg
from matplotlib.figure import Figure
fig = Figure(label='control blocking')
mg.show([fig], block=False) # will never block
mg.show([fig], block=True) # will always block
Der interaktive Zustand wird von Matplotlib gemeinsam genutzt und kann auch mit matplotlib.interactive gesteuert und über matplotlib.is_interactive abgefragt werden.
Figuren- und Achsenerstellung#
In Analogie zu matplotlib.pyplot stellen wir auch figure, subplots und subplot_mosaic bereit.
import mpl_gui as mg
fig1 = mg.figure()
fig2, axs = mg.subplots(2, 2)
fig3, axd = mg.subplot_mosaic('AA\nBC')
mg.show([fig1, fig2, fig3])
Wenn mpl_gui im "interaktiven Modus" ist, werden mpl_gui.figure, mpl_gui.subplots und mpl_gui.subplot_mosaic die neue Figur automatisch in einem Fenster auf dem Bildschirm anzeigen (aber die Event-Loop nicht ausführen).
FigureRegistry#
In den obigen Beispielen liegt es in der Verantwortung des Benutzers, die erstellten Figure-Instanzen zu verfolgen. Wenn der Benutzer keine harte Referenz auf das fig-Objekt behält, weder direkt noch indirekt über seine Kinder, wird es wie jedes andere Python-Objekt vom Garbage Collector entfernt. Dies kann in einigen Fällen vorteilhaft sein (z. B. bei Skripten oder Funktionen, die viele transiente Figuren erstellen). Es geht der Komfort verloren, den matplotlib.pyplot bietet, indem es die Instanzen für Sie verfolgt. Zu diesem Zweck haben wir auch FigureRegistry bereitgestellt.
import mpl_gui as mg
fr = mg.FigureRegistry()
fr.figure()
fr.subplots(2, 2)
fr.subplot_mosaic('AA\nBC')
fr.show_all() # will show all three figures
fr.show() # alias for pyplot compatibility
fr.close_all() # will close all three figures
fr.close('all') # alias for pyplot compatibility
Wenn Sie also nur diesen eingeschränkten Teil der pyplot-API verwenden, können Sie Folgendes ändern:
import matplotlib.pyplot as plt
zu
import mpl_gui as mg
plt = mg.FigureRegistry()
und haben einen (weitgehend) Drop-in-Ersatz.
Zusätzlich gibt es eine FigureRegistry.by_label-Hilfsmethode, die ein Wörterbuch zurückgibt, das die Labels der Figuren mit jeder Figur abbildet.
import mpl_gui as mg
fr = mg.FigureRegistry()
figA = fr.figure(label='A')
figB = fr.subplots(2, 2, label='B')
fr.by_label['A'] is figA
fr.by_label['B'] is figB
FigureContext#
Ein sehr gängiger Anwendungsfall ist die Erstellung mehrerer Figuren und deren gemeinsame Anzeige am Ende. Um dies zu erleichtern, bieten wir eine Unterklasse von FigureRegistry an, die als Kontextmanager verwendet werden kann und die erstellten Figuren (lokal) verfolgt und beim Beenden anzeigt.
import mpl_gui as mg
with mg.FigureContext() as fc:
fc.subplot_mosaic('AA\nBC')
fc.figure()
fc.subplots(2, 2)
Dies erstellt 3 Figuren und blockiert bei __exit__. Das blockierende Verhalten hängt von mg.is_interacitve() ab (und folgt dem Verhalten von mg.show oder kann explizit über das Schlüsselwortargument block gesteuert werden).
Auswahl des GUI-Toolkits#
mpl_gui verwendet Matplotlib-Backends für die tatsächliche Bereitstellung der GUI-Bindings. Analog zu matplotlib.use und matplotlib.pyplot.switch_backend bietet mpl_gui mpl_gui.select_gui_toolkit zur Auswahl des verwendeten GUI-Toolkits. select_gui_toolkit hat dasselbe Fallback-Verhalten wie pyplot und speichert seinen Zustand in rcParams["backend"]. mpl_gui wird konsequent mit von matplotlib.pyplot verwalteten Figuren im selben Prozess koexistieren.