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

StackOverflow https://stackoverflow.com/questions/458209

  •  19-08-2019
  •  | 
  •  

Domanda

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 interattivamente la figura creata da show () mentre il programma esegue ulteriori calcoli.

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

È stato utile?

Soluzione

Utilizza le chiamate di matplotlib che non si bloccano:

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

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

Altri suggerimenti

Utilizza 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.

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

Ma se vuoi una soluzione più generica o se non c'è altro modo, puoi eseguire qualsiasi cosa blocchi in un processo separato usando multprocessing 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 su scenari complessi, quindi preferirei l'altra soluzione (usando matplotlib s chiamate API non bloccate )

Prova

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

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

La show () dice:

  

In modalità non interattiva, mostra 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, block , può essere impostato su True o False per sovrascrivere il comportamento di blocco descritto sopra.

Potresti voler leggere questo documento nella documentazione di matplotlib , intitolata:

Uso di matplotlib in una shell python

IMPORTANTE : solo per chiarire qualcosa. Presumo che i comandi si trovino all'interno di uno script .py e che lo script venga chiamato usando ad es. python 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 show () 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()

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 abbastanza utile per l'interfaccia OO di matplotlib .

Beh, ho avuto grossi problemi a capire i comandi non bloccanti ... Ma alla fine sono riuscito a rielaborare il " Libro di cucina / Matplotlib / Animazioni - Animazione degli elementi della trama selezionati " esempio, quindi funziona con i thread ( e passa i dati tra i thread tramite variabili globali o attraverso un Pipe multiprocesso su Python 2.6.5 su Ubuntu 10.04.

Lo script è disponibile 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,
Cheers!

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

Vantaggi:

  • Puoi aprirlo, guardarlo e chiuderlo in qualsiasi momento del processo. Ciò è particolarmente utile quando l'applicazione è in esecuzione a lungo tempo.
  • Non viene visualizzato nulla e non sei obbligato ad aprire le finestre. Ciò è 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.

Svantaggio:

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

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

plt.show(0)

Quale sarà lo stesso.

Ho dovuto anche aggiungere plt.pause (0.001) al mio codice per farlo funzionare davvero all'interno di un ciclo for (altrimenti mostrerebbe solo il primo e l'ultimo grafico):

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)

Volevo anche che i miei grafici fossero visualizzati per eseguire il resto del codice (e quindi continuare a essere visualizzati) anche se si verifica un errore (a volte uso i grafici per il debug). Ho codificato questo piccolo hack in modo che qualsiasi trama all'interno di questa istruzione con 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 una corretta "tenere aperte le trame (anche se si verifica un errore) e consentire la visualizzazione di nuove trame", vorrei che lo script uscisse correttamente se nessuna interferenza dell'utente lo dice diversamente (per batch scopi di esecuzione).

Potrei usare qualcosa come una domanda di timeout " Fine dello script! \ nPremere p se si desidera mettere in pausa l'output di stampa (hai 5 secondi): " da https://stackoverflow.com/questions/26704840 / angolo-cases-for-my-attendere-per-user-input-interruzione-implementazione .

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

Per bloccare l'esecuzione fino alla chiusura della finestra della trama, ho usato 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.

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

Secondo me, le risposte in questo thread forniscono metodi che non funzionano per tutti i sistemi e in situazioni più complesse come le animazioni. Suggerisco di dare un'occhiata alla risposta di MiKTeX nel seguente thread, in cui è stato trovato un metodo affidabile: Come attendere fino alla fine dell'animazione matplotlib?

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

show(block=False)
draw()

L'OP chiede di rimuovere i grafici matplotlib . La maggior parte delle risposte presuppone l'esecuzione dei comandi da un interprete python. Il caso d'uso presentato qui è la mia preferenza per testare il codice in un terminale (es. Bash) in cui viene eseguito un file.py e vuoi che vengano visualizzati i grafici ma che lo script python sia completo e tornare a un prompt dei comandi.

Questo file autonomo utilizza multiprocessing per avviare un processo separato per la stampa di dati con matplotlib . Il thread principale viene chiuso utilizzando os._exit (1) menzionato in questo post. os._exit () forza l'uscita principale ma lascia il processo figlio matplotlib vivo e reattivo fino alla chiusura della finestra del diagramma. È 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 figura, 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 sottoprocesso . 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 di file.py fa apparire una finestra di figura, quindi __main__ esce ma la figura multiprocessing + matplotlib la finestra rimane reattiva con zoom, pan e altri pulsanti perché è un processo indipendente.

Controlla i processi al prompt dei comandi di bash con:

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

Usa plt.show (block = False) e alla fine della tua chiamata di script plt.show () .

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

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top