web-dev-qa-db-fra.com

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

Après ces instructions dans l'interprète Python, on obtient une fenêtre avec un tracé:

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.

237
meteore

Utilisez les appels de matplotlib qui ne bloquent pas:

Utiliser 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()
199
nosklo

Utilisez le mot clé "bloquer" 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.

119
Jan

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’ya pas d’autre moyen, vous pouvez exécuter tout ce qui bloque un processus séparé en utilisant le module multprocessing inclus dans python. Le calcul continuera:

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, alors je préférerais l’autre solution (en utilisant matplotlib's appels d’API non bloquants )

29
nosklo

Essayer

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.

21
Nico Schlömer

Vous voudrez peut-être lire ce document dans la documentation de matplotlib, intitulée:

tilisation de matplotlib dans un shell python

10
nosklo

IMPORTANT: Juste pour clarifier quelque chose. Je suppose que les commandes se trouvent dans un script _.py_ et que le script est appelé à l'aide de, par exemple. _python script.py_ à partir de la console.

Un moyen simple qui fonctionne pour moi est:

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

Exemple de _script.py_ fichier:

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

9
serafeim

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

8
meteore

Eh bien, j'ai eu beaucoup de mal à comprendre les commandes non bloquantes ... Mais finalement, j'ai réussi à retravailler le " Cookbook/Matplotlib/Animations - Animation des éléments de tracé sélectionnés ", de sorte qu'il fonctionne avec des threads ( et transmet les données entre les threads via des variables globales ou via un multiprocessus Pipe) 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 ça aide quelqu'un,
À votre santé!

6
sdaau

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)

Ce qui sera le même.

5
Anton Protopopov

Dans de nombreux cas, il s’agit plus pratique jusqu’à enregistrer l’image sous forme de 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 pendant une longue période.
  • Rien ne se présente et vous n'êtes pas obligé d'avoir les fenêtres ouvertes. Ceci est particulièrement pratique lorsque vous utilisez de nombreux chiffres.
  • Votre image est accessible pour 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 devrez aller chercher le dossier dans Finder et ouvrir l'image vous-même.
5
elgehelge

Je devais aussi ajouter plt.pause(0.001) à mon code pour qu'il fonctionne vraiment dans une boucle for (sinon, il ne montrerait que le premier et le dernier tracé):

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)
4
Martin Pecka

Je voulais aussi que mes parcelles affichent le reste du code (puis continuent à l'afficher) même s'il y avait une erreur (j'utilise parfois des parcelles pour le débogage). J'ai codé ce petit bidouillage de sorte que toute parcelle à l'intérieur de cette déclaration with se comporte comme telle.

C'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 __== '__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 correctement "garder les tracés ouverts (même en cas d'erreur) et autoriser l'affichage de nouveaux tracés", je voudrais que le script se termine correctement si aucune intervention de l'utilisateur ne le dit autrement (à des fins d'exécution par lots).

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 du tracé soit suspendue (vous avez 5 secondes):" de https://stackoverflow.com/questions/26704840/corner-case-for-my-wait-for-user-input-interruption-implementation .

3
Simon Streicher

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

Afin de bloquer l'exécution jusqu'à la fermeture de la fenêtre de tracé, j'ai utilisé ce qui suit:

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 ait encore fonctionné.

2
Andrew
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
2
thanhtang

À mon avis, les réponses dans ce fil fournissent des méthodes qui ne fonctionnent pas pour tous les systèmes et dans des situations plus complexes comme les animations. Je suggère de jeter un coup d'oeil à 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?

1
MikeTeX

L'OP demande si vous souhaitez détacher _ des parcelles matplotlib. La plupart des réponses supposent l'exécution de la commande 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 file.py est exécuté et vous souhaitez que le ou les tracés apparaissent, mais que le script python compléter et revenir à une invite de commande.

Ce fichier autonome utilise multiprocessing pour lancer un processus séparé permettant de tracer des données avec matplotlib. Le thread principal se termine en utilisant la os._exit(1) mentionnée dans this post. La os._exit() force le système principal à 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 complètement séparé.

Cette approche ressemble un peu à une session de développement Matlab avec des fenêtres de figures qui fournissent une invite de commande sensible. 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 juste la fenêtre et continuez à tester.

multiprocessing est conçu pour une exécution de code python uniquement, ce qui le rend peut-être mieux adapté que subprocess. multiprocessing est multi-plateforme, donc cela devrait bien fonctionner sous Windows ou Mac avec peu ou pas d'ajustement. Il n'est pas nécessaire de vérifier le système d'exploitation sous-jacent. Cela 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 __== "__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

Exécuter file.py ouvre une fenêtre de figure, puis __main__ se ferme, mais la fenêtre multiprocessing + matplotlib figure reste sensible aux boutons de zoom, de panoramique et autres, car il s'agit d'un processus indépendant. .

Vérifiez les processus à la commande bash Invite avec:

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

0
Marc Compere

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

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

0
Ken Mueller

Voici une mise à jour (Python 3.6.5 sur Windows 10).

J'ai essayé toutes sortes de combinaisons - la plus simple que j'ai trouvée consiste simplement à utiliser pause(0.01) après chaque tracé - pas besoin d'un show() pour les graphes intermédiaires - puis d'un seul show() à la end garantit que vous pouvez consulter le tracé final avant la fin.

A titre d'exemple, voici un peu de code que j'utilise pour vérifier la vitesse pour différentes tailles de tableau - les valeurs les plus hautes tracées sont les vitesses les plus élevées ... il y a 10 tracés superposés ...

from pylab import *
import matplotlib.pyplot as plt
from time import *
ttot=clock();
mmax=6;npts=20;nplts=10;
x=[int(a+0.5) for a in 10**linspace(0,mmax,npts)]
for nrun in range(nplts):
    j=0;aa=1;bb=1;b=1;
    tim=zeros(npts)
    for n in x:
        aa=Rand(n);bb=aa;b=aa;
        if n<100:m=10000
        Elif n<5000:m=1000
        Elif n<20000:m=100
        else:m=100
        tt=clock()
        for ii in range(1,m+1):
          b=aa*bb+aa
        tt1=clock()-tt
        tim[j]=tt1/n/m
        j=j+1
    print(n,2/(tt1/n/m)/1e6);
    plt.semilogx(x,2/tim/1e6)
    pause(0.01)
print(clock()-ttot)
show()
0
marzetti

Si vous voulez ouvrir plusieurs figures tout en les gardant toutes, ce code a fonctionné pour moi:

show(block=False)
draw()
0
DomDev