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

时间:2014-10-10 15:11:27

标签: python event-handling kivy

我有一个Python脚本,可以对用户的文件进行一些密集处理,并且可能需要一些时间。我使用Kivy为它构建了一个用户界面,允许用户选择文件,处理模式并在进程继续时显示一些消息。

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

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

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

有人可以举例说明如何正确地做到这一点或者指出我有关该主题的文件吗?

更新

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

2 个答案:

答案 0 :(得分:3)

按照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()

答案 1 :(得分:2)

警告:从名义上工作的另一个线程修改kivy属性时,有迹象表明这不是一个线程安全的操作。 (使用调试器并在后台线程中逐步执行append函数。)Altering a kivy property from another thread表示不应以这种方式修改属性。