Existe-t-il un moyen de détacher des parcelles matplotlib pour que le calcul puisse continuer?

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

  •  19-08-2019
  •  | 
  •  

Question

Après ces instructions dans l'interpréteur Python, une fenêtre avec un tracé apparaît:

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

Malheureusement, je ne sais pas comment continuer à explorer de manière interactive la figure créée par show () pendant que le programme effectue des calculs supplémentaires.

Est-ce possible? Parfois, les calculs sont longs et il serait utile qu’ils procèdent lors de l’examen des résultats intermédiaires.

Était-ce utile?

La solution

Utilisez les appels de matplotlib qui ne bloquent pas:

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

Utilisation du mode interactif:

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

Autres conseils

Utilisez le mot clé "block" pour remplacer le comportement de blocage, par exemple.

from matplotlib.pyplot import show, plot

plot(1)  
show(block=False)

# your code

pour continuer votre code.

Il est préférable de toujours vérifier auprès de la bibliothèque que vous utilisez si elle prend en charge l'utilisation de manière non bloquante .

Mais si vous voulez une solution plus générique ou s'il n'y a pas d'autre moyen, vous pouvez exécuter tout ce qui bloque dans un processus séparé en utilisant module multitraitement inclus dans python. Le calcul se poursuivra:

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

Cela a la surcharge de lancer un nouveau processus et est parfois plus difficile à déboguer sur des scénarios complexes. Je préférerais donc une autre solution (utilisant matplotlib de ne pas bloquer les appels d'API )

Essayez

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 () documentation dit:

  

En mode non interactif, affichez toutes les figures et bloquez-les jusqu'à la fermeture des figures; en mode interactif, il n'a d'effet que si les figures ont été créées avant le passage du mode non interactif au mode interactif (non recommandé). Dans ce cas, il affiche les chiffres mais ne bloque pas.

     

Un seul argument de mot clé expérimental, block , peut être défini sur True ou False pour remplacer le comportement de blocage décrit ci-dessus.

Vous pouvez lire ce document dans la documentation de matplotlib , intitulé:

utiliser matplotlib dans un shell en python

IMPORTANT : il suffit de préciser quelque chose. Je suppose que les commandes sont à l'intérieur d'un script .py et que le script est appelé à l'aide de, par exemple. script python.py à partir de la console.

Voici un moyen simple qui fonctionne pour moi:

  1. Utilisez le bloc = False à l'intérieur du spectacle: plt.show (block = False)
  2. Utilisez un autre show () à la fin du script .py.

Exemple de fichier 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()

Dans mon cas, je voulais faire apparaître plusieurs fenêtres au fur et à mesure de leur calcul. Pour référence, voici le chemin:

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. guide de l'interface OO de matplotlib .

Eh bien, j’ai eu beaucoup de difficulté à comprendre les commandes non bloquantes ... Mais finalement, j’ai réussi à retravailler le & a; Livre de recettes / Matplotlib / Animations - Animation des éléments de tracé sélectionnés " Par exemple, cela fonctionne avec les threads ( et transmet les données entre les threads via des variables globales ou via un Pipe multiprocessus) sur Python 2.6.5 sur Ubuntu 10.04.

Le script peut être trouvé ici: Animating_selected_plot_elements-thread.py - autrement collé ci-dessous ( avec moins de commentaires ) pour référence:

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! 

J'espère que cela aide quelqu'un,
À la vôtre!

Dans de nombreux cas, il est plus pratique de sauvegarder l'image en tant que fichier .png sur le disque dur. Voici pourquoi:

Avantages:

  • Vous pouvez l'ouvrir, l'examiner et le fermer à tout moment du processus. Ceci est particulièrement pratique lorsque votre application est en cours d'exécution depuis longtemps. le temps.
  • Rien ne s'affiche et vous n'êtes pas obligé d'ouvrir les fenêtres. Ceci est particulièrement pratique lorsque vous utilisez de nombreux chiffres.
  • Votre image est accessible pour une référence ultérieure et n'est pas perdue lors de la fermeture de la fenêtre de l'image.

Inconvénient:

  • La seule chose à laquelle je peux penser, c'est que vous devez aller chercher le dossier et ouvrir l'image vous-même.

Si vous travaillez dans la console, c'est-à-dire IPython , vous pouvez utiliser plt.show (block = False) comme indiqué dans les autres réponses. Mais si vous êtes paresseux, vous pouvez simplement taper:

plt.show(0)

Qui sera le même.

Je devais également ajouter plt.pause (0.001) à mon code pour qu'il fonctionne réellement dans une boucle for (sinon, le premier et le dernier tracé seraient affichés):

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)

Je voulais aussi que mes parcelles s'affichent, exécutent le reste du code (puis continuent d'afficher) même s'il y a une erreur (j'utilise parfois des parcelles pour le débogage). J'ai codé ce petit bidouillage pour que tous les complots de cette instruction avec se comportent comme tels.

Ceci est probablement un peu trop non standard et déconseillé pour le code de production. Il y a probablement beaucoup de "pièges" cachés. dans ce code.

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

Si / quand j'implémente un "garder les tracés ouverts (même si une erreur survient) et permettre l'affichage de nouveaux tracés", je voudrais que le script se ferme correctement si aucune intervention de l'utilisateur ne l'indique (par lot). d'exécution).

Je peux utiliser quelque chose comme une question de délai d'attente "Fin du script!" \ nAppuyez sur p si vous souhaitez que la sortie de traçage soit suspendue (vous avez 5 secondes): " sur https://stackoverflow.com/questions/26704840 / corner-case-for-my-wait-for-user-interruption-implémentation .

Sur mon système, show () ne bloque pas, bien que je souhaite que le script attende que l'utilisateur interagisse avec le graphique (et collecte les données à l'aide de rappels 'pick_event') avant de continuer.

Pour bloquer l'exécution jusqu'à la fermeture de la fenêtre de traçage, j'ai utilisé les éléments suivants:

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

Notez cependant que canvas.start_event_loop_default () a généré l'avertissement suivant:

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)

bien que le script soit toujours exécuté.

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

À mon avis, les réponses de ce fil fournissent des méthodes qui ne fonctionnent pas pour tous les systèmes et dans des situations plus complexes telles que des animations. Je suggère de regarder la réponse de MiKTeX dans le fil suivant, où une méthode robuste a été trouvée: Comment attendre la fin de l'animation matplotlib?

Si vous souhaitez ouvrir plusieurs figures tout en les maintenant, ce code a fonctionné pour moi:

show(block=False)
draw()

L'OP demande si vous souhaitez détacher des tracés matplotlib . La plupart des réponses supposent l'exécution de commandes depuis un interpréteur Python. Le cas d'utilisation présenté ici est ma préférence pour le test de code dans un terminal (par exemple, bash) où un fichier.py est exécuté et que vous souhaitez que le ou les tracés apparaissent, mais que le script python soit terminé. et retournez à une invite de commande.

Ce fichier autonome utilise le multitraitement pour lancer un processus distinct pour le traçage des données avec matplotlib . Le thread principal se termine en utilisant le os._exit (1) mentionné dans cette publication. Le os._exit () force main à quitter, mais laisse le processus enfant matplotlib actif et réactif jusqu'à la fermeture de la fenêtre de traçage. C'est un processus à part entière.

Cette approche ressemble un peu à une session de développement Matlab avec des fenêtres d’illustration fournissant une invite de commande réactive. Avec cette approche, vous avez perdu tout contact avec le processus de la fenêtre de figure, mais c'est correct pour le développement et le débogage. Fermez simplement la fenêtre et continuez à tester.

Le

multitraitement est conçu pour l'exécution de code uniquement en python, ce qui le rend peut-être plus adapté que le sous-processus . Le multitraitement étant multi-plateforme, cela devrait bien fonctionner sous Windows ou Mac avec peu ou pas de réglage. Il n'est pas nécessaire de vérifier le système d'exploitation sous-jacent. Ceci a été testé sur 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'exécution de fichier.py ouvre une fenêtre de figure, puis __ main __ se ferme mais la figure multitraitement + matplotlib La fenêtre reste sensible aux boutons de zoom, de panoramique et autres car il s’agit d’un processus indépendant.

Vérifiez les processus à l'invite de commande bash avec:

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

Utilisez plt.show (block = False) et à la fin de votre appel de script plt.show () .

Ceci garantira que la fenêtre ne sera pas fermée une fois le script terminé.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top