使用调度程序包时,将 matplotlib 绘图更新封装在 class 中
Encapsulating matplotlib plot update in a class, when using dispatcher package
我正在尝试将 matplotlib 绘图更新封装在 class 中。这个想法是发送一个信号(即事件),并且监听实例应该更新一个图。我目前正在使用 dispatcher, but I've also tried with PyDispatcher 得到相同的结果。
这是我想做的事情的简化代码,包括有效的和无效的:
from time import sleep
from random import random
import matplotlib.pyplot as plt
import dispatch
plt.ion()
# define signal to be dispatched
plot_update = dispatch.Signal(providing_args=["y"])
# non-encapsulated update (this works alright)
fig = plt.figure('This Works')
ax = fig.add_subplot(111)
line1, = ax.plot([1], [0], 'ko')
ax.axis((0, 2, 0, 1))
def update_callback(sender, **kwargs):
line1.set_ydata(kwargs['y'])
fig.canvas.draw()
plot_update.connect(update_callback)
# class to encapsulate plot update behavior
class DynamicPlot(object):
def __init__(self, fid_name):
self.fig = plt.figure(fid_name)
self.ax = self.fig.add_subplot(111)
self.line1, = self.ax.plot([1], [0], 'ko')
self.ax.axis((0, 2, 0, 1))
self.fig.canvas.draw()
def _update_callback(sender, **kwargs):
self.line1.set_ydata(kwargs['y'])
self.fig.canvas.draw()
# I'd expect this to connect _update_callback to be triggered
# when the `plot_update` signal is dispatched, but that's not
# happening
plot_update.connect(_update_callback)
# Calling this method, will update the plot, but this
# is not what I need (is just here for testing)
def update(self, y):
self.line1.set_ydata(y)
self.fig.canvas.draw()
# test code
if __name__ == "__main__":
# this one should update by listening to events (signal)
d1 = DynamicPlot("This Won't Work")
# this instance is here just for testing, to make sure
# the class can update a plot via a different mechanism
d2 = DynamicPlot("This Will Work Too")
# mocked signal dispatcher
while(True):
plot_update.send(sender=None, y=[random()])
d2.update([random()])
sleep(0.1)
非封装版本按预期工作。当我使用 class 时,我可以调用更新方法,它会执行它应该执行的操作。但我真正需要的是能够在不显式调用更新方法的情况下更新绘图(以避免不必要的耦合)。因此调度包。
我已经测试过 _update_callback
正在被调用。我知道事件正在被调度(否则,非封装版本将不起作用),所以我能想到的唯一解释是 plot_update.connect(_update_callback)
没有做它应该做的,但我不知道为什么?
关于为什么这不能按预期工作有什么想法吗?更好的是,关于如何实现所需行为的任何想法?
经过进一步研究,我意识到问题是 dispatcher.Signal
的 connect()
方法默认尝试使用 弱引用 回调函数.我的问题的解决方案是将 weak=false
传递给 connect()
。这是我在代码中所说的(问题中的示例,但删除了测试和非封装代码):
from time import sleep
from random import random
import matplotlib.pyplot as plt
import dispatch
plot_update = dispatch.Signal(providing_args=["y"])
plt.ion()
class DynamicPlot(object):
def __init__(self, fid_name):
self.fig = plt.figure(fid_name)
plt.clf()
self.ax = self.fig.add_subplot(111)
self.line1, = self.ax.plot([1], [0], 'ko')
self.ax.axis((0, 2, 0, 1))
self.fig.canvas.draw()
plt.show()
def _update_callback(sender, **kwargs):
self.line1.set_ydata(kwargs['y'])
self.fig.canvas.draw()
plot_update.connect(_update_callback, weak=False)
# plot_update.connect(tmp(self))
if __name__ == "__main__":
d1 = DynamicPlot("Not it Works!")
while(True):
plot_update.send_robust(sender=None, y=[random()])
sleep(0.1)
希望这对其他人有帮助!
我正在尝试将 matplotlib 绘图更新封装在 class 中。这个想法是发送一个信号(即事件),并且监听实例应该更新一个图。我目前正在使用 dispatcher, but I've also tried with PyDispatcher 得到相同的结果。
这是我想做的事情的简化代码,包括有效的和无效的:
from time import sleep
from random import random
import matplotlib.pyplot as plt
import dispatch
plt.ion()
# define signal to be dispatched
plot_update = dispatch.Signal(providing_args=["y"])
# non-encapsulated update (this works alright)
fig = plt.figure('This Works')
ax = fig.add_subplot(111)
line1, = ax.plot([1], [0], 'ko')
ax.axis((0, 2, 0, 1))
def update_callback(sender, **kwargs):
line1.set_ydata(kwargs['y'])
fig.canvas.draw()
plot_update.connect(update_callback)
# class to encapsulate plot update behavior
class DynamicPlot(object):
def __init__(self, fid_name):
self.fig = plt.figure(fid_name)
self.ax = self.fig.add_subplot(111)
self.line1, = self.ax.plot([1], [0], 'ko')
self.ax.axis((0, 2, 0, 1))
self.fig.canvas.draw()
def _update_callback(sender, **kwargs):
self.line1.set_ydata(kwargs['y'])
self.fig.canvas.draw()
# I'd expect this to connect _update_callback to be triggered
# when the `plot_update` signal is dispatched, but that's not
# happening
plot_update.connect(_update_callback)
# Calling this method, will update the plot, but this
# is not what I need (is just here for testing)
def update(self, y):
self.line1.set_ydata(y)
self.fig.canvas.draw()
# test code
if __name__ == "__main__":
# this one should update by listening to events (signal)
d1 = DynamicPlot("This Won't Work")
# this instance is here just for testing, to make sure
# the class can update a plot via a different mechanism
d2 = DynamicPlot("This Will Work Too")
# mocked signal dispatcher
while(True):
plot_update.send(sender=None, y=[random()])
d2.update([random()])
sleep(0.1)
非封装版本按预期工作。当我使用 class 时,我可以调用更新方法,它会执行它应该执行的操作。但我真正需要的是能够在不显式调用更新方法的情况下更新绘图(以避免不必要的耦合)。因此调度包。
我已经测试过 _update_callback
正在被调用。我知道事件正在被调度(否则,非封装版本将不起作用),所以我能想到的唯一解释是 plot_update.connect(_update_callback)
没有做它应该做的,但我不知道为什么?
关于为什么这不能按预期工作有什么想法吗?更好的是,关于如何实现所需行为的任何想法?
经过进一步研究,我意识到问题是 dispatcher.Signal
的 connect()
方法默认尝试使用 弱引用 回调函数.我的问题的解决方案是将 weak=false
传递给 connect()
。这是我在代码中所说的(问题中的示例,但删除了测试和非封装代码):
from time import sleep
from random import random
import matplotlib.pyplot as plt
import dispatch
plot_update = dispatch.Signal(providing_args=["y"])
plt.ion()
class DynamicPlot(object):
def __init__(self, fid_name):
self.fig = plt.figure(fid_name)
plt.clf()
self.ax = self.fig.add_subplot(111)
self.line1, = self.ax.plot([1], [0], 'ko')
self.ax.axis((0, 2, 0, 1))
self.fig.canvas.draw()
plt.show()
def _update_callback(sender, **kwargs):
self.line1.set_ydata(kwargs['y'])
self.fig.canvas.draw()
plot_update.connect(_update_callback, weak=False)
# plot_update.connect(tmp(self))
if __name__ == "__main__":
d1 = DynamicPlot("Not it Works!")
while(True):
plot_update.send_robust(sender=None, y=[random()])
sleep(0.1)
希望这对其他人有帮助!