C'è un modo per staccare i grafici matplotlib in modo che il calcolo possa continuare?


258

Dopo queste istruzioni nell'interprete Python si ottiene una finestra con una trama:

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

Sfortunatamente, non so come continuare a esplorare in modo interattivo la figura creata show()mentre il programma esegue ulteriori calcoli.

È possibile affatto? A volte i calcoli sono lunghi e sarebbe utile se procedessero durante l'esame dei risultati intermedi.


5
Non posso confermare che la soluzione selezionata da nosklo su 16:52 funzioni. Per me disegnare non apre una finestra per visualizzare la trama, solo lo spettacolo di blocco alla fine mostra la soluzione. Tuttavia, la sua risposta dalle 17:00 è corretta. L'attivazione della modalità interattiva tramite ion()risolve il problema.
H. Brandsmeier,

se sei un programmatore avanzato, puoi usare os.fork()ma tieni presente che l'utilizzo os.fork()può essere complicato perché stai creando un nuovo processo copiando il vecchio processo.
Trevor Boyd Smith,

Risposte:


214

Uso matplotlib le chiamate che non si bloccano:

Utilizzando 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()

Utilizzando la modalità interattiva:

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
Con matplotlib 0.98.3 l'importazione corretta è da matplotlib.pyplot trama di importazione, disegno, mostra
meteore

112
draw()non funziona per me, non apre nessuna finestra. Comunque usare show(block=False)invece di draw()sembra fare il trucco in matplotlib 1.1.
rumpel

4
@nosklo, hai visto? Hai fatto in un pitone esercitazione
Jan

4
@noskolo cosa succede se ho diverse figure, come tracciare e mostrare Fig1 mentre continuo lo sfondo per continuare? Vorrei che questa figura fosse aperta fino alla successiva generazione del fico, quindi alla fine ho tutti i fichi aperti e il codice è finito. Con la tua attuale soluzione, mi tiene in attesa di chiudere Fig1 e poi il codice continua. Grazie!!
Physiker

9
draw()non ha funzionato neanche per me, solo pause(0.001)fatto: stackoverflow.com/questions/28269157/...
NumesSanguis

133

Utilizzare la parola chiave 'blocco' per sovrascrivere il comportamento di blocco, ad es

from matplotlib.pyplot import show, plot

plot(1)  
show(block=False)

# your code

per continuare il tuo codice.


17
ma questo chiuderà immediatamente la finestra della trama, non manterrà la trama aperta.
LWZ

8
Sì, è vero se chiami il tuo script dalla riga di comando. Se ti trovi nella shell Ipython, la finestra non verrà chiusa.
Jan

1
controlla la risposta di @Nico per un trucco che lascerà la finestra aperta nel caso generale.
Jan

2
Per me, questo non chiude immediatamente la finestra, solo quando lo script è finito (quindi puoi bloccare manualmente alla fine dello script se vuoi che rimanga aperto).
Luator,

Sì, le finestre non bloccate si chiuderanno alla chiusura dello script . Puoi (a) consentire il blocco sull'ultima trama, oppure (b) non uscire dallo script (forse chiedere input: "premi <Invio> per uscire dalla trama" o qualcosa del genere).
Daniel Goldfarb,

29

È meglio verificare sempre con la libreria che si sta utilizzando se supporta l'utilizzo in modo non bloccante .

Ma se vuoi una soluzione più generica, o se non c'è altro modo, puoi eseguire qualsiasi cosa blocchi in un processo separato usando il multprocessingmodulo incluso in Python. Il calcolo continuerà:

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()

Questo ha il sovraccarico di avviare un nuovo processo ed è talvolta più difficile eseguire il debug in scenari complessi, quindi preferirei l'altra soluzione (utilizzando matplotlible chiamate API non bloccanti )


Grazie! Dal momento che non ho ancora Python 2.6 sul mio sistema, ho usato il threading. Thread come sostituto di Process. Ho osservato che le successive dichiarazioni di stampa diventano insopportabilmente lente (terza stampa, ho rilasciato KeyboardInterrupt dopo 1 minuto di attesa). È un effetto dell'utilizzo del threading anziché del multiprocessing?
meteore

@meteore: Sì, il thread fa schifo. Puoi sempre ottenere il multiprocessing per python <2.6 da qui: pyprocessing.berlios.de
nosklo,

Questo è assolutamente eccellente. Hai idea del perché le istruzioni di stampa non vengano eseguite in Emacs (modalità Python) fino alla chiusura della finestra della trama?
meteore

In Ubuntu 8.10 (Intrepid) il pacchetto (per python <2.6) è chiamato elaborazione python e lo si importa con "elaborazione importazione"
meteore

1
Non ti sei perso il if __name__ == '__main__':?
Wernight,

25

Provare

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.

La show()documentazione dice:

In modalità non interattiva, visualizza tutte le figure e blocca fino a quando le figure non sono state chiuse; in modalità interattiva non ha alcun effetto se le figure non sono state create prima di un passaggio dalla modalità non interattiva a quella interattiva (non consigliata). In tal caso visualizza le cifre ma non si blocca.

Un singolo argomento di parola chiave sperimentale, blocco, può essere impostato su Vero o Falso per sovrascrivere il comportamento di blocco descritto sopra.


perché non usare draw (); [.altro codice]; mostrare() ? Per quanto ne so block = False è stato deprecato
Bogdan,

Perché pensi che sia deprecato? Lo vedo documentato qui .
Nico Schlömer,

11

IMPORTANTE : solo per chiarire qualcosa. Presumo che i comandi si trovino all'interno di uno .pyscript e che lo script venga chiamato usando ad espython script.py dalla console.

Un modo semplice che funziona per me è:

  1. Usa il blocco = Falso all'interno di show: plt.show (blocco = Falso)
  2. Usa un altro spettacolo () alla fine dello script .py.

Esempio di script.py file:

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

Nel mio caso, volevo che apparissero diverse finestre mentre venivano calcolate. Per riferimento, questo è il modo:

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. Una guida abbastanza utile all'interfaccia OO di matplotlib .


6

Bene, ho avuto grossi problemi a capire i comandi non bloccanti ... Ma alla fine sono riuscito a rielaborare l'esempio " Ricettario / Matplotlib / Animazioni - Animazione di elementi della trama selezionati ", quindi funziona con i thread ( e passa i dati tra i thread tramite variabili globali o attraverso un multiprocessoPipe ) su Python 2.6.5 su Ubuntu 10.04.

Lo script può essere trovato qui: Animating_selected_plot_elements-thread.py - altrimenti incollato di seguito ( con meno commenti ) come riferimento:

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! 

Spero che questo aiuti qualcuno,
Salute!


5

In molti casi è più conveniente per salvare l'immagine come file .png sul disco rigido. Ecco perché:

vantaggi:

  • Puoi aprirlo, dare un'occhiata e chiuderlo in qualsiasi momento del processo. Ciò è particolarmente utile quando l'applicazione è in esecuzione da molto tempo.
  • Non viene visualizzato nulla e non sei obbligato ad aprire le finestre. Questo è particolarmente utile quando hai a che fare con molte figure.
  • L'immagine è accessibile per riferimento futuro e non viene persa quando si chiude la finestra della figura.

Inconveniente:

  • L'unica cosa che mi viene in mente è che dovrai andare a cercare la cartella e aprire tu stesso l'immagine.

Se stai cercando di generare molte immagini, sono pienamente d'accordo.
fantastico

6
Gli svantaggi del png non sono interattivi: \
Inverso

5

Se lavori in console, ad esempio IPythonpotresti usare plt.show(block=False)come indicato nelle altre risposte. Ma se sei pigro potresti semplicemente digitare:

plt.show(0)

Quale sarà lo stesso.


5

Ho anche dovuto aggiungere plt.pause(0.001)al mio codice per farlo funzionare davvero all'interno di un ciclo for (altrimenti mostrerebbe solo la prima e l'ultima trama):

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)

Questo ha funzionato per me con matplotlib3 su macOS. Grande!
Jerry Ma

4

Nel mio sistema show () non si blocca, anche se volevo che lo script attendesse che l'utente interagisse con il grafico (e raccolga i dati usando i callback 'pick_event') prima di continuare.

Per bloccare l'esecuzione fino alla chiusura della finestra della trama, ho utilizzato quanto segue:

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

Si noti, tuttavia, che canvas.start_event_loop_default () ha prodotto il seguente avviso:

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)

sebbene lo script sia ancora in esecuzione.


Grazie! Spyder importa -pylab all'avvio che è generalmente utile, ma significa che show () non si bloccherà quando ioff () - questo ti permette di correggere questo comportamento!
Perso il

3

Volevo anche che i miei grafici venissero visualizzati per eseguire il resto del codice (e quindi continuare a visualizzarli) anche se si verifica un errore (a volte uso i grafici per il debug). Ho codificato questo piccolo trucco in modo che qualsiasi trama all'interno di questa withaffermazione si comporti come tale.

Questo è probabilmente un po 'troppo non standard e non consigliabile per il codice di produzione. Probabilmente ci sono molti "gotchas" nascosti in questo codice.

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

Se / quando implemento correttamente "mantieni aperti i grafici (anche se si verifica un errore) e consenti la visualizzazione di nuovi grafici", vorrei che lo script uscisse correttamente se nessuna interferenza dell'utente lo dice diversamente (per scopi di esecuzione batch).

Potrei usare qualcosa come una domanda di timeout "Fine dello script! \ NPremi p se vuoi che l'output della stampa venga messo in pausa (hai 5 secondi):" da /programming/26704840/corner -cases-for-my-wait-for-user-input-interruzione-implementazione .


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
Come si preme Invio prima di esistere?
Grovina,

2

L'OP chiede informazioni sulla cattura di matplotlibgrafici. La maggior parte delle risposte presuppone l'esecuzione dei comandi da un interprete python. Il caso d'uso presentato qui è la mia preferenza per il test del codice in un terminale (es. Bash) in cui file.pyviene eseguito a e si desidera che vengano visualizzati i grafici ma lo script python viene completato e ritorna al prompt dei comandi.

Questo file autonomo consente multiprocessingdi avviare un processo separato per la stampa dei dati matplotlib. Il thread principale viene chiuso utilizzando quello os._exit(1)menzionato in questo post. Le os._exit()forze continuano ad uscire ma lascia il matplotlibprocesso figlio vivo e reattivo fino alla chiusura della finestra della trama. È un processo completamente separato.

Questo approccio è un po 'come una sessione di sviluppo di Matlab con finestre di figure che presentano un prompt dei comandi reattivo. Con questo approccio, hai perso tutti i contatti con il processo di figure window, ma va bene per lo sviluppo e il debug. Chiudi la finestra e continua i test.

multiprocessingè progettato per l'esecuzione di codice solo per Python che lo rende forse più adatto di subprocess. multiprocessingè multipiattaforma, quindi dovrebbe funzionare bene su Windows o Mac con poca o nessuna regolazione. Non è necessario controllare il sistema operativo sottostante. Questo è stato testato su Linux, Ubuntu 18.04LTS.

#!/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

L'esecuzione file.pyfa apparire una finestra di figura, quindi si __main__chiude ma la finestra di figura multiprocessing+ matplotlibrimane reattiva con i pulsanti zoom, pan e altri perché è un processo indipendente.

Controlla i processi al prompt dei comandi di bash con:

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


Stavo cercando di usare la tua soluzione ma non sembra funzionare per me e sto cercando di capire perché. Non sto eseguendo il codice attraverso il terminale ma dall'IDE Pycharm se questo fa la differenza, anche se non dovrebbe.
ttsesm,

1
ok, quello che alla fine ha funzionato per me è stato impostare il processo figlio con .daemon=Falsecome descritto qui stackoverflow.com/a/49607287/1476932 Tuttavia, sys.exit()non ho terminato il processo padre come descritto lì fino a quando non ho chiuso la finestra figlio. D'altro canto l'utilizzo os._exit(0)dell'esempio sopra ha funzionato.
ttsesm,


0

Se vuoi aprire più figure, mantenendole tutte aperte, questo codice ha funzionato per me:

show(block=False)
draw()

show (block = False) è stato deprecato e ora non funziona più
Bogdan,

0

Pur non rispondendo direttamente alla richiesta di PO, sto pubblicando questa soluzione alternativa poiché potrebbe aiutare qualcuno in questa situazione:

  • Sto creando un .exe con pyinstaller poiché non riesco a installare Python dove devo generare i grafici, quindi ho bisogno dello script Python per generare il diagramma, salvarlo come .png, chiuderlo e continuare con il successivo, implementato come diversi grafici in un ciclo o usando una funzione.

per questo sto usando:

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() 

Dove "var" identifica la trama nel loop in modo che non venga sovrascritta.


-1

Utilizzare plt.show(block=False)e alla fine della chiamata dello script plt.show().

Ciò assicurerà che la finestra non venga chiusa al termine dello script.


Vedi la risposta di @nico-schlömer
Josh Wolff il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.