Czy istnieje sposób na odłączenie wykresów matplotlib, aby obliczenia mogły być kontynuowane?


258

Po tych instrukcjach w interpretatorze Pythona pojawia się okno z wykresem:

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

Niestety nie wiem, jak kontynuować interaktywne badanie postaci stworzonej przez, show()gdy program wykonuje dalsze obliczenia.

Czy to w ogóle możliwe? Czasami obliczenia są długie i pomogłoby, gdyby kontynuowano je podczas badania wyników pośrednich.


5
Nie mogę potwierdzić, że wybrane rozwiązanie z nosklo na 16:52 działa. Dla mnie rysowanie nie otwiera okna, aby wyświetlić fabułę, tylko pokaz blokujący na końcu wyświetla rozwiązanie. Jednak jego odpowiedź od 17:00 jest poprawna. Włączenie trybu interaktywnego przez ion()rozwiązuje problem.
H. Brandsmeier,

Jeśli jesteś zaawansowanym programistą, możesz go używać, os.fork()ale pamiętaj, że korzystanie z niego os.fork()może być trudne, ponieważ tworzysz nowy proces, kopiując stary proces.
Trevor Boyd Smith,

Odpowiedzi:


214

Użyj matplotlibpołączeń, które nie będą blokować:

Używanie 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()

Korzystanie z trybu interaktywnego:

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
W przypadku matplotlib 0.98.3 właściwy import pochodzi z wykresu importu matplotlib.pyplot, rysowania, pokazu
meteore

112
draw()nie działa dla mnie, nie otwiera żadnego okna. Jednak używanie show(block=False)zamiast draw()wydaje się załatwić sprawę w matplotlib 1.1.
rumpel

4
@nosklo, widziałeś? Zrobiłeś go w python tutorialu
Jan

4
@ noskolo co zrobić, jeśli mam kilka figurek, jak wykreślić i pokazać Fig1, kontynuując tło? Chciałbym, aby ta figura była otwarta do momentu wygenerowania następnej figi, więc na koniec mam wszystkie figi otwarte i kod jest gotowy. Dzięki obecnemu rozwiązaniu czekam na zamknięcie rys. 1, a następnie kod jest kontynuowany. Dzięki!!
fizyk

9
draw()też nie działało dla mnie, tylko pause(0.001): stackoverflow.com/questions/28269157/…
NumesSanguis

133

Użyj słowa kluczowego „blok”, aby zastąpić blokowanie, np

from matplotlib.pyplot import show, plot

plot(1)  
show(block=False)

# your code

aby kontynuować kod.


17
ale to natychmiast zamknie okno wykresu, nie spowoduje, że wykres pozostanie otwarty.
LWZ

8
Tak, to prawda, jeśli wywołujesz swój skrypt z wiersza poleceń. Jeśli jesteś w powłoce Ipython, okno nie zostanie zamknięte.
stycznia

1
sprawdź odpowiedź @Nico, aby znaleźć lewę, która w ogólnym przypadku pozostawi okno otwarte.
Jan

2
Dla mnie nie zamyka to okna natychmiast, tylko po zakończeniu skryptu (więc możesz ręcznie zablokować na końcu skryptu, jeśli chcesz, aby pozostawał otwarty).
luator

Tak, niezablokowane okna zamkną się po zakończeniu skryptu . Możesz albo (a) zezwolić na blokowanie ostatniego wykresu lub (b) nie wychodzić ze skryptu (być może poprosić o wprowadzenie: „naciśnij <Enter>, aby wyjść z wykresu” lub coś w tym rodzaju).
Daniel Goldfarb

29

Lepiej zawsze sprawdzać w używanej bibliotece, czy obsługuje ona użycie w sposób nieblokujący .

Ale jeśli chcesz bardziej ogólnego rozwiązania lub jeśli nie ma innego sposobu, możesz uruchomić wszystko, co blokuje w oddzielnym procesie, używając multprocessingmodułu zawartego w pythonie. Obliczenia będą kontynuowane:

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

Że ma napowietrznej rozpoczęcie nowego procesu, a czasami jest trudniejsze do debugowania złożonych scenariuszy, więc wolałbym inne rozwiązanie (przy użyciu matplotlib„s nonblocking wywołań API )


Dzięki! Ponieważ nie mam jeszcze Python 2.6 w swoim systemie, użyłem wątków. Wątek jako substytut procesu. Zauważyłem, że kolejne instrukcje drukowania stają się nieznośnie wolne (trzeci wydruk, wydałem KeyboardInterrupt po 1 minucie oczekiwania). Czy to efekt używania wątków zamiast wieloprocesowego?
meteore

@meteore: Tak, gwintowanie jest do bani. Zawsze możesz uzyskać przetwarzanie wieloprocesorowe dla pytona <2.6 stąd: pyprocessing.berlios.de
nosklo 21.01.2009

To jest absolutnie doskonałe. Czy masz pojęcie, dlaczego instrukcje drukowania nie są wykonywane w trybie Emacsa (tryb python), dopóki okno wydruku nie zostanie zamknięte?
meteore

W Ubuntu 8.10 (Intrepid) pakiet (dla Pythona <2.6) nazywa się przetwarzaniem pythona i importujesz go za pomocą „importowania przetwarzania”
meteore

1
Nie przegapiłeś if __name__ == '__main__':?
Wernight

25

Próbować

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.

show()Dokumentacja mówi:

W trybie nieinteraktywnym wyświetlaj wszystkie cyfry i blokuj, aż zostaną zamknięte; w trybie interaktywnym nie ma żadnego efektu, chyba że dane zostały utworzone przed zmianą z trybu nieinteraktywnego na tryb interaktywny (niezalecane). W takim przypadku wyświetla liczby, ale nie blokuje.

Pojedynczy eksperymentalny argument słowa kluczowego, blok, można ustawić na Prawda lub Fałsz, aby zastąpić opisane powyżej zachowanie blokujące.


dlaczego nie użyć draw (); [inny kod]; pokazać() ? O ile wiem blok = False został wycofany
Bogdan

Jak myślisz, dlaczego to jest przestarzałe? Widzę to tutaj udokumentowane .
Nico Schlömer,

11

WAŻNE : aby coś wyjaśnić. Zakładam, że polecenia znajdują się w .pyskrypcie i skrypt jest wywoływany przy użyciu np. python script.pyZ konsoli.

Prosty sposób, który działa dla mnie to:

  1. Użyj bloku = False inside show: plt.show (block = False)
  2. Użyj innego show () na końcu skryptu .py.

Przykład script.py pliku:

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

W moim przypadku chciałem, aby pojawiło się kilka okien podczas ich obliczania. Dla porównania:

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. Dość przydatny przewodnik po interfejsie OO matplotlib .


6

Cóż, miałem wielki problem z rozszyfrowaniem poleceń nieblokujących ... Ale w końcu udało mi się przerobić przykład „ Książka kucharska / Matplotlib / Animacje - Animowanie wybranych elementów fabuły ”, więc działa z wątkami ( i przesyła dane między wątkami albo przez zmienne globalne lub przez proces wieloprocesowyPipe ) w Python 2.6.5 w Ubuntu 10.04.

Skrypt można znaleźć tutaj: Animating_selected_plot_elements-thread.py - inaczej wklejony poniżej ( z mniejszą liczbą komentarzy ) w celach informacyjnych:

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! 

Mam nadzieję, że to komuś pomoże,
zdrowie!


5

W wielu przypadkach wygodniej jest zapisać obraz jako plik .png na dysku twardym. Oto dlaczego:

Zalety:

  • Możesz go otworzyć, obejrzeć i zamknąć w dowolnym momencie procesu. Jest to szczególnie wygodne, gdy aplikacja działa przez długi czas.
  • Nic nie wyskakuje i nie musisz otwierać okien. Jest to szczególnie wygodne, gdy masz do czynienia z wieloma postaciami.
  • Twój obraz jest dostępny do późniejszego wykorzystania i nie zostaje utracony po zamknięciu okna figury.

Wada:

  • Jedyne, co mogę wymyślić, to to, że będziesz musiał znaleźć folder i sam otworzyć obraz.

Jeśli próbujesz wygenerować wiele obrazów, serdecznie się zgadzam.
fantastyczny

6
Png z powrotem nie są interaktywne: \
Odwrotny

5

Jeśli pracujesz w konsoli, tzn. IPythonMożesz użyć, plt.show(block=False)jak wskazano w innych odpowiedziach. Ale jeśli jesteś leniwy, możesz po prostu wpisać:

plt.show(0)

Który będzie taki sam.


5

Musiałem także dodać plt.pause(0.001)do mojego kodu, aby naprawdę działał w pętli for (w przeciwnym razie pokazywałby tylko pierwszy i ostatni wykres):

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)

Działa to dla mnie z matplotlib3 na macOS. Wspaniały!
Jerry Ma

4

W moim systemie show () nie blokuje się, chociaż chciałem, aby skrypt czekał na interakcję użytkownika z wykresem (i zbieranie danych za pomocą wywołań zwrotnych „pick_event”) przed kontynuowaniem.

Aby zablokować wykonywanie do momentu zamknięcia okna wydruku, użyłem następujących opcji:

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

Należy jednak pamiętać, że canvas.start_event_loop_default () wygenerował następujące ostrzeżenie:

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)

chociaż skrypt nadal działał.


Dziękuję Ci! Spyder importuje -pylab przy starcie, co jest na ogół przydatne, ale oznacza, że ​​show () nie blokuje się, gdy ioff () - pozwala to naprawić to zachowanie!
przegrał

3

Chciałem również, aby moje wykresy wyświetlały się, uruchamiając resztę kodu (a następnie wyświetlając dalej), nawet jeśli wystąpi błąd (czasami używam wykresów do debugowania). Zakodowałem ten mały hack, aby wszystkie wątki w tym withoświadczeniu zachowywały się jako takie.

Jest to prawdopodobnie nieco zbyt niestandardowe i niewskazane dla kodu produkcyjnego. W tym kodzie jest prawdopodobnie wiele ukrytych „gotchas”.

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

Jeśli / kiedy zaimplementuję właściwe „utrzymuj otwarte wykresy (nawet jeśli wystąpi błąd) i zezwól na wyświetlanie nowych wykresów”, chciałbym, aby skrypt poprawnie zakończył działanie, jeśli żadna interwencja użytkownika nie powie inaczej (dla celów wykonywania wsadowego).

Mogę użyć czegoś w rodzaju pytania „Koniec skryptu! \ NNaciśnij p, jeśli chcesz, aby wyniki wydruku zostały wstrzymane (masz 5 sekund):” /programming/26704840/corner -cases-for-my-wait-for-user-input-interruption-Implementation .


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
Jak wprowadzić jedno naciśnięcie, zanim powstanie?
grovina

2

OP pyta o detatching matplotlibplots. Większość odpowiedzi zakłada wykonanie polecenia z interpretera python. Przypadek użycia przedstawiony tutaj to moja preferencja do testowania kodu w terminalu (np. Bash), w którym file.pyuruchamiany jest a, a chcesz, aby wykresy pojawiały się, ale skrypt python musi się zakończyć i powrócić do wiersza poleceń.

Ten autonomiczny plik służy multiprocessingdo uruchomienia osobnego procesu drukowania danych matplotlib. Główny wątek kończy się przy użyciu os._exit(1)wspomnianego w tym poście. Te os._exit()siły główne, aby wyjść ale pozostawia matplotlibproces dziecko żyje i reagujących aż okno działka jest zamknięta. To całkowicie osobny proces.

To podejście przypomina trochę sesję programistyczną Matlab z oknami figury, które zawierają responsywny wiersz polecenia. Dzięki takiemu podejściu utraciłeś cały kontakt z procesem okna figury, ale jest to odpowiednie do programowania i debugowania. Po prostu zamknij okno i kontynuuj testowanie.

multiprocessingjest przeznaczony do wykonywania kodu tylko w języku Python, co sprawia, że ​​może lepiej nadaje się niż subprocess. multiprocessingjest wieloplatformowy, więc powinno działać dobrze w systemie Windows lub Mac z niewielką lub żadną regulacją. Nie ma potrzeby sprawdzania systemu operacyjnego. Zostało to przetestowane na systemie 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

Uruchomienie file.pypowoduje wyświetlenie okna figury, a następnie __main__wyjście, ale okno figury multiprocessing+ matplotlibpozostaje responsywne dzięki przyciskom powiększania, przesuwania i innym przyciskom, ponieważ jest to niezależny proces.

Sprawdź procesy w wierszu polecenia bash za pomocą:

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


Próbowałem użyć twojego rozwiązania, ale wydaje mi się, że to nie działa i próbuję dowiedzieć się, dlaczego. Nie uruchamiam kodu przez terminal, ale z Pycharm IDE, jeśli to robi jakąkolwiek różnicę, chociaż nie powinno.
ttsesm

1
ok, co w końcu działało dla mnie, to ustawienie procesu potomnego .daemon=Falsezgodnie z opisem tutaj stackoverflow.com/a/49607287/1476932 Jednak sys.exit()nie zakończyłem procesu nadrzędnego zgodnie z opisem, dopóki nie zamknąłem okna potomnego. Z drugiej strony korzystanie os._exit(0)z powyższego przykładu działało.
ttsesm


0

Jeśli chcesz otworzyć wiele cyfr, zachowując je wszystkie, ten kod działał dla mnie:

show(block=False)
draw()

show (block = False) jest przestarzały i teraz już nie działa
Bogdan

0

Chociaż nie odpowiadam bezpośrednio na prośbę PO, publikuję to obejście, ponieważ może pomóc komuś w takiej sytuacji:

  • Tworzę plik .exe z pyinstaller, ponieważ nie mogę zainstalować Pythona tam, gdzie muszę wygenerować wykresy, więc potrzebuję skryptu Python do wygenerowania wykresu, zapisz go jako .png, zamknij i kontynuuj następny, zaimplementowany jako kilka wykresów w pętla lub użycie funkcji.

do tego używam:

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

Gdzie „var” identyfikuje wykres w pętli, więc nie zostanie on nadpisany.


-1

Użyj plt.show(block=False)i na końcu wywołania skryptu plt.show().

Zapewni to, że okno nie zostanie zamknięte po zakończeniu skryptu.


Zobacz odpowiedź @ nico-schlömer
Josh Wolff
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.