代码之家  ›  专栏  ›  技术社区  ›  Thomas Kühn

与matplotlib计时器混淆

  •  1
  • Thomas Kühn  · 技术社区  · 8 年前

    我尝试实施上述解决方法 here draw_event 在用户缩放到绘图时触发。这是为了改进 this answer . 在解决方案中 Timer 已添加到 Canvas Figure ,这会将绘制功能延迟一段时间。

    在链接答案中,这是针对单个数字进行的,对计时器的引用存储为 self.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() ,这对我来说很好。有问题的解决方案称为“\u redraw\u later\u not\u ok()”,它产生以下输出:

    start
    stop
    Segmentation fault: 11
    

    我将Mac与High Sierra、Python 3.6.4或Python 2.7.14以及Matplotlib 2.1.1一起使用 MacOSX 后端。当我切换到 TkAgg 后端(速度非常慢),代码工作正常。有人能解释发生了什么事吗?

    1 回复  |  直到 8 年前
        1
  •  1
  •   ImportanceOfBeingErnest    8 年前

    与通常使用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可能有意义,可以清楚地显示问题。