MEP27: Pyplot von Backends entkoppeln #
Status #
Fortschritt
Branches und Pull-Requests #
Haupt-PR (einschließlich GTK3):
Backend-spezifische Branch-Diffs:
Zusammenfassung #
Dieser MEP überarbeitet die Backends, um eine strukturiertere und konsistentere API zu erhalten, entfernt generischen Code und konsolidiert bestehenden Code. Dazu schlagen wir eine Aufteilung vor:
FigureManagerBase
und ihre abgeleiteten Klassen in die KernfunktionalitätsklasseFigureManager
und eine Backend-spezifische KlasseWindowBase
undShowBase
und seine abgeleiteten Klassen inGcf.show_all
undMainLoopBase
.
Detaillierte Beschreibung #
Dieses MEP zielt darauf ab, die Backend-API in einer einzigen einheitlichen API zu konsolidieren, generischen Code aus dem Backend (einschließlich
_pylab_helpers
und Gcf
) zu entfernen und Code auf eine angemessenere Ebene in matplotlib zu verschieben. Damit entfernen wir automatisch Inkonsistenzen, die in den Backends auftreten, wie z. B.
die manchmal den Canvas setzen und manchmal das gesamte Fenster auf die angegebenen Abmessungen setzen, je nach Backend.FigureManagerBase.resize(w, h)
Two main places for generic code appear in the classes derived from
FigureManagerBase
and ShowBase
.
FigureManagerBase
has three jobs at the moment:The documentation describes it as a Helper class for pyplot mode, wraps everything up into a neat bundle
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 codeself.set_window_title(title)
with matplotlib generic codetitle = "Figure %d" % num
.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.
ShowBase
has two jobs:Es hat die Aufgabe, alle registrierten Figurenmanager durchzugehen
_pylab_helpers.Gcf
und ihnen zu sagen, dass sie sich zeigen sollen.Und zweitens hat es die Aufgabe, das Backend spezifisch auszuführen
mainloop
, um das Hauptprogramm zu blockieren und so das Zahlensterben zu verhindern.
Implementierung #
Die Beschreibung dieses Abgeordneten gibt uns den größten Teil der Lösung:
Um den Windowing-Aspekt aus dem Zulassen zu entfernen,
FigureManagerBase
packen Sie einfach diese neue Klasse zusammen mit den anderen Backend-Klassen ein. Erstellen Sie eine neueWindowBase
Klasse, die diese Funktionalität handhaben kann, mit Pass-Through-Methoden (:arrow_right:) bisWindowBase
. Von Klassen, die eine Unterklasse bilden,WindowBase
sollte auch die GUI-spezifische Fensterklasse abgeleitet werden, um die Abwärtskompatibilität zu gewährleisten ( ).manager.window == manager.window
Refaktorieren Sie die Hauptschleife von
ShowBase
inMainLoopBase
, die auch das Ende der Schleife kapselt. Wir geben eine Instanz vonMainLoop
toFigureManager
als Schlüssel, um die Exit-Methode zu entsperren (wobei alle Schlüssel zurückgegeben werden müssen, bevor die Schleife sterben kann). Beachten Sie, dass dies die Möglichkeit eröffnet, dass mehrere Backends gleichzeitig ausgeführt werden.Nun,
FigureManagerBase
das enthält keine Backend-Besonderheiten, um es in umzubenennenFigureManager
und in eine neue Datei zu verschieben, wobei Folgendes zubackend_managers.py
beachten ist:Dadurch können wir die Konvertierung von Backends in separate PRs aufteilen, da wir die vorhandene
FigureManagerBase
Klasse und ihre Abhängigkeiten intakt halten können.Und dies nimmt auch MEP22 vorweg, wo sich das neue
NavigationBase
in ein unabhängiges Backend verwandelt hatToolManager
.
FigureManagerBase(canvas, num) |
FigureManager(Zahl, Zahl) |
|
Anmerkungen |
---|---|---|---|
Show |
Show |
||
zerstören |
ruft auf allen Komponenten destrue auf |
zerstören |
|
full_screen_toggle |
behandelt Logik |
set_fullscreen |
|
Größe ändern |
Größe ändern |
||
Tastendruck |
Tastendruck |
||
get_window_title |
get_window_title |
||
set_window_title |
set_window_title |
||
_get_toolbar |
Eine gemeinsame Methode für alle Unterklassen von FigureManagerBase |
||
set_default_size |
|||
add_element_to_window |
ShowBase |
MainLoopBase |
Anmerkungen |
---|---|---|
Hauptschleife |
Start |
|
Ende |
Wird automatisch aufgerufen, wenn keine Instanzen der Unterklasse mehr existieren |
|
__Anruf__ |
Methode nach Gcf.show_all verschoben |
Zukunftskompatibilität #
Wie oben bei der Erörterung von MEP 22 umgangen, erleichtert dieser Refactor das Hinzufügen neuer generischer Funktionen. Im Moment muss MEP 22 hässliche Hacks für jede Klasse machen, die von FigureManagerBase
. Mit diesem Code muss dies nur in der Einzelklasse gemacht FigureManager
werden. Dies macht auch die spätere Abwertung
sehr einfach, da nur die einzelne Klasse NavigationToolbar2
berührt werden mussFigureManager
MEP 23 ist ein weiterer Anwendungsfall, bei dem sich dieser umgestaltete Code als sehr nützlich erweisen wird.
Abwärtskompatibilität #
Da wir den gesamten Backend-Code intakt lassen und nur fehlende Methoden zu vorhandenen Klassen hinzufügen, sollte dies für alle Anwendungsfälle nahtlos funktionieren. FigureManager.resize
Der einzige Unterschied besteht aufgrund der Standardisierung der API für Backends, die früher
die Größe der Leinwand und nicht des Fensters geändert haben.
Ich würde mir vorstellen, dass die Klassen, die durch diesen Refactor obsolet gemacht wurden, veraltet sind und im selben Zeitplan wie entfernt
NavigationToolbar2
werden. Beachten Sie auch, dass die Änderung der Aufrufsignatur für den FigureCanvasWx
Konstruktor zwar abwärtskompatibel ist, aber ich denke, dass die alte Signatur (imho hässlicher Stil) veraltet sein sollte und auf die gleiche Weise wie alles andere entfernt.
Backend |
manager.resize(w,h) |
Extra |
---|---|---|
gtk3 |
Fenster |
|
Tk |
Leinwand |
|
Qt |
Fenster |
|
Wx |
Leinwand |
FigureManagerWx hatte
|
Alternativen #
Wenn es alternative Lösungen zur Lösung des gleichen Problems gibt, sollten diese hier diskutiert werden, zusammen mit einer Begründung für den gewählten Ansatz.
Fragen #
Mdehoon: Können Sie erläutern, wie Sie mehrere Backends gleichzeitig ausführen können?
OceanWolf: @mdehoon, wie ich schon sagte, nicht für diesen Abgeordneten, aber ich sehe, dass dieser Abgeordnete es als zukünftige Möglichkeit eröffnet. Grundsätzlich fungiert die MainLoopBase
Klasse als Gcf pro Backend, in diesem MEP verfolgt sie die Anzahl der pro Backend geöffneten Figuren und verwaltet die Hauptschleifen für diese Backends. Es schließt die Backend-spezifische Hauptschleife, wenn es feststellt, dass für dieses Backend keine Zahlen offen bleiben. Aus diesem Grund stelle ich mir vor, dass wir mit nur wenigen Anpassungen eine vollständige Multi-Backend-Matplotlib erstellen können. Noch keine Ahnung, warum man das wollen sollte, aber ich lasse die Möglichkeit in MainLoopBase da. Mit all den Back-End-Code-Besonderheiten, die FigureManager
auch umgestaltet wurden, hilft dabei ein Manager, der sie (die Back-Ends) alle regiert.
Mdehoon: @OceanWolf, OK, danke für die Erklärung. Eine einheitliche API für die Backends zu haben, ist sehr wichtig für die Wartbarkeit von matplotlib. Ich denke, dieser Abgeordnete ist ein Schritt in die richtige Richtung.