Search code examples
pythonkivy

Kivy: how to add on_release method to floating button (FloatLayout)?


I added a FloatButton on my screen, but I am unable to associate any on_release actions to it. I suspect the issue is linked to the fact that my FloatButton actually inherits from FloatLayout, so it does not have the on_release property? Here is my FloatButton definition in the kv file:

<FloatButton@FloatLayout>:
    id: float_add_button  # Giving id to button
    size_hint: (None, None)
    text: ''
    btn_size: (70, 70)
    size: (70, 70)
    bg_color: (0.404, 0.227, 0.718, 1.0)
    # pos_hint: {'x': .6}
 
    Button:
        text: float_add_button.text
        markup: True
        font_size: 40
        size_hint: (None, None)
        size: float_add_button.btn_size
        pos_hint: {'x': .5, 'y': .8} 
        background_normal: ''
        background_color: (0, 0, 0, 0)
        canvas.before:
            Color:
                rgba: (0.404, 0.227, 0.718, 1.0)
            Ellipse:
                size: self.size
                pos: self.pos

and further down the kv file I assing on_release

<MainWindow>:
    name: 'main'

    RelativeLayout:
        orientation: 'lr-tb'
        size_hint: 1, 1
        padding: '20sp'
        canvas.before:
            Rectangle:
                pos: self.pos
                size: self.size

        # A few buttons here
        #
        #
        #

    FloatButton:
        id: addButton
        text: '+'
        markup: True
        background_color: 1, 0, 1, 0
        pos_hint: {"x":.5,"top":.5}
        on_release:
            print("[TEST] on_release works!")

However, I get the following error:

 Traceback (most recent call last):
   File "C:\Users\gitcanzo\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\kivy\lang\builder.py", line 734, in _apply_rule 
     raise AttributeError(key)
 AttributeError: release

 During handling of the above exception, another exception occurred:

 Traceback (most recent call last):
   File "D:\Recipy_project\Recipy\main.py", line 1, in <module>
     from Recipy.app import Recipy
   File "D:\Recipy_project\Recipy\Recipy\app.py", line 21, in <module>
     kv = Builder.load_file("Recipy/ui/layout.kv")
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "C:\Users\gitcanzo\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\kivy\lang\builder.py", line 310, in load_file   
     return self.load_string(data, **kwargs)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "C:\Users\gitcanzo\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\kivy\lang\builder.py", line 412, in load_string 
     self._apply_rule(
   File "C:\Users\gitcanzo\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\kivy\lang\builder.py", line 665, in _apply_rule 
     child.apply_class_lang_rules(
   File "C:\Users\gitcanzo\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\kivy\uix\widget.py", line 470, in apply_class_lang_rules
     Builder.apply(
   File "C:\Users\gitcanzo\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\kivy\lang\builder.py", line 545, in apply       
     self._apply_rule(
   File "C:\Users\gitcanzo\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\kivy\lang\builder.py", line 741, in _apply_rule 
     raise BuilderException(
 kivy.lang.builder.BuilderException: Parser: File "D:\Recipy_project\Recipy\Recipy\ui\layout.kv", line 590:
 ...
     588:               pos_hint: {"x":.5,"top":.5}
     589:               on_release:
 >>  590:                       print("[TEST] on_release works!")
     591:                       # root.manager.transition.direction = "left"
     592:                       # app.root.current = 'add'
 ...
 AttributeError: release
   File "C:\Users\gitcanzo\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\kivy\lang\builder.py", line 734, in _apply_rule 
     raise AttributeError(key)

Solution

  • The cause of the problem is exactly what you say, your FloatButton class is derived from FloatLayout not from Button, so it does not have the on_release event.

    One option is to define that custom event in your FloatButton class:

    class FloatButton(FloatLayout):
        def __init__(self, *args, **kwargs):
            self.register_event_type('on_release')
            super().__init__(*args, **kwargs)
    
        def on_release(self, *args):
            pass
    

    and dispatch it when the internal button is pressed:

    <FloatButton@FloatLayout>:
        # ...
        Button:
            on_release: root.dispatch('on_release')
    

    A complete reproducible example:

    from kivy.app import App
    from kivy.lang import Builder
    from kivy.uix.floatlayout import FloatLayout
    
    
    kv = '''
    <FloatButton@FloatLayout>:
        id: float_add_button  # Giving id to button
        size_hint: (None, None)
        text: ''
        btn_size: (70, 70)
        size: (70, 70)
        bg_color: (0.404, 0.227, 0.718, 1.0)
        # pos_hint: {'x': .6}
    
        Button:
            on_release: root.dispatch('on_release')
            text: float_add_button.text
            markup: True
            font_size: 40
            size_hint: (None, None)
            size: float_add_button.btn_size
            pos_hint: {'x': .5, 'y': .8}
            background_normal: ''
            background_color: (0, 0, 0, 0)
            canvas.before:
                Color:
                    rgba: (0.404, 0.227, 0.718, 1.0)
                Ellipse:
                    size: self.size
                    pos: self.pos
    
    
    <MainWindow@RelativeLayout>:
        name: 'main'
    
        FloatButton:
            id: addButton
            text: '+'
            markup: True
            background_color: 1, 0, 1, 0
            pos_hint: {"right":.5,"top":.5}
            on_release:
                print("[TEST] on_release works!")
    
    MainWindow:
    '''
    
    
    class FloatButton(FloatLayout):
        def __init__(self, *args, **kwargs):
            self.register_event_type('on_release')
            super().__init__(*args, **kwargs)
    
        def on_release(self, *args):
            pass
    
    
    class MainWindow(App):
        def build(self):
            return Builder.load_string(kv)
    
    
    if __name__ == '__main__':
        MainWindow().run()