使用调度程序包时,将 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.Signalconnect() 方法默认尝试使用 弱引用 回调函数.我的问题的解决方案是将 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)

希望这对其他人有帮助!