【问题标题】:Have a matplotlib button disconnect its own callback让 matplotlib 按钮断开自己的回调
【发布时间】:2020-08-14 18:21:27
【问题描述】:

我想要一个只能使用一次的 matplotlib 按钮。理想情况下,我可以通过断开回调来做到这一点。但是,回调断开本身存在时间问题。

import matplotlib.pyplot as plt
from matplotlib.widgets import Button

fig, ax = plt.subplots()
donebutton = Button(ax, "Disconnect the button")
def donecallback(event):
    donebutton.disconnect(donecid)
    print("Disconnected")

donecid = donebutton.on_clicked(donecallback)

plt.show()

要断开回调,我需要它的回调 ID,donecid,这是我在连接回调时获得的。要连接回调,我首先必须定义它,donecallback。要定义回调,我必须已经知道 CID。因此,我陷入了先有鸡还是先有蛋的问题。

有一些变通方法,例如定义一个类以便我可以通过self 将数据传递到回调中,具有一个跟踪按钮是否被按下的全局标志,或者创建一个没有回调的新的相同按钮连接的。但是,如果有更简单的方法,那就太好了。有吗?

编辑: 当我使用我提供的代码时,会发生以下错误。或者dnalow提供的代码。

    func(*args, **kwargs)
  File "C:\Users\MyName\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0\LocalCache\local-packages\Python38\site-packages\matplotlib\widgets.py", line 210, in _release
    for cid, func in self.observers.items():
RuntimeError: dictionary changed size during iteration```

【问题讨论】:

    标签: python matplotlib callback


    【解决方案1】:

    你可以在它周围包裹一个类:

    
    
    class MyButton(Button):
        def on_clicked(self, func):
            self.cb_id = super(MyButton, self).on_clicked(func)
            return self.cb_id
    
        def disconnect(self):
            return super(MyButton, self).disconnect(self.cb_id)
    
    
    
    donebutton = MyButton(ax, "Disconnect the button")
    def donecallback(event):
        donebutton.disconnect()
        print("Disconnected")
    
    donebutton.on_clicked(donecallback)
    

    但是,您可能希望更好地处理定义多个事件的情况。另外,您更愿意定义一个在第一个事件后自动断开连接的 Button 类?!

    编辑:

    上述方法不起作用。相反,您可以使用active 属性来停用整个按钮。它不会与某个回调函数断开连接,因此它不是您真正要求的。

    按照你的例子:

    import matplotlib.pyplot as plt
    from matplotlib.widgets import Button
    
    fig, ax = plt.subplots()
    donebutton = Button(ax, "Disconnect the button")
    def donecallback(event):
        donebutton.active = False
        print("Disconnected")
    
    donecid = donebutton.on_clicked(donecallback)
    
    plt.show()
    

    编辑 2:

    另一种方法是重写 Button 的 _release 方法:

    class SingleUseButton(Button):
        def _release(self, event):
            if self.ignore(event):
                return
            if event.canvas.mouse_grabber != self.ax:
                return
            event.canvas.release_mouse(self.ax)
            if not self.eventson:
                return
            if event.inaxes != self.ax:
                return
            for cid in list(self.observers):
                func = self.observers.pop(cid)
                func(event)
    
    

    【讨论】:

    • 谢谢 - 这是我在回答末尾提到的第一个解决方法。我想知道是否有办法在没有辅助对象的情况下做到这一点,但这可能是最好的解决方案。
    • 我刚试过这个,它实际上给了我在不定义类的情况下尝试做事时遇到的相同错误:“RuntimeError: dictionary changed size during iteration”
    • 你能给出完整的回溯吗?
    • 它不起作用是有道理的。按钮连接是字典的一部分。在遍历所有连接的过程中,字典的大小不得改变。因此,无法在活动中使用断开连接,您的问题仍未得到解答
    • 你可以在这里找到Button类的定义:matplotlib.org/3.1.1/_modules/matplotlib/widgets.html#Button 如果你定义了你自己的Button,继承自那个,你只需要重写_release方法。你需要一个例子吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-19
    相关资源
    最近更新 更多