如何 运行 从属模式下的 Kivy EventLoop?

How to run a Kivy EventLoop in slave mode?

相关问题here

我发现 runTouchApp 函数的 slave 属性 可以阻止 Kivy 的事件循环 运行ning 并强制从其他地方更新它。
这是 app.py 的一部分,其中使用了 属性:

# we are in a slave mode, don't do dispatching.
if slave:
    return

try:
    if EventLoop.window is None:
        _run_mainloop()
    else:
        EventLoop.window.mainloop()
finally:
    stopTouchApp()

在这里,如果应用程序未 运行 从属模式,我们有两种选择如何 运行 主循环。
第一个,_run_mainloop() 函数工作起来非常简单——它简单地调用 EventLoop.run(),后者又无限地调用 EventLoop.idle().
这可能会让我们相信要保持 GUI 运行ning,我们只需要调用 idle.

但是还有第二个选项,它调用 kivy.core.window.WindowSDL 的方法 mainloop
该方法通过调用另一个方法 _mainloop 来工作,这就是它变得有趣的地方。所述方法的定义是huge,它处理各种事件。

好吧,我 运行 我的应用处于从属模式:

class TestApp(App):
    def start_event(self):
        pass

    def build(self):
        return Button(text = "hello")

    def run(self):
        # This definition is copied from the superclass 
        # except for the start_event call and slave set to True

        if not self.built:
            self.load_config()
            self.load_kv(filename=self.kv_file)
            root = self.build()
            if root:
                self.root = root

        if self.root:
            Window.add_widget(self.root)

        window = EventLoop.window
        if window:
            self._app_window = window
            window.set_title(self.get_application_name())
            icon = self.get_application_icon()
            if icon:
                window.set_icon(icon)
            self._install_settings_keys(window)


        self.dispatch('on_start')
        runTouchApp(slave = True)
        self.start_event()  # Here we start updating
        self.stop()

现在,如果我把它放在 start_event 方法中(按预期):

def start_event(self):
    while True:
        EventLoop.idle()

你猜怎么着,应用程序不响应触摸事件并冻结。
所以我试着调用 Window 的主循环:

def start_event(self):
    EventLoop.window.mainloop()

然后突然一切又开始正常工作了。但这里的问题是这样的调用永远阻塞,因为它是一个无限循环,所以没有像 EventLoop.idle

这样的一次性更新调用

如何使用此类一次性调用来保持应用程序 运行ning?

好吧,这是 Python 所以假设你想坚持使用 WindowSDL 供应商,你总是可以猴子修补这个 mainloop 函数所以它不会是无限的:

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
from kivy.base import EventLoop

Builder.load_string('''
<MyWidget>:
    Button:
        text: 'test'
        on_press: print('doing my job')
''')

# https://github.com/kivy/kivy/blob/master/kivy/core/window/window_sdl2.py#L630
def mainloop(self):
    # replaced while with if
    if not EventLoop.quit and EventLoop.status == 'started':
        try:
            self._mainloop()
        except EventLoop.BaseException as inst:
            # use exception manager first
            r = EventLoop.ExceptionManager.handle_exception(inst)
            if r == EventLoop.ExceptionManager.RAISE:
                EventLoop.stopTouchApp()
                raise
            else:
                pass


class MyWidget(BoxLayout):
    pass


if __name__ == '__main__':
    from kivy.base import runTouchApp
    runTouchApp(MyWidget(), slave=True)
    # monkey patch
    EventLoop.window.mainloop = mainloop
    while True:
        EventLoop.window.mainloop(EventLoop.window)
        print('do the other stuff')
        if EventLoop.quit:
            break

虽然它真的很 hacky,因此我不建议 运行 在生产代码中使用类似的东西。