Je voulais représenter graphiquement certaines données que je générais à partir d'une simple application de sondage. J'ai bricolé pyplot dans le passé, mais je n'ai pas essayé de créer quoi que ce soit à partir de zéro. Heureusement, c'est très populaire et il existe des tonnes d'exemples sur StackOverflow et ailleurs.
J'ai fait une recherche et j'ai commencé avec cette réponse SO liée à la mise à jour d'un graphique au fil du temps.
import matplotlib.pyplot as plt import numpy as np # You probably won't need this if you're embedding things in a tkinter plot... plt.ion() x = np.linspace(0, 6*np.pi, 100) y = np.sin(x) fig = plt.figure() ax = fig.add_subplot(111) line1, = ax.plot(x, y, 'r-') # Returns a tuple of line objects, thus the comma for phase in np.linspace(0, 10*np.pi, 500): line1.set_ydata(np.sin(x + phase)) fig.canvas.draw() fig.canvas.flush_events()
Ce code anime une phase de changement d'onde sinusoïdale.
Les deux premières lignes importent les bibliothèques que je souhaite utiliser : matplotlib.pyplot s'occupe du traçage et de la gestion de l'interface graphique.
La méthode ion(), si je comprends bien (même si je ne peux pas le faire), permet à pyplot de piloter l'interface graphique. Vous pouvez également l'utiliser dans un programme tkinter, ou l'utiliser pour générer des images statiques, mais dans notre cas, il est logique de le laisser gérer l'interface graphique de l'intrigue pour nous. (C'est ce que fait l'appel flush_events() plus tard : permettre l'interactivité avec la fenêtre de la figure.)
Cet exemple utilise la méthode numpy linspace() pour créer les valeurs x. Il renvoie un tableau numpy, qui est une liste Python sophistiquée.
La raison d'utiliser np.sin au lieu de math.sin est la diffusion. C'est le terme numpy pour appliquer la fonction à chaque élément d'une liste. En fait, il me vient à l'esprit que la même chose pourrait être réalisée sans numpy en utilisant map :
map(lambda n: math.sin(n), x)
Mais la diffusion numpy est pratique et simple à utiliser.
Vient maintenant la configuration de pyplot. Tout d'abord, créez une nouvelle "figure" (fig). Dans cette figure, ajoutez une sous-intrigue (hache) – il pourrait y en avoir plusieurs. 111 a une interprétation plutôt ésotérique : "créez une grille 1x1 et placez cette sous-intrigue dans la première cellule."
Dans ce sous-tracé (ou ensemble d'axes), une ligne est tracée en utilisant les valeurs x et y transmises. (Les points sont reliés par des lignes droites et tracés en continu.) « r- » est la manière abrégée de spécifier une ligne rouge continue. Nous pourrions spécifier plusieurs lignes, donc plot() renvoie un tuple ; le code ci-dessus utilise le déballage de tuple pour extraire la valeur souhaitée.
C'est un bon début, mais je dois étendre l'axe des x au fil du temps. Ce code ne met pas non plus à jour les limites de l'axe y si nécessaire : il est verrouillé dans les limites qu'il calcule pour le premier tracé. Un peu plus de recherche m'amène à cette réponse SO. Pour les citer :
Vous devrez mettre à jour le dataLim des axes, puis mettre à jour ensuite le viewLim des axes en fonction du dataLim. Les méthodes appropriées sont les méthodes axes.relim() et ax.autoscale_view().
Bien sûr, ça a l'air bien. Sur la base de leur exemple, j'ai créé un graphique de démonstration qui grandit à la fois en x et en y.
import matplotlib.pyplot as plt import numpy as np from threading import Thread from time import sleep x = list(map(lambda x: x / 10, range(-100, 100))) x_next_max = 100 y = np.sin(x) # You probably won't need this if you're embedding things in a tkinter plot... plt.ion() fig = plt.figure() ax = fig.add_subplot(111) line1 = ax.plot(x, y, 'r-')[0] # Returns a tuple of line objects growth = 0 while True: x.append(x_next_max / 10) x_next_max += 1 line1.set_xdata(x) line1.set_ydata(np.sin(x) + np.sin(np.divide(x, 100)) + np.divide(x, 100)) ax.relim() ax.autoscale() fig.canvas.draw() fig.canvas.flush_events() sleep(0.1)
Maintenant, j'arrive à quelque chose. Mais il s’agit d’une boucle de blocage et j’ai besoin que mes données soient occasionnellement mises à jour. Si j'avais plusieurs threads, je devrais m'inquiéter d'être sûr des threads lors de la mise à jour de mes variables. Dans ce cas, je peux être paresseux car je sais que la variable n'est mise à jour qu'une fois toutes les 5 minutes (ou quelle que soit la fréquence d'exécution de la fonction d'interrogation) ; il n'y a aucun risque que la variable soit écrasée au milieu d'une ligne de code.
import matplotlib.pyplot as plt import numpy as np from threading import Timer from time import sleep x = list(map(lambda x: x / 10, range(-100, 100))) x_next_max = 100 y = np.sin(x) # You probably won't need this if you're embedding things in a tkinter plot... plt.ion() fig = plt.figure() ax = fig.add_subplot(111) line1 = ax.plot(x, y, 'r-')[0] # Plot returns a tuple of line objects growth = 0 new_x = None dT = 1 def grow(): global new_x, x_next_max while True: new_x = x + [x_next_max / 10] x_next_max += 1 sleep(dT) # grow every dT seconds t = Thread(target=grow) t.start() while True: if new_x: x = new_x new_x = None line1.set_xdata(x) line1.set_ydata(np.sin(x) + np.sin(np.divide(x, 100)) + np.divide(x, 100)) ax.relim() ax.autoscale() fig.canvas.draw() fig.canvas.flush_events() sleep(0.1)
Le graphique n'est mis à jour que lorsque le thread de croissance attribue une valeur à new_x. Notez que l'appel flush_events() se trouve en dehors de l'instruction "if", de sorte qu'il est appelé fréquemment.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!