Binding function to a button inside a Custom Widget from outside

1.1k views Asked by At

I made this Widget:

<ImageButton>:
    source:''
    text:''
    release_fn: None
    Button:
        on_release: root.release_fn()
        size: root.size
        pos: root.pos
        BoxLayout:
            padding: 30
            size: self.parent.size
            pos: self.parent.pos
            orientation:'vertical'
            Image:
                id: img
                source: root.source
            Label:
                size_hint_y:.3
                text: root.text

Now I want to pass to it a function to be called when the button is released, but I can't figure out how to do it (and I couldn't find anything with the answer...)

The following gives me only a syntax error in the last line (the rest - and good working part - of the kivy file is omitted)

ImageButton:
    source: 'iconUsuario.png'
    text: 'Usuario'
    release_fn: print('HI!')


     print('HI!')
         ^
 SyntaxError: invalid syntax
2

There are 2 answers

0
griloHBG On BEST ANSWER

Well, it turned out that it seemed not to be the best idea and I used a custom event:

class ImageButton(Widget):
    def __init__(self,*args,**kwargs):
        super(ImageButton,self).__init__(*args,**kwargs)
        self.register_event_type('on_release')

    def on_release(self,*args):
        pass

    def callback(self,instance):
        self.dispatch('on_release')
        pass

kv file:

<ImageButton>:
    source:''
    text:''
    Button:
        on_release: root.callback(self)
        size: root.size
        pos: root.pos
        BoxLayout:
            padding: 30
            size: self.parent.size
            pos: self.parent.pos
            orientation:'vertical'
            Image:
                id: img
                source: root.source
            Label:
                size_hint_y:.3
                text: root.text
1
Peter Badida On

Python doesn't like using its print as a name for anything else for some reason. Try to run this:

def print():
    pass
def print:
    pass

always a SyntaxError at print. KV lang does basically this:

eval(print('bla'))

So, let's have a wrapper for the print. You probably won't use pure print anyway. Then you may notice the function returns None, that's because it's called with on_release: root.release_fn() as the first value release_fn: something() is just like a partial (and when you call partial(func, arg)() the function is really called).

That's why you need to import functools.partial, to do it one more time, so that the function is called at the desired place after the button is pressed, not immediately:

on_release: root.release_fn()

Example:

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

kv = """
#:import partial functools.partial
ImageButton:
    release_fn: partial(app.prints, 'bla')

<ImageButton@ButtonBehavior+Image>:
    source:''
    text:''
    release_fn: None
    Button:
        on_release: root.release_fn()
        size: root.size
        pos: root.pos
        BoxLayout:
            padding: 30
            size: self.parent.size
            pos: self.parent.pos
            orientation:'vertical'
            Image:
                id: img
                source: root.source
            Label:
                size_hint_y:.3
                text: root.text
"""


class TestApp(App):
    def prints(self, value):
        print(value)

    def build(self):
        return Builder.load_string(kv)


if __name__ == '__main__':
    TestApp().run()