【问题标题】:Confusion with matplotlib timers与 matplotlib 计时器混淆
【发布时间】:2018-01-29 19:45:42
【问题描述】:

我尝试实施here 提到的解决方法,以解决当用户放大绘图时触发draw_event 时图形无法正确更新的问题。这是为了改进this answer。在解决方案中,在FigureCanvas 中添加了一个Timer,这会延迟绘制功能一小会儿。

在链接的答案中,这是针对单个图形完成的,对计时器的引用存储为 self.timer 到包含更新函数的类。我希望有一些更一般的行为,并允许对许多 Figure 实例进行此操作。因此我尝试不保存Timer 参考(我不再需要它了)。但是当我这样做时,脚本会因分段错误(无回溯)而崩溃。这是我的代码:

from matplotlib import pyplot as plt
import numpy as np

##plt.switch_backend('TkAgg')

class DrawEventHandler:

    def __init__(self):
        x = np.linspace(0,1,10)
        y = np.sin(x)

        self.fig, self.ax = plt.subplots()
        self.ax.plot(x,y)

        self.ax.figure.canvas.mpl_connect('draw_event', self.update)


    def update(self, event = None):
        self._redraw_later_ok()
        ##self._redraw_later_not_ok(self.fig)

    def _redraw_later_ok(self):
        self.timer = self.fig.canvas.new_timer(interval=10)
        self.timer.single_shot = True
        self.timer.add_callback(lambda : self.fig.canvas.draw_idle())
        self.timer.start()

    def _redraw_later_not_ok(self, fig):
        print('start')
        timer = fig.canvas.new_timer(interval=10)
        timer.single_shot = True
        timer.add_callback(lambda : fig.canvas.draw_idle())
        timer.start()
        print('stop')    

if __name__ == '__main__':
    my_handler = DrawEventHandler()
    plt.show()

原始解决方案实现为_redraw_later_ok(),对我来说效果很好。有问题的解决方案称为 `_redraw_later_not_ok()',它会产生以下输出:

start
stop
Segmentation fault: 11

我使用带有 High Sierra、Python 3.6.4 或 Python 2.7.14 和 Matplotlib 2.1.1 以及 MacOSX 后端的 Mac。当我切换到TkAgg 后端(非常慢)时,代码工作正常。谁能解释一下是怎么回事?

【问题讨论】:

  • 为什么需要上交self.fig作为参数?您可以随时直接访问它。
  • @MikeMüller 在这种情况下是的,但这只是为了 MCVE。我的真实例子更复杂。我会将其链接到问题。

标签: python macos matplotlib


【解决方案1】:

与使用 GUI 事件循环的交互功能一样,您需要保留对处理回调的对象的引用。

这段代码中不存储定时器的方法的问题

def _redraw_later_not_ok(self, fig):
    print('start')
    timer = fig.canvas.new_timer(interval=10)
    timer.single_shot = True
    timer.add_callback(lambda : fig.canvas.draw_idle())
    timer.start()
    print('stop')

timer 会在_redraw_later_not_ok 函数终止后被垃圾回收(即直接在打印停止之后)。此时将有一个指向内存中某个位置的指针,该位置可能会或可能不会存储回调(更多)。因此,python 解释器调用回调的尝试将(很可能)失败。

解决方案确实是始终保持对引导回调的对象的引用。这是_redraw_later_ok 中显示的解决方案,其中计时器是一个类属性self.timer。这样它就可以在以后调用回调时使用。

我不明白使用这种工作方法在多大程度上会阻止使用多个数字,因此创建一个包含两个数字的 MWE 可能是有意义的,以清楚地显示问题。

【讨论】:

  • 感谢您解决这个问题。这个问题更多的是关于理解,而不是解决方案。我只是不知道垃圾收集部分。
  • 可能类似于self.list_of_timers,它为每个数字托管一个计时器?
猜你喜欢
  • 2020-09-19
  • 2012-12-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多