wake-up-neo.net

Nicht blockierendes Plotten mit Matplotlib

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 bereits viele Threads hier auf SO gibt, die ähnliche Fragen stellen, und ich habe ziemlich viel gegoogelt, aber es ist mir nicht gelungen, dies zum Laufen zu bringen.

Ich habe versucht, show (block = False) zu verwenden, wie es 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 mitteilen, ob etwas nicht stimmt? 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 __== '__main__':
    main()

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

100
opetroch

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

Es sieht so aus, als ob Sie die Kombination von plt.ion(), plt.show() (nicht mit blocking=False, Das ist veraltet) und benötigen, um zu bekommen, was Sie (und ich) wollen vor allem plt.pause(.001) (oder wann immer Sie wollen). Das 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 die Zeit von einem schlafenden Thread abgeholt wird. IDEs können sich also damit herumschlagen - ich weiß nicht.

Hier ist eine Implementierung, die für mich auf 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 __== '__main__':
    main()
130
krs013

Ein einfacher Trick, der bei mir funktioniert, ist der folgende:

  1. Verwenden Sie das Argument block = False in show: plt.show (block = False)
  2. Verwenden Sie eine weitere 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()

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

17
serafeim

Sie können verhindern, dass die Ausführung blockiert wird, 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 Erzeugen 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:

Sine animation

Haftungsausschluss: Ich bin der Maintainer für Pyformeln.

Referenz: Matplotlib: Plot in Numpy-Array speichern

12
Default picture

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

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

Für mein spezielles Projekt zeichne ich Bilder, aber Sie können plot() oder scatter() oder was auch immer anstelle von figimage() verwenden, 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 Figur verwenden.
Ich habe @ krs013 und @ Default Picture's Antworten verwendet, um dies herauszufinden
Hoffentlich erspart dies jemandem, jede einzelne Figur in einem separaten Thread zu veröffentlichen oder diese Romane nur lesen zu müssen, um dies herauszufinden

3
iggy12345

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

Plot nach Programmende halten

Dies war mein eigentliches Problem, für das ich keine zufriedenstellende Antwort finden konnte. Ich wollte ein Diagramm, das nach dem Beenden des Skripts nicht geschlossen wurde (wie MATLAB).

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

  1. blockiere das Beenden des Skripts (das ist plt.show () und nicht das, was ich will)
  2. führe den Plot auf einem separaten Thread aus (zu kompliziert)

das war für mich nicht zufriedenstellend, deshalb habe ich eine andere Lösung außerhalb des Rahmens gefunden

SaveToFile und View im externen Viewer

Aus diesem Grund sollte das Speichern und Anzeigen sowohl schnell als auch der Betrachter die Datei nicht sperren und den Inhalt automatisch aktualisieren

Format zum Speichern auswählen

vektorbasierte Formate sind klein und schnell

  • [~ # ~] svg [~ # ~] ist gut, kann aber keinen guten Viewer dafür finden, mit Ausnahme des Webbrowsers, der standardmäßig manuell aktualisiert werden muss
  • [~ # ~] pdf [~ # ~] unterstützt Vektorformate und es gibt Lightweight-Viewer, die Live-Updates 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 nur 1,8 MB RAM für meinen Fall)

  • 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, auch der Prozess ist schnell genug, um die Semi-Live-Aktualisierungsrate zu erhalten (ich kann bei meinem Setup fast 10 Hz erreichen, benutze einfach time.sleep() zwischen den Intervallen) pyPlot,Non-Blocking

1
Ali80

Iggys Antwort war für mich am einfachsten zu befolgen, aber bei der Ausführung eines nachfolgenden Befehls subplot, der nicht vorhanden war, als ich gerade show ausgeführt habe, wurde folgende Fehlermeldung ausgegeben:

MatplotlibDeprecationWarning: Beim 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 übergeben wird.

Um diesen Fehler zu vermeiden, ist es hilfreich, den Plot zu schließen (oder clear ), 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()
1
Pro Q

Das Python -Paketdrawow ermöglicht es, einen Plot in Echtzeit nicht blockierend zu aktualisieren.
Es funktioniert auch mit einer Webcam und OpenCV, um beispielsweise Maße für jeden Frame zu zeichnen.
Siehe Originalbeitrag .

0
Ismael EL ATIFI