运行 多个 Kivy 应用同时相互通信

Running multiple Kivy apps at same time that communicate with each other

我希望我的 Kivy 应用程序能够在可以相互通信的 Windows 机器上生成多个应用程序(即新的 windows)。

ScreenManager and Popup 选项不会削减它,因为它们存在于同一个 window 中。我需要能够在多个显示器上拖动新屏幕,因此需要多个 windows。

Kivy 文档明确指出 “Kivy 仅支持一个 window 每个应用程序:请不要尝试创建多个。"

google 搜索产生 this simple approach 从另一个应用程序中简单生成一个新应用程序,如下所示:

from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.label import Label


class ChildApp(App):
    def build(self):
        return Label(text='Child')


class MainApp(App):

    def build(self):
        b = Button(text='Launch Child App')
        b.bind(on_press=self.launchChild)
        return b

    def launchChild(self, button):
        ChildApp().run()

if __name__ == '__main__':
    MainApp().run()

然而,当我这样做时,它在同一个 window 中启动应用程序并崩溃,我的终端疯狂地吐出:

Original exception was:
Error in sys.exceptionhook:

如果我不使用 ChildApp().run() 我会得到相同的结果 multiprocessing.Process(target=ChildApp().run()).start()

使用 subprocess 库让我更接近我想要的东西:

# filename: test2.py

from kivy.app import App
from kivy.uix.label import Label


class ChildApp(App):
    def build(self):
        return Label(text='Child')

if __name__ == '__main__':
    ChildApp().run()

# filename: test.py

from kivy.app import App
from kivy.uix.button import Button

import subprocess


class MainApp(App):

    def build(self):
        b = Button(text='Launch Child App')
        b.bind(on_press=self.launchChild)
        return b

    def launchChild(self, button):
        subprocess.call('ipython test2.py', shell=True)

if __name__ == '__main__':
    MainApp().run()

这会毫无错误地生成子 window,但是现在主 window 已锁定(白色 canvas),如果我关闭子 window,它只会重新开放。

他们需要能够在彼此之间传递数据。关于如何在 Windows 中正确执行此操作的任何想法? post 似乎表明这是可能的,但我不确定从哪里开始。

我不确定为什么它不适用于多处理(我从未尝试过),但它至少应该适用于 subprocess。您的主 window 被锁定的原因是因为 subprocess.call 在等待子进程完成和 return 结果时阻塞调用它的线程。

您想改用 subprocess.Popen,它不会阻塞。

bj0 关于子流程的回答是正确的。

更好的是,我想出了如何通过多处理来做到这一点,这允许应用程序之间更好的通信和信息传递。它以前没有用,因为我在应该 multiprocessing.Process(target=ChildApp().run).start() 的时候做了 multiprocessing.Process(target=ChildApp().run()).start()。以下作品

# filename: test.py

from kivy.app import App
from kivy.uix.button import Button

from test2 import ChildApp

import multiprocessing


class MainApp(App):

    def build(self):
        b = Button(text='Launch Child App')
        b.bind(on_press=self.launchChild)
        return b

    def launchChild(self, button):
        app = ChildApp()
        p = multiprocessing.Process(target=app.run)
        p.start()

if __name__ == '__main__':
    MainApp().run()

# filename: test2.py

from kivy.app import App
from kivy.uix.label import Label


class ChildApp(App):
    def build(self):
        return Label(text='Child')

if __name__ == '__main__':
    ChildApp().run()

我试过 baconwichsand 的代码,可以用 Python 3.6 和 Windows 10 确认它不起作用。显然只有顶级对象 classes 可以被 pickle,并且由于两个应用程序都继承自应用程序 class python 会引发错误。但是,可以对仅执行 ChildApp().运行() 命令的顶级定义进行 pickle 和工作。这是我的工作代码。

import multiprocessing
from kivy.app import App
from kivy.uix.label import Label

class MainApp(App):
    def build(self):
        return Label(text='Main App Window')

class OtherApp(App):
    def build(self):
        return Label(text='Other App Window')

def open_parent():
    MainApp().run()

def open_child():
    OtherApp().run()

if __name__ == '__main__':
    a = multiprocessing.Process(target=open_parent)
    b = multiprocessing.Process(target=open_child)
    a.start()
    b.start()

这是我正在使用的代码,包括为两者使用共享 .kv 文件的生成器 windows。

import multiprocessing
from kivy.lang import Builder
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.widget import Widget

class MainRoot(Widget):
    pass

class OtherRoot(Widget):
    pass

class MainApp(App):
    def build(self):
        Builder.load_file('B:\Python_Codes\Testing Grounds\shared.kv')
        main = MainRoot()
        return main

class OtherApp(App):
    def build(self):
        Builder.load_file('B:\Python_Codes\Testing Grounds\shared.kv')
        other = OtherRoot()
        return other

def open_parent():
    MainApp().run()

def open_child():
    OtherApp().run()

if __name__ == '__main__':
    a = multiprocessing.Process(target=open_parent)
    b = multiprocessing.Process(target=open_child)
    a.start()
    b.start()