Event bubbling of custom events

37 views
Skip to first unread message

Paul van Haren

unread,
May 19, 2023, 4:54:41 AM5/19/23
to Kivy users support
Hi all,

I want to create an app, consisting of multiple screens managed by a screen manager. 
In the screen manager code, I register a custom event 'my_event'  and then at some point the event is dispatched, following this documentation. The 'on_my_event()'  method in the screen manager fires correctly when the event is dispatched. 

The code of the screens managed by the screen managers also implement an 'on_my_event' method. I would like to see these methods fired as well, preferably only the method of the active screen. I tried the guidance from this documentation and many variations therefore, but I was never able to trigger the event handler in the screen code for this customer event. 

How can I trigger an event handler in my screen code for a custom event dispatched by the screen manager?

Thank you for your help.

ElliotG

unread,
May 20, 2023, 12:55:09 PM5/20/23
to Kivy users support
Share some example code, and be more specific on what you are trying to achieve.  Also see: https://kivy.org/doc/stable/api-kivy.event.html#kivy.event.EventDispatcher

Paul van Haren

unread,
May 23, 2023, 2:11:07 AM5/23/23
to Kivy users support
The main objective is to implement code that transforms gestures or strokes into events. The approach is as follows: event handlers for touch_down, touch_move and touch_up record the positions of these touch events. On touch_up, the recorded positions or gesture is decoded / looked up in a GestureDatabase, a gesture event is fired and the name of the gesture is passed as a parameter. This all works are expected. 

Next, I want to bubble the gesture event to all other (active) widgets, so that only one widget has the code to record the touch events and the lookup in the GestureDatabase, and all connected widgets can respond to the gesture event. The code to record the touch events is implemented in a MDScreenManager and I want the child MDScreens to respond to the gesture events.

The code for the MyScreenManager class implements a touch event handler as below. The on_gesture method in the MyScreenManager class fires as expected.  However, the on_gesture in  the  BatchesScreen class is never called.

class BatchesScreen(MDScreen):
    def __init__(self, name):
        super().__init__(name=name)
        self.add_widget(self.build())
        
    def build(self):
        print(self.__class__)
        return Builder.load_file('uix/batches.kv')
 
    def on_gesture(self, *args):
        print('Gesture dispacthed for Batches Screen: {}'.format(args[0]))


class TopLayout(MDBoxLayout):   
    pass
 
    
class MyScreenManager(MDScreenManager, EventDispatcher):
    _strokes = {
        'right' : 13, 
        'left' : 31, 
        'down' : 14, 
        'up' : 41
    }  
    
    def __init__(self, **kwargs):
        self._gestureDb = GestureDatabase()
        for name, points in self._strokes.items():
            self._gestureDb.add_gesture(Gesture(name=name, point_list=points))
        self.register_event_type('on_gesture')
        super(MyScreenManager, self).__init__(**kwargs)
        
    def on_touch_down(self, touch):
        touch.ud['line'] = list()
        touch.ud['line'].append(touch.pos)       
        return MDScreenManager.on_touch_down(self, touch)

    def on_touch_move(self, touch):
        touch.ud['line'].append(touch.pos)       
        return MDScreenManager.on_touch_move(self, touch)
    
    def on_touch_up(self, touch):
        touch.ud['line'].append(touch.pos)       
        stroke = self._gestureDb.find(Gesture(point_list=touch.ud['line']), 0.5, False)
        if stroke is not None:
            self.dispatch('on_gesture', stroke[1].name)     
        MDScreenManager.on_touch_up(self, touch)
              
    def on_gesture(self, *args):
        print('Gesture dispacthed: {}'.format(args[0]))

        
class MyApp(MDApp):

    def build(self):
        root_widget = TopLayout()
        root_widget.ids['batchesListScreen'].add_widget(BatchesScreen().build(), 1000)
        root_widget.ids['recipesListScreen'].add_widget(RecipesScreen().build(), 1000)
        
        root_widget.ids['manager'].current = 'RecipesScreen'
            
        return root_widget

MyApp().run()        

The widget hierarchy is defined in a KV-file:

<BatchesScreen@MDScreen>:
    name: 'BatchesScreen'
    MDBoxLayout:
    
<RecipesScreen@MDScreen>:
    name: 'RecipesScreen'
    MDBoxLayout:
    
<MyScreenManager@MDScreenManager>:
    transition: MDSlideTransition()
    direction: 'left'
    
<TopLayout@MDBoxLayout>:
    orientation: 'vertical'
    MDTopAppBar:
        pos_hint: {"top": 1}
        size_hint: (1, 0.1)
        elevation: 4
        title: "My Toolbar"
        left_action_items: [["menu", lambda x: app.root.ids['nav_drawer'].set_state("open")]]

    MDNavigationLayout:
    id: top
    size_hint: (1, 0.9)
            pos_hint: {"top": 0.9}
    MyScreenManager:
        id: manager
        BatchesScreen:
            id: batchesListSscreen
RecipesScreen:
    id: recipesListScreen

    MDNavigationDrawer:
        id: nav_drawer
        anchor: "left"
        radius: (0, 16, 16, 0)
    
        MDNavigationDrawerMenu:
    MDNavigationDrawerHeader:
        title: "My App"
        text: "Header text"

    DrawerClickableItem:
        text: "Batches"
                on_press:
                    root.ids['manager'].current = 'BatchesScreen'
                    root.ids['nav_drawer'].set_state("closed")

    DrawerClickableItem:
        text: "Recipes"
                on_press:
                    root.ids['manager'].current = 'RecipesScreen'
                            root.ids['nav_drawer'].set_state("closed")

  
Op zaterdag 20 mei 2023 om 18:55:09 UTC+2 schreef ElliotG:

Robert

unread,
May 23, 2023, 1:07:11 PM5/23/23
to Kivy users support
Reply all
Reply to author
Forward
0 new messages