Попросите кнопку matplotlib отключить собственный обратный вызов - PullRequest
0 голосов
/ 06 августа 2020

Я хотел бы иметь кнопку 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()

Чтобы отключить обратный вызов, мне нужен его идентификатор обратного вызова, 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```

1 Ответ

1 голос
/ 06 августа 2020

Вы можете обернуть вокруг него класс:



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, который автоматически отключается после первого события?!

EDIT:

Вышеупомянутый подход не работает. Вместо этого вы можете использовать атрибут 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)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...