Chazara
Chazara

Reputation: 169

Kivy - unbind all methods attached to a button

I am writing a program that has a panel of buttons on the right hand side, that successfully binds a method to each, dependant on user input/actions. My issues is that I cannot unbind() the method explicitely, as the method bound is dynamic.

Consider;

    i = 1
    while i <= 8:
        string = "action" + str(i) 
        #Buttons named 'action1, action1, ..., action8'
        ref = self.ids[string] 
        ref.text = ""
        observers = ref.get_property_observers('on_release')
        print observers
        ref.unbind()
        ref.bind(on_release=partial(self.BlankMethod, arg1))
        i += 1

The lines;

        observers = ref.get_property_observers('on_release')
        print observers

Show that I have an increasing list of weakmethods bound, every time the method is called, but the unbind function does not unbind the method.

Although my code example does not show it, the bound method changes regularly, and the self.BlankMethod was meant to override the original binding. This is not the case, and the Button's bound methods increase.

[<kivy.weakmethod.WeakMethod object at 0x7f8cc826a810>] [<kivy.weakmethod.WeakMethod object at 0x7f8cc826a850>, <kivy.weakmethod.WeakMethod object at 0x7f8cc826acd0>]

I have tried;

        observers = ref.get_property_observers('on_release')
        if observers != []:
            for observer in observers:
                ref.unbind(observer)
        ref.bind(on_release=partial(self.Blank))

But I am returned the error;

TypeError: unbind() takes exactly 0 positional arguments (1 given)

I had a look at using funbind() function but subsequently given;

AttributeError: 'Button' object has no attribute 'funbind'

Trying to use fbind() prior to funbind() also gives the same error, but with respect to the button not having an fbind() attribute.

How can I list all bound methods of an object, and subsequently unbind them?

Upvotes: 3

Views: 2672

Answers (1)

zeeMonkeez
zeeMonkeez

Reputation: 5157

Here is a toy example demonstrating setting, saving, and later clearing callbacks. When a_button or b_button is pressed, set_callbacks is triggered, which binds callbacks to all of MyButtons. MyButton has a list property _cb storing user defined callbacks. c_button will trigger clear_callbacks, which goes through each of the MyButton's _cb lists and unbinds each stored callback.

from kivy.app import App
from kivy.lang import Builder
from functools import partial

kv_str = """
<MyButton@Button>:
    _cb: []
    my_id: 1
    text: "hello {}".format(self.my_id)
BoxLayout:
    orientation: 'vertical'
    BoxLayout:
        Button:
            id: a_button
            text: "a button"
        Button:
            id: b_button
            text: "b button"
        Button:
            id: c_button
            text: "clear callbacks"
    GridLayout:
        cols: 4
        id: grid
        MyButton:
            my_id: 1
        MyButton:
            my_id: 2
        MyButton:
            my_id: 3
        MyButton:
            my_id: 4
"""

def cb(sender, arg='nothing'):
    print "we got: {} pressed with {}".format(sender.text, arg)

class MyApp(App):
    def build(self):
        root = Builder.load_string(kv_str)

        def set_callbacks(sender):
            for b in root.ids.grid.children:
                new_cb = partial(cb, arg=sender.text)
                b._cb.append(new_cb)
                b.fbind('on_press', new_cb)
        def clear_callbacks(sender):
            for b in root.ids.grid.children:
                for cb in b._cb:
                    b.funbind('on_press', cb)
                b._cb = []
        root.ids.a_button.bind(on_press=set_callbacks)
        root.ids.b_button.bind(on_press=set_callbacks)
        root.ids.c_button.bind(on_press=clear_callbacks)
        return root

if __name__ == '__main__':
    a = MyApp()
    a.run()

Upvotes: 3

Related Questions