我想绘制一些从简单的投票应用程序生成的数据的图表。我过去曾对 pyplot 进行过修改,但我没有尝试从头开始创建任何东西。幸运的是,它非常流行,并且在 StackOverflow 和其他地方可以找到大量示例。
我进行了搜索,并从这个与随时间更新图表相关的答案开始。
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()
此代码动画正弦波变化阶段。
前两行导入我想要使用的库:matplotlib.pyplot 进行绘图和处理 GUI。
ion() 方法,如果我理解的话(尽管我可能不理解),使 pyplot 驱动 GUI。您还可以在 tkinter 程序中使用它,或者使用它来生成静态图像,但在我们的例子中,让它为我们处理图形的 GUI 是有意义的。 (这就是稍后调用的flush_events()正在做的事情:允许与图形窗口进行交互。)
此示例使用 numpy 方法 linspace() 来创建 x 值。它返回一个 numpy 数组,这是一个奇特的 Python 列表。
使用 np.sin 而不是 math.sin 的原因是广播。这是将函数应用于列表中每个项目的 numpy 术语。事实上,我突然想到,不用 numpy 使用 map 也可以实现同样的事情:
map(lambda n: math.sin(n), x)
但是numpy广播使用起来方便又简单。
现在是 pyplot 设置。首先,创建一个新的“图形”(图)。在此图中,添加一个子图(ax)——可能有很多。 111 有相当深奥的解释,“创建一个 1x1 网格,并将这个子图放在第一个单元格中。”
在这个子图(或一组轴)中,使用传递的 x 和 y 值绘制一条线。 (点用直线连接并连续绘制。)“r-”是指定红色实线的简写方式。我们可以指定多行,因此plot()返回一个元组;上面的代码使用元组解包来提取我们想要的一个值。
这是一个好的开始,但我需要随着时间的推移延长 x 轴。如果需要,此代码也不会更新 y 轴的边界 - 它被锁定到为第一个图计算的任何边界。更多的搜索让我找到了这个答案。引用他们的话:
您需要更新坐标区的 dataLim,然后根据 dataLim 更新坐标区的 viewLim。适当的方法是axes.relim()和ax.autoscale_view()方法。
当然,听起来不错。根据他们的示例,我创建了一个在 x 和 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)
现在我已经取得进展了。但是,这是一个阻塞循环,我需要偶尔更新我的数据。如果我有多个线程,我就必须担心更新变量时的线程安全问题。在这种情况下,我可以偷懒,因为我知道该变量仅每 5 分钟更新一次(或者无论轮询函数运行多久);变量在代码行中间不存在被覆盖的危险。
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)
只有当增长线程为 new_x 赋值时,图表才会更新。请注意,flush_events() 调用位于“if”语句之外,因此它被频繁调用。
以上是使用 pyplot 进行实时绘图的详细内容。更多信息请关注PHP中文网其他相关文章!