Mit Matplotlib nicht blockierend zeichnen

138

Ich habe in den letzten Tagen mit Numpy und Matplotlib gespielt. Ich habe Probleme beim Versuch, matplotlib plot zu einer Funktion zu machen, ohne die Ausführung zu blockieren. Ich weiß, dass es hier auf SO bereits viele Threads gibt, die ähnliche Fragen stellen, und ich habe ziemlich viel gegoogelt, aber es nicht geschafft, dass dies funktioniert.

Ich habe versucht, show (block = False) zu verwenden, wie einige Leute vorschlagen, aber alles, was ich bekomme, ist ein eingefrorenes Fenster. Wenn ich einfach show () aufrufe, wird das Ergebnis korrekt dargestellt, aber die Ausführung wird blockiert, bis das Fenster geschlossen wird. Von anderen Threads, die ich gelesen habe, vermute ich, dass es vom Backend abhängt, ob show (block = False) funktioniert oder nicht. Ist das richtig? Mein Backend ist Qt4Agg. Könnten Sie sich meinen Code ansehen und mir sagen, wenn Sie etwas falsch sehen? Hier ist mein Code. Vielen Dank für jede Hilfe.

from math import *
from matplotlib import pyplot as plt
print plt.get_backend()



def main():
    x = range(-50, 51, 1)
    for pow in range(1,5):   # plot x^1, x^2, ..., x^4

        y = [Xi**pow for Xi in x]
        print y

        plt.plot(x, y)
        plt.draw()
        #plt.show()             #this plots correctly, but blocks execution.
        plt.show(block=False)   #this creates an empty frozen window.
        _ = raw_input("Press [enter] to continue.")


if __name__ == '__main__':
    main()

PS. Ich habe vergessen zu sagen, dass ich das vorhandene Fenster jedes Mal aktualisieren möchte, wenn ich etwas zeichne, anstatt ein neues zu erstellen.

opetroch
quelle
1
haben Sie versuchen , matplotlib interaktiven Modus mit plt.ion()vor plt.show()? Es sollte dann nicht blockierend sein, da jeder Plot in einem untergeordneten Thread erzeugt wird.
Anzel
@Anzel Ich habe es gerade versucht, aber es scheint keinen Unterschied zu machen.
Opetroch
3
Wie führen Sie Ihr Skript aus? Wenn ich Ihren Beispielcode über die Terminal- / Eingabeaufforderung ausführe, scheint er einwandfrei zu funktionieren, aber ich glaube, ich hatte in der Vergangenheit Probleme, als ich versuchte, solche Dinge über die IPython QtConsole oder IDEs auszuführen.
Marius
1
@ Marius Aha !! Du hast recht. In der Tat starte ich es von der Konsole meiner IDE (PyCharm). Wenn Sie es über die Eingabeaufforderung cmd ausführen, funktioniert plt.show (block = False) einwandfrei! Werde ich zu viel fragen, wenn ich Sie frage, ob Sie eine Idee / Lösung dafür gefunden haben? Vielen Dank!
Opetroch
Es tut mir nicht wirklich leid. Ich verstehe die Details der Interaktion von matplotlib mit der Konsole nicht wirklich, daher wechsle ich im Allgemeinen einfach von der Eingabeaufforderung zur Ausführung, wenn ich diese Dinge erledigen muss matplotlib.
Marius

Antworten:

167

Ich habe lange nach Lösungen gesucht und diese Antwort gefunden .

Es sieht aus wie, um zu bekommen , was Sie (und ich) wollen, müssen Sie die Kombination plt.ion(), plt.show()(nicht block=False) und, was am wichtigsten ist , plt.pause(.001)(oder was auch immer Zeit , die Sie wollen). Die Pause wird benötigt, da die GUI-Ereignisse auftreten, während der Hauptcode im Ruhezustand ist, einschließlich Zeichnen. Es ist möglich, dass dies implementiert wird, indem Zeit von einem schlafenden Thread abgeholt wird. Vielleicht spielen IDEs damit herum - ich weiß es nicht.

Hier ist eine Implementierung, die für mich unter Python 3.5 funktioniert:

import numpy as np
from matplotlib import pyplot as plt

def main():
    plt.axis([-50,50,0,10000])
    plt.ion()
    plt.show()

    x = np.arange(-50, 51)
    for pow in range(1,5):   # plot x^1, x^2, ..., x^4
        y = [Xi**pow for Xi in x]
        plt.plot(x, y)
        plt.draw()
        plt.pause(0.001)
        input("Press [enter] to continue.")

if __name__ == '__main__':
    main()
krs013
quelle
3
Ihre Antwort hat mir sehr geholfen, ein ähnliches Problem zu lösen, das ich hatte. Zuvor hatte ich plt.drawgefolgt, plt.show(block = False)aber dann funktionierte es nicht mehr: Abbildung reagierte nicht, beim Schließen stürzte iPython ab. Meine Lösung bestand darin, jede Instanz zu entfernen plt.draw()und durch zu ersetzen plt.pause(0.001). Anstatt es plt.show(block = False)wie plt.drawzuvor folgen zu lassen , wurde es von plt.ion()und vorangestellt plt.show(). Ich habe jetzt eine, MatplotlibDeprecationWarningaber damit kann ich meine Zahlen zeichnen, also bin ich mit dieser Lösung zufrieden.
blue_chip
3
Beachten Sie, dass Sie in Python 2.7 raw_inputnicht verwenden müssen input. Siehe hier
Chris
Wirklich nützliche Problemumgehung, wenn der reaktive "animierte" Ansatz nicht möglich ist! Weiß jemand, wie man die Abwertungswarnung loswird?
Frederic Fortier
Kann mir bitte jemand sagen, warum ich eine Freezen-Eingabeaufforderung erhalte, wenn ich versuche, plt.ion vor plt.show hinzuzufügen?
Gabriel Augusto
@ GabrielAugusto Ich bin mir nicht sicher, was das verursachen könnte, und ich bin mir nicht ganz sicher, was du meinst. Ich habe dieses Beispiel gerade in Python 3.6 getestet und es funktioniert immer noch. Wenn Sie dasselbe Muster verwendet haben und es einfriert, stimmt möglicherweise etwas mit Ihrer Installation nicht. Sie sollten zuerst prüfen, ob das normale Plotten funktioniert. Wenn Sie etwas anderes ausprobiert haben, gibt es in den Kommentaren nicht viel zu tun. In beiden Fällen können Sie eine separate Frage stellen.
krs013
23

Ein einfacher Trick, der für mich funktioniert, ist der folgende:

  1. Verwenden Sie das Argument block = False in show: plt.show (block = False)
  2. Verwenden Sie eine andere plt.show () am Ende des .py-Skripts.

Beispiel :

import matplotlib.pyplot as plt

plt.imshow(add_something)
plt.xlabel("x")
plt.ylabel("y")

plt.show(block=False)

#more code here (e.g. do calculations and use print to see them on the screen

plt.show()

Hinweis : plt.show()ist die letzte Zeile meines Skripts.

Seralouk
quelle
7
Dies erzeugt (für mich unter Linux, Anaconda, Python 2.7, Standard-Backend) ein leeres Fenster, das bis zum Ende der Ausführung leer bleibt, bis es schließlich ausgefüllt wird. Nicht nützlich für die Aktualisierung eines Plots während der Ausführung. :-(
sh37211
@ sh37211 Ich bin mir nicht sicher, was dein Ziel ist. In einigen Fällen, in denen Sie versuchen, etwas zu zeichnen, aber nach dem Befehl plot andere Befehle haben, ist dies nützlich, da Sie damit die anderen Befehle zeichnen und ausführen können. Weitere Informationen hierzu finden Sie in diesem Beitrag: stackoverflow.com/questions/458209/… . Wenn Sie ein Diagramm aktualisieren möchten, sollte dies eine andere Möglichkeit sein.
Seralouk
17

Sie können das Blockieren der Ausführung vermeiden, indem Sie den Plot in ein Array schreiben und das Array dann in einem anderen Thread anzeigen. Hier ist ein Beispiel für das gleichzeitige Generieren und Anzeigen von Plots mit pf.screen aus pyformulas 0.2.8 :

import pyformulas as pf
import matplotlib.pyplot as plt
import numpy as np
import time

fig = plt.figure()

canvas = np.zeros((480,640))
screen = pf.screen(canvas, 'Sinusoid')

start = time.time()
while True:
    now = time.time() - start

    x = np.linspace(now-2, now, 100)
    y = np.sin(2*np.pi*x) + np.sin(3*np.pi*x)
    plt.xlim(now-2,now+1)
    plt.ylim(-3,3)
    plt.plot(x, y, c='black')

    # If we haven't already shown or saved the plot, then we need to draw the figure first...
    fig.canvas.draw()

    image = np.fromstring(fig.canvas.tostring_rgb(), dtype=np.uint8, sep='')
    image = image.reshape(fig.canvas.get_width_height()[::-1] + (3,))

    screen.update(image)

#screen.close()

Ergebnis:

Sinusanimation

Haftungsausschluss: Ich bin der Betreuer für Pyformeln.

Referenz: Matplotlib: Plot im Numpy-Array speichern

Standardbild
quelle
9

Viele dieser Antworten sind sehr aufgeblasen und nach allem, was ich finden kann, ist die Antwort gar nicht so schwer zu verstehen.

Sie können verwenden, plt.ion()wenn Sie möchten, aber ich fand die Verwendung plt.draw()genauso effektiv

Für mein spezielles Projekt zeichne ich Bilder, aber Sie können plot()oder scatter()oder was auch immer verwenden figimage(), es spielt keine Rolle.

plt.figimage(image_to_show)
plt.draw()
plt.pause(0.001)

Oder

fig = plt.figure()
...
fig.figimage(image_to_show)
fig.canvas.draw()
plt.pause(0.001)

Wenn Sie eine tatsächliche Zahl verwenden.
Ich habe @ krs013 und die Antworten von @Default Picture verwendet, um dies herauszufinden.
Hoffentlich erspart dies jemandem, jede einzelne Figur in einem separaten Thread zu starten oder diese Romane lesen zu müssen, um dies herauszufinden

iggy12345
quelle
3

Live-Plotten

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0, 2 * np.pi, 100)
# plt.axis([x[0], x[-1], -1, 1])      # disable autoscaling
for point in x:
    plt.plot(point, np.sin(2 * point), '.', color='b')
    plt.draw()
    plt.pause(0.01)
# plt.clf()                           # clear the current figure

Wenn die Datenmenge zu groß ist, können Sie die Aktualisierungsrate mit einem einfachen Zähler senken

cnt += 1
if (cnt == 10):       # update plot each 10 points
    plt.draw()
    plt.pause(0.01)
    cnt = 0

Halten des Plots nach dem Beenden des Programms

Dies war mein eigentliches Problem, für das keine zufriedenstellende Antwort gefunden werden konnte. Ich wollte ein Plotten, das nach Abschluss des Skripts nicht geschlossen wurde (wie MATLAB).

Wenn Sie darüber nachdenken, wird das Programm nach Abschluss des Skripts beendet und es gibt keine logische Möglichkeit, den Plot auf diese Weise zu halten. Daher gibt es zwei Optionen

  1. Blockiere das Beenden des Skripts (das ist plt.show () und nicht das, was ich will)
  2. Führen Sie den Plot in einem separaten Thread aus (zu kompliziert).

Das war für mich nicht zufriedenstellend, also fand ich eine andere Lösung außerhalb des Rahmens

SaveToFile und View im externen Viewer

Zu diesem Zweck sollte das Speichern und Anzeigen schnell erfolgen und der Betrachter sollte die Datei nicht sperren und den Inhalt automatisch aktualisieren

Format zum Speichern auswählen

Vektorbasierte Formate sind sowohl klein als auch schnell

  • SVG ist gut, findet aber keinen guten Viewer dafür, außer dem Webbrowser, der standardmäßig manuell aktualisiert werden muss
  • PDF kann Vektorformate unterstützen und es gibt leichtgewichtige Viewer, die Live-Aktualisierungen unterstützen

Schneller Lightweight Viewer mit Live Update

Für PDF gibt es mehrere gute Optionen

  • Unter Windows verwende ich SumatraPDF, das kostenlos, schnell und leicht ist (verwendet für meinen Fall nur 1,8 MB RAM).

  • Unter Linux gibt es verschiedene Optionen wie Evince (GNOME) und Ocular (KDE).

Beispielcode & Ergebnisse

Beispielcode für die Ausgabe eines Plots in eine Datei

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0, 2 * np.pi, 100)
y = np.sin(2 * x)
plt.plot(x, y)
plt.savefig("fig.pdf")

Öffnen Sie nach dem ersten Start die Ausgabedatei in einem der oben genannten Viewer und genießen Sie.

Hier ist ein Screenshot von VSCode neben SumatraPDF. Außerdem ist der Vorgang schnell genug, um eine Semi-Live-Aktualisierungsrate zu erhalten (ich kann bei meinem Setup fast 10 Hz erreichen, wenn ich es nur time.sleep()zwischen den Intervallen verwende). pyPlot, nicht blockierend

Ali80
quelle
2

Iggys Antwort war für mich am einfachsten zu befolgen, aber ich bekam den folgenden Fehler, als ich einen nachfolgenden subplotBefehl ausführte, der nicht da war, als ich gerade ausführte show:

MatplotlibDeprecationWarning: Durch Hinzufügen einer Achse mit denselben Argumenten wie bei einer vorherigen Achse wird derzeit die frühere Instanz wiederverwendet. In einer zukünftigen Version wird immer eine neue Instanz erstellt und zurückgegeben. In der Zwischenzeit kann diese Warnung unterdrückt und das zukünftige Verhalten sichergestellt werden, indem jeder Achseninstanz eine eindeutige Bezeichnung zugewiesen wird.

Um diesen Fehler zu vermeiden, ist es hilfreich, das Diagramm zu schließen (oder zu löschen ), nachdem der Benutzer die Eingabetaste gedrückt hat.

Hier ist der Code, der für mich funktioniert hat:

def plt_show():
    '''Text-blocking version of plt.show()
    Use this instead of plt.show()'''
    plt.draw()
    plt.pause(0.001)
    input("Press enter to continue...")
    plt.close()
Pro Q.
quelle
0

Mit dem gezeichneten Python-Paket können Sie einen Plot in Echtzeit auf nicht blockierende Weise aktualisieren.
Es funktioniert auch mit einer Webcam und OpenCV, um beispielsweise Kennzahlen für jeden Frame zu zeichnen.
Siehe den Originalbeitrag .

Ismael EL ATIFI
quelle