在使用调度程序包时,在类中封装matplotlib绘图更新

时间:2015-01-19 00:42:39

标签: python matplotlib plot dispatch

我正在尝试在类中封装matplotlib绘图更新。想法是调度信号(即事件),并且监听实例应该更新绘图。我目前正在使用dispatcher,但我也尝试使用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)

非封装版本按预期工作。当我使用该类时,我可以调用更新方法,它可以完成它应该做的事情。但我真正需要的是能够更新绘图而无需显式调用更新方法(以避免不希望的耦合)。因此调度包。

我已经测试过并且正在调用_update_callback。我知道事件正在被调度(否则,非封装版本将不起作用),所以我能想到的唯一解释是plot_update.connect(_update_callback)没有做它应该做的事情,但我不知道为什么?

关于为什么这不能按预期工作的任何想法?更好的是,关于如何实现理想行为的任何想法?

1 个答案:

答案 0 :(得分:0)

经过一些进一步的研究后,我意识到问题是connect() dispatcher.Signal方法默认情况下会尝试将弱引用用于回调函数。我的问题的解决方案是将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)

希望这有助于其他人!