web-dev-qa-db-fra.com

Tracer de manière non bloquante avec Matplotlib

J'ai joué avec Numpy et matplotlib ces derniers jours. J'ai des problèmes pour essayer de faire en sorte que matplotlib trace une fonction sans bloquer l'exécution. Je sais qu'il y a déjà beaucoup de discussions ici sur SO posant des questions similaires, et j'ai beaucoup cherché sur Google mais je n'ai pas réussi à faire en sorte que cela fonctionne.

J'ai essayé d'utiliser show (block = False) comme certaines personnes le suggèrent, mais tout ce que je reçois est une fenêtre figée. Si j'appelle simplement show (), le résultat est tracé correctement mais l'exécution est bloquée jusqu'à la fermeture de la fenêtre. D'après d'autres discussions que j'ai lues, je soupçonne que le fonctionnement de show (block = False) dépend du backend. Est-ce correct? Mon dos est Qt4Agg. Pourriez-vous regarder mon code et me dire si vous voyez quelque chose qui ne va pas? Voici mon code. Merci pour toute aide.

from math import *
from matplotlib import pyplot as plt
print plt.get_backend()



def main():
    x = range(-50, 51, 1)
    for pow in range(1,5):   # plot x^1, x^2, ..., x^4

        y = [Xi**pow for Xi in x]
        print y

        plt.plot(x, y)
        plt.draw()
        #plt.show()             #this plots correctly, but blocks execution.
        plt.show(block=False)   #this creates an empty frozen window.
        _ = raw_input("Press [enter] to continue.")


if __== '__main__':
    main()

PS J'ai oublié de dire que je voudrais mettre à jour la fenêtre existante chaque fois que je dessine quelque chose au lieu d'en créer une nouvelle.

100
opetroch

J'ai passé beaucoup de temps à chercher des solutions et j'ai trouvé cette réponse .

Il semble que pour obtenir ce que vous (et moi) voulons, vous avez besoin de la combinaison de plt.ion(), plt.show() (pas avec blocking=False, c'est déprécié) et, plus important encore, plt.pause(.001) (ou à l'heure que vous voulez). pause est nécessaire car les événements de l'interface graphique se produisent pendant que le code principal est en veille, y compris le dessin. Il est possible que cela soit mis en œuvre en prenant le temps d'un thread en veille, alors peut-être que les IDE s'en mêlent - je ne sais pas.

Voici une implémentation qui fonctionne pour moi sur python 3.5:

import numpy as np
from matplotlib import pyplot as plt

def main():
    plt.axis([-50,50,0,10000])
    plt.ion()
    plt.show()

    x = np.arange(-50, 51)
    for pow in range(1,5):   # plot x^1, x^2, ..., x^4
        y = [Xi**pow for Xi in x]
        plt.plot(x, y)
        plt.draw()
        plt.pause(0.001)
        input("Press [enter] to continue.")

if __== '__main__':
    main()
130
krs013

n truc simple qui fonctionne pour moi est le suivant:

  1. Utilisez l'argument block = False dans show: plt.show (block = False)
  2. Utilisez n autre plt.show ()à la fin du script .py.

Exemple:

import matplotlib.pyplot as plt

plt.imshow(add_something)
plt.xlabel("x")
plt.ylabel("y")

plt.show(block=False)

#more code here (e.g. do calculations and use print to see them on the screen

plt.show()

Note: plt.show() est la dernière ligne de mon script.

17
serafeim

Vous pouvez éviter le blocage de l'exécution en écrivant le tracé dans un tableau, puis en affichant le tableau dans un autre thread. Voici un exemple de génération et d’affichage simultané de tracés à l’aide de pf.screen à partir de pyformulas 0.2.8 :

import pyformulas as pf
import matplotlib.pyplot as plt
import numpy as np
import time

fig = plt.figure()

canvas = np.zeros((480,640))
screen = pf.screen(canvas, 'Sinusoid')

start = time.time()
while True:
    now = time.time() - start

    x = np.linspace(now-2, now, 100)
    y = np.sin(2*np.pi*x) + np.sin(3*np.pi*x)
    plt.xlim(now-2,now+1)
    plt.ylim(-3,3)
    plt.plot(x, y, c='black')

    # If we haven't already shown or saved the plot, then we need to draw the figure first...
    fig.canvas.draw()

    image = np.fromstring(fig.canvas.tostring_rgb(), dtype=np.uint8, sep='')
    image = image.reshape(fig.canvas.get_width_height()[::-1] + (3,))

    screen.update(image)

#screen.close()

Résultat:

Sine animation

Disclaimer: Je suis le responsable des pyformulas.

Référence: Matplotlib: sauvegarder le tracé dans un tableau numpy

12
Default picture

Beaucoup de ces réponses sont super gonflées et d'après ce que je peux trouver, la réponse n'est pas si difficile à comprendre.

Vous pouvez utiliser plt.ion() si vous le souhaitez, mais j'ai trouvé que plt.draw() était tout aussi efficace.

Pour mon projet spécifique, je trace des images, mais vous pouvez utiliser plot() ou scatter() ou peu importe au lieu de figimage(), cela n'a pas d'importance.

plt.figimage(image_to_show)
plt.draw()
plt.pause(0.001)

Ou

fig = plt.figure()
...
fig.figimage(image_to_show)
fig.canvas.draw()
plt.pause(0.001)

Si vous utilisez un chiffre réel.
J'ai utilisé @ krs013 et les réponses de @Default Picture pour comprendre cela.
J'espère que cela évitera à quelqu'un de lancer chaque personnage sur un fil séparé ou d'avoir à lire ces romans juste pour comprendre cela

3
iggy12345

Live Tracé

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0, 2 * np.pi, 100)
# plt.axis([x[0], x[-1], -1, 1])      # disable autoscaling
for point in x:
    plt.plot(point, np.sin(2 * point), '.', color='b')
    plt.draw()
    plt.pause(0.01)
# plt.clf()                           # clear the current figure

si la quantité de données est trop importante, vous pouvez réduire le taux de mise à jour avec un simple compteur

cnt += 1
if (cnt == 10):       # update plot each 10 points
    plt.draw()
    plt.pause(0.01)
    cnt = 0

Parcelle d'attente après la sortie du programme

C'était mon problème actuel pour lequel je ne pouvais pas trouver de réponse satisfaisante, je voulais un tracé qui ne se ferme pas une fois le script terminé (comme MATLAB),

Si vous y réfléchissez, une fois le script terminé, le programme est terminé et il n’existe aucun moyen logique de conserver le tracé de cette façon. Il existe donc deux options.

  1. empêche le script de sortir (c'est plt.show () et pas ce que je veux)
  2. exécuter l'intrigue sur un thread séparé (trop compliqué)

ce n'était pas satisfaisant pour moi alors j'ai trouvé une autre solution en dehors de la boîte

SaveToFile and View dans le visualiseur externe

Pour cela, l'enregistrement et la visualisation doivent être rapides et le visualiseur ne doit pas verrouiller le fichier et doit mettre à jour le contenu automatiquement

Sélection du format d'enregistrement

les formats vectoriels sont à la fois petits et rapides

  • SVG est bon mais ne peut pas trouver un bon visualiseur sauf le navigateur Web qui, par défaut, nécessite une actualisation manuelle.
  • PDF peut prendre en charge les formats vectoriels et certains visualiseurs légers prennent en charge la mise à jour en direct.

Fast Lightweight Viewer avec mise à jour en direct

Pour PDF il existe plusieurs bonnes options

  • Sous Windows, j'utilise SumatraPDF , qui est gratuit, rapide et léger (n'utilise que 1,8 Mo RAM pour mon cas)

  • Sous Linux, il existe plusieurs options telles que Evince (GNOME) et Ocular (KDE)

Exemple de code et résultats

Exemple de code pour la sortie d'un tracé dans un fichier

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0, 2 * np.pi, 100)
y = np.sin(2 * x)
plt.plot(x, y)
plt.savefig("fig.pdf")

après la première exécution, ouvrez le fichier de sortie dans l’un des lecteurs mentionnés ci-dessus et profitez-en.

Voici une capture d'écran de VSCode aux côtés de SumatraPDF, le processus est également assez rapide pour obtenir une vitesse de mise à jour semi-directe (je peux obtenir près de 10Hz sur ma configuration, utilisez simplement time.sleep() entre les intervalles) pyPlot,Non-Blocking

1
Ali80

La réponse d'Iggy était la plus facile à suivre, mais j'ai l'erreur suivante lors de l'exécution d'une commande ultérieure subplot qui n'était pas présente alors que je ne faisais que show:

MatplotlibDeprecationWarning: l'ajout d'un axe utilisant les mêmes arguments qu'un axe précédent réutilise actuellement l'instance précédente. Dans une version ultérieure, une nouvelle instance sera toujours créée et renvoyée. En attendant, cet avertissement peut être supprimé et le comportement futur assuré en transmettant une étiquette unique à chaque instance d'axes.

Afin d'éviter cette erreur, il est utile de fermer (ou effacer ) le tracé après que l'utilisateur ait cliqué sur Entrée.

Voici le code qui a fonctionné pour moi:

def plt_show():
    '''Text-blocking version of plt.show()
    Use this instead of plt.show()'''
    plt.draw()
    plt.pause(0.001)
    input("Press enter to continue...")
    plt.close()
1
Pro Q

Le package Python drawnow permet de mettre à jour un tracé en temps réel de manière non bloquante.
Il fonctionne également avec une webcam et OpenCV, par exemple, pour tracer des mesures pour chaque image.
Voir le message d'origine .

0
Ismael EL ATIFI