Ou Tsei
Ou Tsei

Reputation: 482

Kivy custom event propagation

I am making a Python/Kivy GUI for internet connected device. There is lot of a sync stuff going on, as the device uses WAMP pub/sub method for communication. The problem is that I don't know how to propagate the custom events, suchs as on_connect, on_disconnect etc from the kivy app to the appropriate kivy widget.

Here is what I have now, stripped to the bare minimum for better readability, also dummy connect button, attached to simulate connect event happening.

MyApp.py:

from kivy.app import App
from kivy.config import Config
from MainPage import MainPage
from AboutPage import AboutPage
from MainContainer import MainContainer

class MyApp(App):
    def build(self):
        self.container = MainContainer()
        self.register_event_type('on_connect')
        return self.container

    def on_session(self, session):
        print("session connected!")
        self.session = session
        self.dispatch('on_connect')

    def on_connect(self, *args):
        pass
MyApp().run()

my.kv:

#:kivy 1.10.1
#:import CardTransition kivy.uix.screenmanager.CardTransition
#:set menucolor1 0, 0.603, 0.784, 1
#:set menucolor2 0, 0.203, 0.384, 1
#:set backgroundcolor 0.95, 0.95, 0.95, 1

<MenuButton@Button>:
    size_hint_y: None
    height: 50
    markup: True

<MenuSpacer@Label>:
    size_hint_y: None
    height: 3
    canvas.before:
        Color:
            rgba: menucolor1
        Rectangle:
            pos: self.pos
            size: self.size
<MainPage>:
    canvas.before:
        Color:
            rgba: backgroundcolor
        Rectangle:
            pos: self.pos
            size: self.size
    name: 'main_screen'
    Label:
        color: menucolor1
        text: "Hi I'm Main Screen"

<AboutPage>:
    canvas:
        Color:
            rgba: backgroundcolor
        Rectangle:
            pos: self.pos
            size: self.size
    name: 'about_screen'
    Label:
        color: menucolor1
        text: "Hi I'm About Screen"


<MainContainer>:
    manager: manager
    canvas:
        Color:
            rgba: backgroundcolor
        Rectangle:
            pos: self.pos
            size: self.size
    BoxLayout:
        size: root.size
        orientation: 'horizontal'
        BoxLayout:
            orientation: 'vertical'
            width: 150
            size_hint_x: None
            MenuButton:
                text: '[b]Main[/b]'
                on_press: root.switch_to('main_screen')
            MenuSpacer:
            MenuButton:
                text: "[b]About[/b]"
                on_press: root.switch_to('about_screen')
            MenuSpacer:
            MenuButton:
                text: "[b]DummyConnect[/b]"
                on_press: root.dummyconnect()
            Widget:
                canvas.before:
                    Color:
                        rgba: menucolor1
                    Rectangle:
                        pos: self.pos
                        size: self.size
        ScreenManager:
            id:manager
            transition: CardTransition(direction='right',mode='push',duration=0.2)
            MainPage:
            AboutPage:

MainContainer.py

from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty
from kivy.app import App

class MainContainer(Widget):

    manager = ObjectProperty(None)

    def __init__(self, **kwargs):
        self.register_event_type('on_connect')
        super(MainContainer,self).__init__(**kwargs)
        print(self.children)

    def switch_to(self,name):
        self.manager.current = name

    def on_connect(self, *args):
        print("hello there")

    def dummyconnect(self):
        App.get_running_app().on_session("dummysession")

MainPage.py

from kivy.uix.screenmanager import Screen

class MainPage(Screen):
    pass

AboutPage.py

from kivy.uix.screenmanager import Screen

class AboutPage(Screen):
    pass

So basically I'd like the on_connect event that is dispatched in MyApp to propagate to the MainPage and AboutPages, currently only the MainContainer has on_connect defined, but even that does not fire.

I guess I could do it by propgating it myself to all the childrens at each widget, but I would need to implement this to all the boxlayouts,screenmanagers and screens as well? I'd think there is a better way to do it but I did not find one yet.

Any help is appreciated!

Upvotes: 1

Views: 2378

Answers (1)

ikolim
ikolim

Reputation: 16031

Solution

To propagate the on_connect to all the screens i.e. MainPage, and AboutPage. Please refer to the example for details.

  1. Added ids for MainPage and AboutPage
  2. Bind callbacks to them. When the event is dispatched, your callbacks will be called with the parameters relevant to that specific event.
  3. return True, indicating that we have consumed the touch and don’t want it to propagate any further.
  4. Removed codes in App (main.py) and added some in MainContainer.py

Note

I have commented off return True to show the event propagation. Try uncomment return True to show propagation stopped.

Snippets

MainContainer

def __init__(self, **kwargs):
    super(MainContainer, self).__init__(**kwargs)
    self.register_event_type('on_connect')
    print(self.children)
    self.bind(on_connect=self.ids.main_page.on_connect)
    self.bind(on_connect=self.ids.about_page.on_connect)
...
def on_connect(self, *args):
    print("\nMainContainer.on_connect()")
    print("hello there")
    # return True   # indicating that we have consumed the touch and don’t want it to propagate any further.

Example

main.py

from kivy.app import App
from kivy.config import Config
from MainPage import MainPage
from AboutPage import AboutPage
from MainContainer import MainContainer


class MyApp(App):

    def build(self):
        return MainContainer()


if __name__ == "__main__":
    MyApp().run()

my.kv

#:kivy 1.10.1
#:import CardTransition kivy.uix.screenmanager.CardTransition
#:set menucolor1 0, 0.603, 0.784, 1
#:set menucolor2 0, 0.203, 0.384, 1
#:set backgroundcolor 0.95, 0.95, 0.95, 1

<MenuButton@Button>:
    size_hint_y: None
    height: 50
    markup: True

<MenuSpacer@Label>:
    size_hint_y: None
    height: 3
    canvas.before:
        Color:
            rgba: menucolor1
        Rectangle:
            pos: self.pos
            size: self.size
<MainPage>:
    canvas.before:
        Color:
            rgba: backgroundcolor
        Rectangle:
            pos: self.pos
            size: self.size
    name: 'main_screen'
    Label:
        color: menucolor1
        text: "Hi I'm Main Screen"

<AboutPage>:
    canvas:
        Color:
            rgba: backgroundcolor
        Rectangle:
            pos: self.pos
            size: self.size
    name: 'about_screen'
    Label:
        color: menucolor1
        text: "Hi I'm About Screen"


<MainContainer>:
    manager: manager
    canvas:
        Color:
            rgba: backgroundcolor
        Rectangle:
            pos: self.pos
            size: self.size
    BoxLayout:
        size: root.size
        orientation: 'horizontal'
        BoxLayout:
            orientation: 'vertical'
            width: 150
            size_hint_x: None
            MenuButton:
                text: '[b]Main[/b]'
                on_press: root.switch_to('main_screen')
            MenuSpacer:
            MenuButton:
                text: "[b]About[/b]"
                on_press: root.switch_to('about_screen')
            MenuSpacer:
            MenuButton:
                text: "[b]DummyConnect[/b]"
                on_press:
                    root.on_session()
            Widget:
                canvas.before:
                    Color:
                        rgba: menucolor1
                    Rectangle:
                        pos: self.pos
                        size: self.size
        ScreenManager:
            id: manager
            transition: CardTransition(direction='right',mode='push',duration=0.2)

            MainPage:
                id: main_page

            AboutPage:
                id: about_page

MainContainer.py

from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty


class MainContainer(Widget):

    manager = ObjectProperty(None)

    def __init__(self, **kwargs):
        super(MainContainer, self).__init__(**kwargs)
        self.register_event_type('on_connect')
        print(self.children)
        self.bind(on_connect=self.ids.main_page.on_connect)
        self.bind(on_connect=self.ids.about_page.on_connect)

    def switch_to(self, name):
        self.manager.current = name

    def on_connect(self, *args):
        print("\nMainContainer.on_connect()")
        print("hello there")
        # return True   # indicating that we have consumed the touch and don’t want it to propagate any further.

    def on_session(self):
        print("session connected!")
        self.dispatch('on_connect')

MainPage.py

from kivy.uix.screenmanager import Screen


class MainPage(Screen):

    def on_connect(self, *args):
        print("\nMainPage.on_connect()")
        # return True   # indicating that we have consumed the touch and don’t want it to propagate any further.

AboutPage

from kivy.uix.screenmanager import Screen


class AboutPage(Screen):

    def on_connect(self, *args):
        print("\nAboutPage.on_connect()")
        # return True   # indicating that we have consumed the touch and don’t want it to propagate any further.

Output

Img01

Upvotes: 3

Related Questions