使用 Matplotlib 以非阻塞方式绘图
Plotting in a non-blocking way with Matplotlib
最近几天一直在玩Numpy和matplotlib。我在尝试使 matplotlib 绘制函数而不阻止执行时遇到问题。我知道这里已经有很多关于 SO 的帖子在问类似的问题,而且我已经用谷歌搜索了很多但还没有成功。
我试过像某些人建议的那样使用 show(block=False),但我得到的只是一个冻结 window。如果我只是调用 show(),结果会正确绘制,但执行会被阻止,直到 window 关闭。从我读过的其他线程来看,我怀疑 show(block=False) 是否有效取决于后端。这个对吗?我的后端是 Qt4Agg。你能看看我的代码并告诉我你是否发现错误吗?这是我的代码。感谢您的帮助。
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 __name__ == '__main__':
main()
PS。我忘了说我想在每次绘制内容时更新现有的 window,而不是创建一个新的。
我花了很长时间寻找解决方案,找到了this answer。
看起来,为了得到你(和我)想要的东西,你需要 plt.ion()
、plt.show()
(而不是 block=False
)的组合,最重要的是, plt.pause(.001)
(或任何你想要的时间)。 pause 是必需的,因为 GUI 事件在主代码休眠时发生,包括绘图。这可能是通过从休眠线程中获取时间来实现的,所以也许 IDE 会搞砸它——我不知道。
这是一个适用于 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 __name__ == '__main__':
main()
一个对我有用的简单技巧如下:
- 在节目中使用 block = False 参数:plt.show(block = False)
- 在 .py 脚本的末尾使用另一个plt.show()。
示例:
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()
注意:plt.show()
是我脚本的最后一行。
您可以通过将绘图写入数组,然后在不同的线程中显示该数组来避免阻塞执行。这是使用 pyformulas 0.2.8 中的 pf.screen 同时生成和显示绘图的示例:
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()
结果:
免责声明:我是 pyformulas 的维护者。
参考:Matplotlib: save plot to numpy array
实时绘图
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
如果数据量太大,您可以使用简单的计数器降低更新率
cnt += 1
if (cnt == 10): # update plot each 10 points
plt.draw()
plt.pause(0.01)
cnt = 0
程序退出后保留地块
这是我无法找到满意答案的实际问题,我想要在脚本完成后不关闭的绘图(如 MATLAB),
仔细想想,脚本完成后,程序就终止了,没有逻辑的方式来保持剧情,所以有两个选择
- 阻止脚本退出(那是 plt.show() 而不是我想要的)
- 运行单独线程上的情节(太复杂)
这对我来说并不令人满意,所以我找到了另一种开箱即用的解决方案
保存到文件并在外部查看器中查看
为此,保存和查看应该都很快,查看者不应锁定文件,应该自动更新内容
选择保存格式
基于矢量的格式既小又快
- SVG 不错,但找不到合适的查看器,除了默认需要手动刷新的网络浏览器
- PDF可以支持矢量格式,还有支持实时更新的轻量级查看器
具有实时更新功能的快速轻量级查看器
对于 PDF 有几个不错的选择
在 Windows 上,我使用 SumatraPDF,它免费、快速且轻便(我的情况仅使用 1.8MB RAM)
示例代码和结果
将绘图输出到文件的示例代码
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")
在第一个 运行 之后,在上面提到的其中一个查看器中打开输出文件并欣赏。
这是 VSCode 和 SumatraPDF 的屏幕截图,而且这个过程足够快以获得半实时更新率(我的设置可以接近 10Hz,只需在间隔之间使用 time.sleep()
)
其中很多答案都非常夸张,据我所知,答案并不难理解。
如果你愿意,你可以使用 plt.ion()
,但我发现使用 plt.draw()
也同样有效
对于我的特定项目,我正在绘制图像,但您可以使用 plot()
或 scatter()
或其他任何代替 figimage()
的东西,这无关紧要。
plt.figimage(image_to_show)
plt.draw()
plt.pause(0.001)
或
fig = plt.figure()
...
fig.figimage(image_to_show)
fig.canvas.draw()
plt.pause(0.001)
如果您使用的是实际数字。
我使用@krs013 和@Default Picture 的答案来解决这个问题
希望这可以避免有人在单独的线程上发布每个人物,或者不必阅读这些小说来弄清楚这个问题
Python 包 drawnow 允许以非阻塞方式实时更新绘图。
它还可以与网络摄像头和 OpenCV 一起使用,例如为每一帧绘制测量值。
见 original post.
对我来说是最容易理解的,但是在执行后续 subplot
命令时出现以下错误,而我刚刚执行 show
时不存在该命令:
MatplotlibDeprecationWarning: Adding an axes using the same arguments
as a previous axes currently reuses the earlier instance. In a future
version, a new instance will always be created and returned.
Meanwhile, this warning can be suppressed, and the future behavior
ensured, by passing a unique label to each axes instance.
为了避免这个错误,它有助于在用户点击回车后关闭(或clear)绘图。
这是对我有用的代码:
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()
我发现 plt.pause(0.001)
命令是唯一需要的,没有别的。
plt.show() 和 plt.draw() 是不必要的和/或以一种或另一种方式阻塞。所以这是一个绘制和更新图形并继续运行的代码。本质上 plt.pause(0.001) 似乎最接近 matlab 的 drawnow。
不幸的是,这些图不会是交互式的(它们会冻结),除非您插入一个 input() 命令,但随后代码将停止。
plt.pause(interval) 命令的文档指出:
If there is an active figure, it will be updated and displayed before the pause......
This can be used for crude animation.
这正是我们想要的。试试这个代码:
import numpy as np
from matplotlib import pyplot as plt
x = np.arange(0, 51) # x coordinates
for z in range(10, 50):
y = np.power(x, z/10) # y coordinates of plot for animation
plt.cla() # delete previous plot
plt.axis([-50, 50, 0, 10000]) # set axis limits, to avoid rescaling
plt.plot(x, y) # generate new plot
plt.pause(0.1) # pause 0.1 sec, to force a plot redraw
最近几天一直在玩Numpy和matplotlib。我在尝试使 matplotlib 绘制函数而不阻止执行时遇到问题。我知道这里已经有很多关于 SO 的帖子在问类似的问题,而且我已经用谷歌搜索了很多但还没有成功。
我试过像某些人建议的那样使用 show(block=False),但我得到的只是一个冻结 window。如果我只是调用 show(),结果会正确绘制,但执行会被阻止,直到 window 关闭。从我读过的其他线程来看,我怀疑 show(block=False) 是否有效取决于后端。这个对吗?我的后端是 Qt4Agg。你能看看我的代码并告诉我你是否发现错误吗?这是我的代码。感谢您的帮助。
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 __name__ == '__main__':
main()
PS。我忘了说我想在每次绘制内容时更新现有的 window,而不是创建一个新的。
我花了很长时间寻找解决方案,找到了this answer。
看起来,为了得到你(和我)想要的东西,你需要 plt.ion()
、plt.show()
(而不是 block=False
)的组合,最重要的是, plt.pause(.001)
(或任何你想要的时间)。 pause 是必需的,因为 GUI 事件在主代码休眠时发生,包括绘图。这可能是通过从休眠线程中获取时间来实现的,所以也许 IDE 会搞砸它——我不知道。
这是一个适用于 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 __name__ == '__main__':
main()
一个对我有用的简单技巧如下:
- 在节目中使用 block = False 参数:plt.show(block = False)
- 在 .py 脚本的末尾使用另一个plt.show()。
示例:
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()
注意:plt.show()
是我脚本的最后一行。
您可以通过将绘图写入数组,然后在不同的线程中显示该数组来避免阻塞执行。这是使用 pyformulas 0.2.8 中的 pf.screen 同时生成和显示绘图的示例:
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()
结果:
免责声明:我是 pyformulas 的维护者。
参考:Matplotlib: save plot to numpy array
实时绘图
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
如果数据量太大,您可以使用简单的计数器降低更新率
cnt += 1
if (cnt == 10): # update plot each 10 points
plt.draw()
plt.pause(0.01)
cnt = 0
程序退出后保留地块
这是我无法找到满意答案的实际问题,我想要在脚本完成后不关闭的绘图(如 MATLAB),
仔细想想,脚本完成后,程序就终止了,没有逻辑的方式来保持剧情,所以有两个选择
- 阻止脚本退出(那是 plt.show() 而不是我想要的)
- 运行单独线程上的情节(太复杂)
这对我来说并不令人满意,所以我找到了另一种开箱即用的解决方案
保存到文件并在外部查看器中查看
为此,保存和查看应该都很快,查看者不应锁定文件,应该自动更新内容
选择保存格式
基于矢量的格式既小又快
- SVG 不错,但找不到合适的查看器,除了默认需要手动刷新的网络浏览器
- PDF可以支持矢量格式,还有支持实时更新的轻量级查看器
具有实时更新功能的快速轻量级查看器
对于 PDF 有几个不错的选择
在 Windows 上,我使用 SumatraPDF,它免费、快速且轻便(我的情况仅使用 1.8MB RAM)
示例代码和结果
将绘图输出到文件的示例代码
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")
在第一个 运行 之后,在上面提到的其中一个查看器中打开输出文件并欣赏。
这是 VSCode 和 SumatraPDF 的屏幕截图,而且这个过程足够快以获得半实时更新率(我的设置可以接近 10Hz,只需在间隔之间使用 time.sleep()
)
其中很多答案都非常夸张,据我所知,答案并不难理解。
如果你愿意,你可以使用 plt.ion()
,但我发现使用 plt.draw()
也同样有效
对于我的特定项目,我正在绘制图像,但您可以使用 plot()
或 scatter()
或其他任何代替 figimage()
的东西,这无关紧要。
plt.figimage(image_to_show)
plt.draw()
plt.pause(0.001)
或
fig = plt.figure()
...
fig.figimage(image_to_show)
fig.canvas.draw()
plt.pause(0.001)
如果您使用的是实际数字。
我使用@krs013 和@Default Picture 的答案来解决这个问题
希望这可以避免有人在单独的线程上发布每个人物,或者不必阅读这些小说来弄清楚这个问题
Python 包 drawnow 允许以非阻塞方式实时更新绘图。
它还可以与网络摄像头和 OpenCV 一起使用,例如为每一帧绘制测量值。
见 original post.
subplot
命令时出现以下错误,而我刚刚执行 show
时不存在该命令:
MatplotlibDeprecationWarning: Adding an axes using the same arguments as a previous axes currently reuses the earlier instance. In a future version, a new instance will always be created and returned. Meanwhile, this warning can be suppressed, and the future behavior ensured, by passing a unique label to each axes instance.
为了避免这个错误,它有助于在用户点击回车后关闭(或clear)绘图。
这是对我有用的代码:
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()
我发现 plt.pause(0.001)
命令是唯一需要的,没有别的。
plt.show() 和 plt.draw() 是不必要的和/或以一种或另一种方式阻塞。所以这是一个绘制和更新图形并继续运行的代码。本质上 plt.pause(0.001) 似乎最接近 matlab 的 drawnow。
不幸的是,这些图不会是交互式的(它们会冻结),除非您插入一个 input() 命令,但随后代码将停止。
plt.pause(interval) 命令的文档指出:
If there is an active figure, it will be updated and displayed before the pause...... This can be used for crude animation.
这正是我们想要的。试试这个代码:
import numpy as np
from matplotlib import pyplot as plt
x = np.arange(0, 51) # x coordinates
for z in range(10, 50):
y = np.power(x, z/10) # y coordinates of plot for animation
plt.cla() # delete previous plot
plt.axis([-50, 50, 0, 10000]) # set axis limits, to avoid rescaling
plt.plot(x, y) # generate new plot
plt.pause(0.1) # pause 0.1 sec, to force a plot redraw