让 matplotlib 按钮断开自己的回调

Have a matplotlib button disconnect its own callback

我想要一个只能使用一次的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。因此,我陷入了先有鸡还是先有蛋的问题。

有解决方法,例如定义 class 以便我可以通过 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```

你可以用 class 包裹它:



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)

但是,人们可能希望更好地处理定义了多个事件的情况。此外,您还可以定义一个 class 的 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:

另一种方法是覆盖按钮的 _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)