Existe uma maneira de separar parcelas matplotlib para que a computação pode continuar?

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

  •  19-08-2019
  •  | 
  •  

Pergunta

Depois destas instruções no Python intérprete obtém-se uma janela com um enredo:

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

Infelizmente, eu não sei como continuar a explorar de forma interativa a figura criada por show() enquanto o programa faz outros cálculos.

É possível em tudo? Às vezes, os cálculos são longos e seria bom se eles iriam continuar durante o exame dos resultados intermediários.

Foi útil?

Solução

chamadas de Uso matplotlib que não vai bloquear:

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

Usando o modo interativo:

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

Outras dicas

Use a palavra-chave 'block' para substituir o comportamento de bloqueio, por exemplo,

from matplotlib.pyplot import show, plot

plot(1)  
show(block=False)

# your code

para continuar seu código.

É melhor verificar sempre com a biblioteca que você está usando se ele suporta o uso de non-blocking forma.

Mas se você quiser uma solução mais genérica, ou se não houver nenhuma outra maneira, você pode executar qualquer coisa que bloqueia em um processo separado usando o multprocessing módulo incluído no python. Computação continuará:

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

Isso tem a sobrecarga de lançamento de um novo processo, e às vezes é mais difícil de depuração em cenários complexos, então eu prefiro a outra solução (usando de matplotlib bloqueante chamadas de API )

Tente

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.

O show() documentação diz:

No modo não interactivo, exibir todas as figuras e bloquear até que os valores foram fechadas; no modo interativo não tem efeito a menos que figuras foram criadas antes de uma mudança de não-interativo para modo interativo (não recomendado). Nesse caso, ele exibe os números, mas não bloqueia.

Uma única palavra-chave argumento experimental, block, pode ser definido para True ou False para substituir o comportamento de bloqueio descrito acima.

Você pode querer ler este documento na documentação do matplotlib, intitulado:

Usando matplotlib em um shell python

IMPORTANTE : Só para deixar algo bem claro. Presumo que os comandos estão dentro de um script .py eo script é chamado usando por exemplo python script.py do console.

Uma simples maneira que funciona para mim é:

  1. Use o bloco = False dentro show: plt.show (bloco = False)
  2. Use outra show () no final do script py.

Exemplo de arquivo script.py:

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

No meu caso, eu queria ter várias janelas pop-up como eles estão sendo calculado. Para referência, este é o caminho:

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. Um bastante útil guia para OO do matplotlib interface de .

Bem, eu tinha grande dificuldade para descobrir os comandos sem bloqueio ... Mas, finalmente, consegui refazer o " Cookbook / Matplotlib / animações - animando selecionado elementos do enredo " exemplo, assim que trabalha com fios ( e passa dados entre threads, quer através de variáveis ??globais, ou através de um Pipe multiprocess ) em Python 2.6.5 no Ubuntu 10.04.

O script pode ser encontrada aqui: Animating_selected_plot_elements-thread.py - caso contrário colado abaixo ( com menos comentários ) para referência:

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! 

Espero que isso ajude alguém,
Felicidades!

Em muitos casos, é mais conveniente até salvar a imagem como um arquivo .png no disco rígido. Aqui está o porquê:

Vantagens:

  • Você pode abri-lo, ter um olhar para ele e fechá-lo a qualquer momento durante o processo. Isto é particularmente conveniente quando o seu aplicativo está sendo executado por um longo tempo.
  • Nada aparece e você não é obrigado a ter as janelas abertas. Isto é particularmente útil quando você está lidando com muitas figuras.
  • A sua imagem está acessível para consulta posterior e não são perdidos quando fechar a janela da figura.

Drawback:

  • A única coisa que posso pensar é que você terá que ir e localizador da pasta e abra a imagem si mesmo.

Se você estiver trabalhando no console, ou seja IPython você poderia usar plt.show(block=False) como fora apontado em outras respostas. Mas se você é preguiçoso você poderia simplesmente digitar:

plt.show(0)

Qual será o mesmo.

Eu tive que adicionar também plt.pause(0.001) ao meu código para torná-la realmente trabalhando dentro de um loop for (caso contrário, seria apenas mostrar a primeira e última parcela):

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)

Eu também queria que meus planos para exibir executar o resto do código (e, em seguida, continuar a visualizar), mesmo se houver um erro (eu às vezes uso parcelas para depuração). I codificado-se este pequeno truque para que quaisquer parcelas dentro deste comportam declaração with como tal.

Este é provavelmente um pouco fora do padrão e não aconselhável para código de produção. Há provavelmente um monte de escondidas "pegadinhas" neste código.

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 eu implementar uma adequada "manter as parcelas aberta (mesmo se ocorrer um erro) e permitir que novas parcelas para ser mostrado", eu gostaria que o script corretamente Sair se nenhuma interferência do usuário diz o contrário (para execução em lote fins).

Eu posso usar algo como um time-out-pergunta "End of script \ nPrima p se você deseja que a saída conspirar para ser pausado (você tem 5 segundos):" a partir de https://stackoverflow.com/questions/26704840/corner-cases-for-my-wait- for-user-input-interrupção-implementação .

No meu show system () não bloqueia, embora eu queria o script para aguardar o usuário interagir com o gráfico (e dados de coleta usando 'pick_event' callbacks) antes de continuar.

Para a execução do bloco até que a janela do enredo está fechada, eu usei o seguinte:

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

Note, no entanto, que canvas.start_event_loop_default () produziu o seguinte aviso:

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)

Embora o roteiro ainda corria.

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

Na minha opinião, as respostas neste segmento fornecer métodos que não funcionam para todos os sistemas e em situações mais complexas como animações. Sugiro ter um olhar para a resposta de MiKTeX na seguinte discussão, onde um método robusto foi encontrado: Como esperar até fins de animação matplotlib?

Se você quiser abrir várias figuras, mantendo-os abertos, este código funcionou para mim:

show(block=False)
draw()

O OP pergunta sobre detatching parcelas matplotlib. A maioria das respostas assumir a execução de comandos a partir de um interpretador Python. O caso de uso apresentado aqui é a minha preferência para testar o código em um terminal (por exemplo, bash) onde um file.py é executado e você quer (s) o enredo para chegar, mas o script Python para preencher e devolver a um prompt de comando.

Esta usos de arquivo multiprocessing stand-alone para lançar um processo separado para plotagem de dados com matplotlib. As principais saídas de rosca usando o os._exit(1) mencionado na este pós . As forças os._exit() principal para sair, mas deixa o processo filho matplotlib vivos e sensíveis até que a janela do enredo está fechado. É um processo separado completamente.

Esta abordagem é um pouco como uma sessão de desenvolvimento Matlab com figura janelas que surgem com um prompt de comando responsivo. Com esta abordagem, você perdeu todo o contato com o processo janela figura, mas, que é ok para o desenvolvimento e depuração. Basta fechar a janela e manter o teste.

multiprocessing é projetado para a execução de código só de python que torna talvez mais adequado do que subprocess. multiprocessing é multi-plataforma de modo que este deve funcionar bem no Windows ou Mac com pouco ou nenhum ajuste. Não há necessidade de verificar o sistema operacional subjacente. Este foi testado em 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

Running file.py abre uma janela figura, sai então __main__ mas a janela do multiprocessing + figura matplotlib continua a responder com zoom, pan, e outros botões, porque é um processo independente.

Verifique os processos no comando bash tempo com:

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

Use plt.show(block=False), e no final do seu plt.show() chamada de script.

Isto irá assegurar que a janela não será fechada quando o script for concluído.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top