Gibt es eine Möglichkeit, Matplotlib-Diagramme zu trennen, damit die Berechnung fortgesetzt werden kann?


258

Nach diesen Anweisungen im Python-Interpreter erhält man ein Fenster mit einem Plot:

from matplotlib.pyplot import *
plot([1,2,3])
show()
# other code

Leider weiß ich nicht, wie ich die von erstellte Figur weiterhin interaktiv untersuchen soll, show()während das Programm weitere Berechnungen durchführt.

Ist das überhaupt möglich? Manchmal sind die Berechnungen lang und es wäre hilfreich, wenn sie während der Prüfung der Zwischenergebnisse fortgesetzt würden.


5
Ich kann nicht bestätigen, dass die ausgewählte Lösung von nosklo am 16:52 funktioniert. Für mich öffnet Zeichnen kein Fenster, um das Diagramm anzuzeigen, nur die Blockierungsshow am Ende zeigt die Lösung an. Seine Antwort von 17:00 ist jedoch richtig. Das Einschalten des interaktiven Modus über ion()behebt das Problem.
H. Brandsmeier

Wenn Sie ein fortgeschrittener Programmierer sind, können Sie verwenden, os.fork()aber denken Sie daran, dass die Verwendung os.fork()schwierig sein kann, da Sie einen neuen Prozess erstellen, indem Sie den alten Prozess kopieren.
Trevor Boyd Smith

Antworten:


214

Verwenden Sie matplotlibAnrufe, die nicht blockieren:

Verwenden von draw():

from matplotlib.pyplot import plot, draw, show
plot([1,2,3])
draw()
print 'continue computation'

# at the end call show to ensure window won't close.
show()

Interaktiven Modus verwenden:

from matplotlib.pyplot import plot, ion, show
ion() # enables interactive mode
plot([1,2,3]) # result shows immediatelly (implicit draw())

print 'continue computation'

# at the end call show to ensure window won't close.
show()

28
Mit matplotlib 0.98.3 ist der richtige Import von matplotlib.pyplot Import Plot, Zeichnen, Zeigen
Meteore

112
draw()funktioniert bei mir nicht, es öffnet kein Fenster. Die Verwendung von show(block=False)anstelle von draw()scheint jedoch in matplotlib 1.1 den Trick zu tun.
Rumpel

4
@nosklo, hast du gesehen? Sie haben es zu einem Python- Tutorial gemacht
Januar

4
@noskolo was ist, wenn ich mehrere Figuren habe, wie man Fig1 zeichnet und zeigt, während man den Hintergrund fortsetzt, um fortzufahren? Ich möchte, dass diese Figur geöffnet ist, bis die nächste Figur generiert wird. Am Ende habe ich alle Feigen geöffnet und der Code ist fertig. Mit Ihrer aktuellen Lösung muss ich darauf warten, Abb. 1 zu schließen, und dann geht der Code weiter. Vielen Dank!!
Physiker

9
draw()hat auch bei mir nicht funktioniert, nur pause(0.001): stackoverflow.com/questions/28269157/…
NumesSanguis

133

Verwenden Sie das Schlüsselwort 'block', um das Blockierungsverhalten zu überschreiben, z

from matplotlib.pyplot import show, plot

plot(1)  
show(block=False)

# your code

um deinen Code fortzusetzen.


17
Dadurch wird das Plotfenster jedoch sofort geschlossen und der Plot nicht geöffnet.
LWZ

8
Ja, das stimmt, wenn Sie Ihr Skript über die Befehlszeile aufrufen. Wenn Sie sich in der Ipython-Shell befinden, wird das Fenster nicht geschlossen.
Januar

1
Überprüfen Sie die Antwort von @Nico auf einen Trick, der das Fenster im allgemeinen Fall offen lässt.
Jan

2
Für mich schließt dies das Fenster nicht sofort, nur wenn das Skript fertig ist (Sie können es also am Ende des Skripts manuell blockieren, wenn Sie möchten, dass es geöffnet bleibt).
Luator

Ja, die nicht blockierten Fenster werden geschlossen, wenn das Skript beendet wird . Sie können entweder (a) das Blockieren Ihres letzten Plots zulassen oder (b) das Skript nicht beenden (fragen Sie möglicherweise nach einer Eingabe: "Drücken Sie die <Eingabetaste>, um das Plot zu beenden" oder ähnliches).
Daniel Goldfarb

29

Es ist besser, immer bei der Bibliothek zu überprüfen, die Sie verwenden, wenn sie die Verwendung auf nicht blockierende Weise unterstützt.

Wenn Sie jedoch eine allgemeinere Lösung wünschen oder wenn es keine andere Möglichkeit gibt, können Sie mithilfe des multprocessingin Python enthaltenen Moduls alles ausführen, was in einem separaten Prozess blockiert . Die Berechnung wird fortgesetzt:

from multiprocessing import Process
from matplotlib.pyplot import plot, show

def plot_graph(*args):
    for data in args:
        plot(data)
    show()

p = Process(target=plot_graph, args=([1, 2, 3],))
p.start()

print 'yay'
print 'computation continues...'
print 'that rocks.'

print 'Now lets wait for the graph be closed to continue...:'
p.join()

Dies hat den Aufwand, einen neuen Prozess zu starten, und ist in komplexen Szenarien manchmal schwieriger zu debuggen. Daher würde ich die andere Lösung bevorzugen (unter Verwendung matplotlibder nicht blockierenden API-Aufrufe ).


Vielen Dank! Da ich Python 2.6 noch nicht auf meinem System habe, habe ich threading.Thread als Ersatz für Process verwendet. Ich habe festgestellt, dass nachfolgende Druckanweisungen unerträglich langsam werden (dritter Druck, ich habe KeyboardInterrupt nach 1 Minute Wartezeit ausgegeben). Ist dies ein Effekt der Verwendung von Threading anstelle von Multiprocessing?
Meteore

@meteore: Ja, Threading ist scheiße. Sie können Multiprocessing für Python <2.6 immer von hier erhalten: pyprocessing.berlios.de
nosklo

Das ist absolut hervorragend. Haben Sie eine Idee, warum die print-Anweisungen im Emacs-Modus (Python-Modus) erst ausgeführt werden, wenn das Plotfenster geschlossen wird?
Meteore

In Ubuntu 8.10 (Intrepid) heißt das Paket (für Python <2.6) Python-Verarbeitung und Sie importieren es mit 'Import Processing'
meteore

1
Hast du das nicht verpasst if __name__ == '__main__':?
Wernight

25

Versuchen

import matplotlib.pyplot as plt
plt.plot([1,2,3])
plt.show(block=False)
# other code
# [...]

# Put
plt.show()
# at the very end of your script to make sure Python doesn't bail out
# before you finished examining.

Die show()Dokumentation sagt:

Zeigen Sie im nicht interaktiven Modus alle Figuren an und blockieren Sie sie, bis die Figuren geschlossen wurden. Im interaktiven Modus hat dies keine Auswirkung, es sei denn, die Zahlen wurden vor dem Wechsel vom nicht interaktiven in den interaktiven Modus erstellt (nicht empfohlen). In diesem Fall werden die Zahlen angezeigt, aber nicht blockiert.

Ein einzelnes experimentelles Schlüsselwortargument, block, kann auf True oder False gesetzt werden, um das oben beschriebene Blockierungsverhalten zu überschreiben.


Warum nicht draw () verwenden? [.anderer Code]; Show() ? Soweit ich weiß, ist block = False veraltet
Bogdan

Warum denkst du, ist es veraltet? Ich sehe es hier dokumentiert .
Nico Schlömer

11

WICHTIG : Nur um etwas klar zu machen. Ich gehe davon aus, dass sich die Befehle in einem .pySkript befinden und das Skript zB python script.pyvon der Konsole aus aufgerufen wird .

Ein einfacher Weg, der für mich funktioniert, ist:

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

Beispiel einer script.py Datei:

plt.imshow(*something*)                                                               
plt.colorbar()                                                                             
plt.xlabel("true ")                                                                   
plt.ylabel("predicted ")                                                              
plt.title(" the matrix")  

# Add block = False                                           
plt.show(block = False)

################################
# OTHER CALCULATIONS AND CODE HERE ! ! !
################################

# the next command is the last line of my script
plt.show()



8

In meinem Fall wollte ich, dass mehrere Fenster während der Berechnung angezeigt werden. Als Referenz ist dies der Weg:

from matplotlib.pyplot import draw, figure, show
f1, f2 = figure(), figure()
af1 = f1.add_subplot(111)
af2 = f2.add_subplot(111)
af1.plot([1,2,3])
af2.plot([6,5,4])
draw() 
print 'continuing computation'
show()

PS. Eine sehr nützliche Anleitung zur OO-Schnittstelle von matplotlib .


6

Nun, ich hatte große Probleme, die nicht blockierenden Befehle herauszufinden ... Aber schließlich gelang es mir, das Beispiel " Kochbuch / Matplotlib / Animationen - Ausgewählte Plotelemente animieren " zu überarbeiten , sodass es mit Threads funktioniert ( und Daten auch zwischen Threads Pipeweitergibt) über globale Variablen oder über einen Multiprozess ) unter Python 2.6.5 unter Ubuntu 10.04.

Das Skript finden Sie hier: Animating_selected_plot_elements-thread.py - ansonsten unten eingefügt ( mit weniger Kommentaren ) als Referenz:

import sys
import gtk, gobject
import matplotlib
matplotlib.use('GTKAgg')
import pylab as p
import numpy as nx 
import time

import threading 



ax = p.subplot(111)
canvas = ax.figure.canvas

# for profiling
tstart = time.time()

# create the initial line
x = nx.arange(0,2*nx.pi,0.01)
line, = ax.plot(x, nx.sin(x), animated=True)

# save the clean slate background -- everything but the animated line
# is drawn and saved in the pixel buffer background
background = canvas.copy_from_bbox(ax.bbox)


# just a plain global var to pass data (from main, to plot update thread)
global mypass

# http://docs.python.org/library/multiprocessing.html#pipes-and-queues
from multiprocessing import Pipe
global pipe1main, pipe1upd
pipe1main, pipe1upd = Pipe()


# the kind of processing we might want to do in a main() function,
# will now be done in a "main thread" - so it can run in
# parallel with gobject.idle_add(update_line)
def threadMainTest():
    global mypass
    global runthread
    global pipe1main

    print "tt"

    interncount = 1

    while runthread: 
        mypass += 1
        if mypass > 100: # start "speeding up" animation, only after 100 counts have passed
            interncount *= 1.03
        pipe1main.send(interncount)
        time.sleep(0.01)
    return


# main plot / GUI update
def update_line(*args):
    global mypass
    global t0
    global runthread
    global pipe1upd

    if not runthread:
        return False 

    if pipe1upd.poll(): # check first if there is anything to receive
        myinterncount = pipe1upd.recv()

    update_line.cnt = mypass

    # restore the clean slate background
    canvas.restore_region(background)
    # update the data
    line.set_ydata(nx.sin(x+(update_line.cnt+myinterncount)/10.0))
    # just draw the animated artist
    ax.draw_artist(line)
    # just redraw the axes rectangle
    canvas.blit(ax.bbox)

    if update_line.cnt>=500:
        # print the timing info and quit
        print 'FPS:' , update_line.cnt/(time.time()-tstart)

        runthread=0
        t0.join(1)   
        print "exiting"
        sys.exit(0)

    return True



global runthread

update_line.cnt = 0
mypass = 0

runthread=1

gobject.idle_add(update_line)

global t0
t0 = threading.Thread(target=threadMainTest)
t0.start() 

# start the graphics update thread
p.show()

print "out" # will never print - show() blocks indefinitely! 

Hoffe das hilft jemandem,
Prost!


5

In vielen Fällen ist es bequemer, das Bild als PNG-Datei auf der Festplatte zu speichern. Hier ist warum:

Vorteile:

  • Sie können es jederzeit öffnen, ansehen und schließen. Dies ist besonders praktisch, wenn Ihre Anwendung längere Zeit ausgeführt wird.
  • Es erscheint nichts und Sie müssen die Fenster nicht öffnen. Dies ist besonders praktisch, wenn Sie mit vielen Zahlen zu tun haben.
  • Ihr Bild ist zur späteren Bezugnahme zugänglich und geht beim Schließen des Figurenfensters nicht verloren.

Nachteil:

  • Das einzige, woran ich denken kann, ist, dass Sie den Ordner suchen und das Bild selbst öffnen müssen.

Wenn Sie versuchen, viele Bilder zu generieren, stimme ich Ihnen von Herzen zu.
fantastisch

6
Die Rückzugs-PNGs sind nicht interaktiv: \
Inverse

5

Wenn Sie in der Konsole arbeiten, können IPythonSie diese plt.show(block=False)wie in den anderen Antworten angegeben verwenden. Aber wenn Sie faul sind, können Sie einfach Folgendes eingeben:

plt.show(0)

Welches wird das gleiche sein.


5

Ich musste plt.pause(0.001)meinen Code auch ergänzen , damit er wirklich in einer for-Schleife funktioniert (andernfalls würde nur der erste und der letzte Plot angezeigt):

import matplotlib.pyplot as plt

plt.scatter([0], [1])
plt.draw()
plt.show(block=False)

for i in range(10):
    plt.scatter([i], [i+1])
    plt.draw()
    plt.pause(0.001)

Dies funktionierte bei mir mit matplotlib3 unter macOS. Toll!
Jerry Ma

4

Auf meinem System blockiert show () nicht, obwohl ich wollte, dass das Skript darauf wartet, dass der Benutzer mit dem Diagramm interagiert (und Daten mithilfe von 'pick_event'-Rückrufen sammelt), bevor ich fortfahre.

Um die Ausführung zu blockieren, bis das Plotfenster geschlossen wird, habe ich Folgendes verwendet:

fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.plot(x,y)

# set processing to continue when window closed
def onclose(event):
    fig.canvas.stop_event_loop()
fig.canvas.mpl_connect('close_event', onclose)

fig.show() # this call does not block on my system
fig.canvas.start_event_loop_default() # block here until window closed

# continue with further processing, perhaps using result from callbacks

Beachten Sie jedoch, dass canvas.start_event_loop_default () die folgende Warnung ausgegeben hat:

C:\Python26\lib\site-packages\matplotlib\backend_bases.py:2051: DeprecationWarning: Using default event loop until function specific to this GUI is implemented
  warnings.warn(str,DeprecationWarning)

obwohl das Skript noch lief.


Danke dir! Spyder importiert -pylab beim Start, was im Allgemeinen nützlich ist, aber bedeutet, dass show () nicht blockiert, wenn ioff () - dies ermöglicht es Ihnen, dieses Verhalten zu beheben!
verlor

3

Ich wollte auch, dass meine Diagramme den Rest des Codes ausführen (und dann weiterhin angezeigt werden), auch wenn ein Fehler vorliegt (ich verwende manchmal Diagramme zum Debuggen). Ich habe diesen kleinen Hack so codiert, dass sich alle Handlungen in dieser withAnweisung als solche verhalten.

Dies ist wahrscheinlich etwas zu unüblich und für den Produktionscode nicht ratsam. In diesem Code sind wahrscheinlich viele versteckte "Fallstricke" enthalten.

from contextlib import contextmanager

@contextmanager
def keep_plots_open(keep_show_open_on_exit=True, even_when_error=True):
    '''
    To continue excecuting code when plt.show() is called
    and keep the plot on displaying before this contex manager exits
    (even if an error caused the exit).
    '''
    import matplotlib.pyplot
    show_original = matplotlib.pyplot.show
    def show_replacement(*args, **kwargs):
        kwargs['block'] = False
        show_original(*args, **kwargs)
    matplotlib.pyplot.show = show_replacement

    pylab_exists = True
    try:
        import pylab
    except ImportError: 
        pylab_exists = False
    if pylab_exists:
        pylab.show = show_replacement

    try:
        yield
    except Exception, err:
        if keep_show_open_on_exit and even_when_error:
            print "*********************************************"
            print "Error early edition while waiting for show():" 
            print "*********************************************"
            import traceback
            print traceback.format_exc()
            show_original()
            print "*********************************************"
            raise
    finally:
        matplotlib.pyplot.show = show_original
        if pylab_exists:
            pylab.show = show_original
    if keep_show_open_on_exit:
        show_original()

# ***********************
# Running example
# ***********************
import pylab as pl
import time
if __name__ == '__main__':
    with keep_plots_open():
        pl.figure('a')
        pl.plot([1,2,3], [4,5,6])     
        pl.plot([3,2,1], [4,5,6])
        pl.show()

        pl.figure('b')
        pl.plot([1,2,3], [4,5,6])
        pl.show()

        time.sleep(1)
        print '...'
        time.sleep(1)
        print '...'
        time.sleep(1)
        print '...'
        this_will_surely_cause_an_error

Wenn / wenn ich ein ordnungsgemäßes "Halten Sie die Diagramme offen (auch wenn ein Fehler auftritt) und das Anzeigen neuer Diagramme zulasse" implementiere, möchte ich, dass das Skript ordnungsgemäß beendet wird, wenn keine Benutzereingriffe dies anders angeben (für Stapelausführungszwecke).

Ich verwende möglicherweise eine Timeout-Frage "Ende des Skripts! \ NDrücken Sie p, wenn die Plotausgabe angehalten werden soll (Sie haben 5 Sekunden Zeit):" von /programming/26704840/corner -cases-for-my-wait-for-User-Input-Interruption-Implementierung .


2
plt.figure(1)
plt.imshow(your_first_image)

plt.figure(2)
plt.imshow(your_second_image)

plt.show(block=False) # That's important 

raw_input("Press ENTER to exist") # Useful when you run your Python script from the terminal and you want to hold the running to see your figures until you press Enter

16
Wie würde man die Eingabetaste drücken, bevor es existiert?
Grovina

2

Das OP fragt nach dem Entpacken von matplotlibParzellen. Die meisten Antworten setzen die Befehlsausführung in einem Python-Interpreter voraus. Der hier vorgestellte Anwendungsfall ist meine Präferenz für das Testen von Code in einem Terminal (z. B. Bash), in dem a ausgeführt file.pywird, und Sie möchten, dass die Handlung (en) angezeigt werden, das Python-Skript jedoch abgeschlossen wird und zu einer Eingabeaufforderung zurückkehrt.

Diese eigenständige Datei wird verwendet multiprocessing, um einen separaten Prozess zum Plotten von Daten mit zu starten matplotlib. Der Haupt-Thread wird mit dem os._exit(1)in diesem Beitrag erwähnten beendet . Die os._exit()Hauptkräfte verlassen das System, lassen den matplotlibuntergeordneten Prozess jedoch lebendig und reagieren, bis das Plotfenster geschlossen wird. Es ist ein völlig separater Prozess.

Dieser Ansatz ähnelt einer Matlab-Entwicklungssitzung mit Figurenfenstern, die eine reaktionsschnelle Eingabeaufforderung enthalten. Mit diesem Ansatz haben Sie jeglichen Kontakt zum Prozess des Figurenfensters verloren, aber das ist für die Entwicklung und das Debuggen in Ordnung. Schließen Sie einfach das Fenster und testen Sie weiter.

multiprocessingist für die reine Python-Codeausführung konzipiert, wodurch es möglicherweise besser geeignet ist als subprocess. multiprocessingist plattformübergreifend, daher sollte dies unter Windows oder Mac mit geringen oder keinen Anpassungen gut funktionieren. Das zugrunde liegende Betriebssystem muss nicht überprüft werden. Dies wurde unter Linux Ubuntu 18.04LTS getestet.

#!/usr/bin/python3

import time
import multiprocessing
import os

def plot_graph(data):
    from matplotlib.pyplot import plot, draw, show
    print("entered plot_graph()")
    plot(data)
    show() # this will block and remain a viable process as long as the figure window is open
    print("exiting plot_graph() process")

if __name__ == "__main__":
    print("starting __main__")
    multiprocessing.Process(target=plot_graph, args=([1, 2, 3],)).start()
    time.sleep(5)
    print("exiting main")
    os._exit(0) # this exits immediately with no cleanup or buffer flushing

Beim Ausführen file.pywird ein Figurenfenster geöffnet und dann beendet. __main__Das multiprocessing+ matplotlibFigurenfenster reagiert jedoch weiterhin mit Zoom-, Schwenk- und anderen Schaltflächen, da es sich um einen unabhängigen Vorgang handelt.

Überprüfen Sie die Prozesse an der bash-Eingabeaufforderung mit:

ps ax|grep -v grep |grep file.py


Ich habe versucht, Ihre Lösung zu verwenden, aber es scheint für mich nicht zu funktionieren, und ich versuche herauszufinden, warum. Ich führe den Code nicht über das Terminal aus, sondern über die Pycharm-IDE, wenn dies einen Unterschied macht, obwohl dies nicht der Fall sein sollte.
ttsesm

1
ok, was schließlich für mich funktioniert hat, war das Einstellen des untergeordneten Prozesses mit .daemon=Falsewie hier beschrieben stackoverflow.com/a/49607287/1476932 Der sys.exit()übergeordnete Prozess wurde jedoch nicht wie dort beschrieben beendet, bis ich das untergeordnete Fenster geschlossen habe. Auf der anderen Seite hat die Verwendung os._exit(0)aus dem obigen Beispiel funktioniert.
ttsesm


0

Wenn Sie mehrere Figuren öffnen möchten, während alle geöffnet bleiben, hat dieser Code für mich funktioniert:

show(block=False)
draw()

show (block = False) ist veraltet und funktioniert jetzt nicht mehr
Bogdan

0

Während ich die Anfrage des OP nicht direkt beantworte, poste ich diese Problemumgehung, da sie jemandem in dieser Situation helfen kann:

  • Ich erstelle eine .exe mit pyinstaller, da ich Python nicht dort installieren kann, wo ich die Plots generieren muss. Daher benötige ich das Python-Skript, um das Plot zu generieren, es als .png zu speichern, es zu schließen und mit dem nächsten fortzufahren, das als mehrere Plots in implementiert ist eine Schleife oder mit einer Funktion.

dafür benutze ich:

import matplotlib.pyplot as plt
#code generating the plot in a loop or function
#saving the plot
plt.savefig(var+'_plot.png',bbox_inches='tight', dpi=250) 
#you can allways reopen the plot using
os.system(var+'_plot.png') # unfortunately .png allows no interaction.
#the following avoids plot blocking the execution while in non-interactive mode
plt.show(block=False) 
#and the following closes the plot while next iteration will generate new instance.
plt.close() 

Wobei "var" das Diagramm in der Schleife identifiziert, damit es nicht überschrieben wird.


-1

Verwenden Sie plt.show(block=False)und am Ende Ihres Skriptaufrufsplt.show() .

Dadurch wird sichergestellt, dass das Fenster nach Abschluss des Skripts nicht geschlossen wird.


Siehe @ nico-schlömers Antwort
Josh Wolff
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.