绝望的自定义​​事件传播

Kivy custom event propagation

我正在为联网设备制作 Python/Kivy GUI。由于设备使用 WAMP pub/sub 方法进行通信,所以有很多同步的事情在进行。问题是我不知道如何传播自定义事件,例如 on_connect、on_disconnect 等从 kivy 应用程序到适当的 kivy 小部件。

这是我现在所拥有的,为了更好的可读性,将其剥离到最低限度,还有虚拟连接按钮,用于模拟连接事件的发生。

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

所以基本上我希望在 MyApp 中调度的 on_connect 事件传播到 MainPage 和 AboutPages,目前只有 MainContainer 定义了 on_connect,但即使这样也不会触发。

我想我可以通过自己将它传播给每个小部件上的所有孩子来做到这一点,但是我还需要将它实现到所有的盒子布局、屏幕管理器和屏​​幕上吗?我认为有更好的方法可以做到这一点,但我还没有找到。

感谢任何帮助!

解决方案

将 on_connect 传播到所有屏幕,即 MainPage 和 AboutPage。具体请参考示例

  1. 添加了 MainPage 和 AboutPage 的 ID
  2. 为它们绑定回调。调度事件时,将使用与该特定事件相关的参数调用您的回调。
  3. return True,表明我们已经消耗了触摸并且不希望它进一步传播。
  4. 删除了 App (main.py) 中的代码,并在 MainContainer.py
  5. 中添加了一些代码

备注

我已注释掉 return True 以显示事件传播。尝试取消注释 return True 以显示传播已停止。

片段

主容器

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.

例子

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.

关于页面

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.

输出