Python Kivy:正确启动更新 GUI 元素的后台进程

Posted

技术标签:

【中文标题】Python Kivy:正确启动更新 GUI 元素的后台进程【英文标题】:Python Kivy: Properly start a background process that updates GUI elements 【发布时间】:2014-12-05 19:25:25 【问题描述】:

我有一个 Python 脚本,它对用户文件执行一些密集处理,并且可能需要一些时间。我使用 Kivy 为其构建了一个用户界面,允许用户选择文件、处理模式并在处理过程中向他们显示一些消息。

我的问题是当主 Kivy 循环通过调用底层用户界面时,窗口冻结。

据我了解,解决此问题的正确方法是创建一个单独的进程,将脚本卸载到该进程并将更新发送到用户界面。

但是,我找不到如何执行此操作的示例或任何关于如何将消息从单独线程发送回应用程序的规范。

有人可以举例说明如何正确执行此操作或将我指向与该主题相关的文档吗?

更新:

为了保持程序的可维护性,我想避免从主线程调用处理器循环的元素,而是调用一个返回更新的 GUI 元素的长进程,例如进度条或文本域。看起来这些元素只能从主 kivy 线程修改。如何从外部访问它们?

【问题讨论】:

【参考方案1】:

使用here 所述的发布者/消费者模型。这是该链接中修改为使用单独线程的示例:

from kivy.app import App
from kivy.clock import Clock, _default_time as time  # ok, no better way to use the same clock as kivy, hmm
from kivy.lang import Builder
from kivy.factory import Factory
from kivy.uix.button import Button
from kivy.properties import ListProperty

from threading import Thread
from time import sleep

MAX_TIME = 1/60.

kv = '''
BoxLayout:
    ScrollView:
        GridLayout:
            cols: 1
            id: target
            size_hint: 1, None
            height: self.minimum_height

    MyButton:
        text: 'run'

<MyLabel@Label>:
    size_hint_y: None
    height: self.texture_size[1]
'''

class MyButton(Button):
    def on_press(self, *args):
        Thread(target=self.worker).start()

    def worker(self):
        sleep(5) # blocking operation
        App.get_running_app().consommables.append("done")

class PubConApp(App):
    consommables = ListProperty([])

    def build(self):
        Clock.schedule_interval(self.consume, 0)
        return Builder.load_string(kv)

    def consume(self, *args):
        while self.consommables and time() < (Clock.get_time() + MAX_TIME):
            item = self.consommables.pop(0)  # i want the first one
            label = Factory.MyLabel(text=item)
            self.root.ids.target.add_widget(label)

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

【讨论】:

这是一个很好的例子,我已经阅读了他们的文档。问题是我必须从主应用程序中分割对底层脚本的调用。这是我特别想避免的。我有一个观察者监视消息并将它们发送回 GUI,但它不添加元素 GUI,它只是修改元素的属性,例如进度条值或文本字段文本。在这种情况下,来自线程的信号似乎无法返回到主线程。我该如何规避这个问题? 您可以尝试使用kivy.clock.mainthread decorator 在主线程中安排回调的调用。一些例子:github.com/kivy/kivy/wiki/…【参考方案2】:

注意:虽然从另一个线程修改 kivy 属性名义上有效,但有各种迹象表明这不是线程安全操作。 (使用调试器并在后台线程中逐步执行 append 函数。)Altering a kivy property from another thread 声明您不应以这种方式修改属性。

【讨论】:

以上是关于Python Kivy:正确启动更新 GUI 元素的后台进程的主要内容,如果未能解决你的问题,请参考以下文章

Kivy Python 启动 GUI 后无法执行任何操作

从终端启动 kivy GUI 应用程序

Kivy,Python:更新标签 on_file_drop

单击后更新按钮的位置? (Tkinter Python GUI)

python中的Kivy弹出窗口,按钮上有多个on_release操作

尝试编译后,Python/Kivy 应用程序不会启动