Kivy 标签文本未更新 - 仅在方法的最后一次更改时更新

Kivy Label text not updating - only updates at the last change in the method

使用 Kivy 1.11.1 和 Python 3.7.6.

我遇到了这个奇怪的问题,找不到任何答案。

我想在按下按钮时调用的方法两次中更新标签的文本(此处:status标签)(在本例中, display_hello_status()。但似乎 Kivy 仅在 方法调用完成后 更新界面 - 导致仅呈现标签文本的最新更改。

下面是有问题方法的代码。

main.py:

import time

import kivy
from kivy.properties import ObjectProperty
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.app import App

kivy.require('1.11.1')


class WindowManager(ScreenManager):
    pass


class PrintHello(Screen):
    username = ObjectProperty(None)
    status = ObjectProperty(None)

    def display_hello_status(self):
        # Inform about process of generating hello text.
        self.status.text = "printing hello..."  # this text is never displayed.
        # Pretend something is happening in the background.
        time.sleep(2)
        self.username.text = f"Hello, {self.username.text}!"
        # Display information indicating successful printing.
        self.status.text = "printed!"


class MyApp(App):
    pass


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

my.kv:

WindowManager:
    PrintHello:

<PrintHello>:
    username: username_text_input
    status: status_label

    BoxLayout:
        orientation: "vertical"

        GridLayout:
            cols: 2
            padding: 8
            spacing: 8

            Label:
                text: "Your name:"
                size_hint_y: None
                height: 32
                bold: True

            TextInput:
                id: username_text_input
                size_hint_y: None
                height: 32
                multiline: False

        FloatLayout:

            Button:
                text: "Print"
                size_hint: 0.2, 0.2
                pos_hint: {"center_x": 0.6, "y": 0.4}
                on_release: root.display_hello_status()

            BoxLayout:
                orientation: "horizontal"
                size_hint: 0.2, 0.2
                pos_hint: {"center_x": 0.2, "y": 0.4}
                padding: 4

                Label:
                    text: "Status:"
                    bold: True

                Label:
                    id: status_label
                    text: "off"
                    background_color: 0, 0, 0, 0  # black
                    color: 1, 1, 1, 1  # white

所以我要实现的是:

  1. 通过文本和颜色变化通知用户某些进程正在进行。
  2. 在后台做一些处理,这需要一些时间(由time.sleep(2)模拟。
  3. 显示程序成功完成进程的信息。

问题是:我该怎么做?如何强制 Widget(在本例中为 Label)在方法中间进行更新?

谢谢!

问题是只要你在主线程上有一个方法运行ning,就像你的display_hello_status()一样,kivy就无法更新GUI。因此,当 display_hello_status() 方法 运行s 时,对 usernamestatus 小部件进行了更改,但是 kivy 无法显示这些更改,直到它有机会 运行 在主线程上。当 kivy 回到主线程时,第一个更改已经再次更改,所以只显示最后的更改。

修复是从 display_hello_status() 方法快速 return,允许 kivy 反映您所做的更改。 background 某些东西实际上可以 运行 在后台使用 Thread。然后 Thread 可以在主线程上安排另一个 GUI 更新。

一般来说,你应该避免在主线程上进行冗长的处理。任何 运行 作为事件(如按下按钮)的结果将在主线程上 运行。所以在另一个线程(或者在某些情况下甚至是另一个进程)上进行处理允许 kivy 保持 GUI 最新和响应。除此之外,您对 GUI 所做的任何更改都应该在主线程上完成。这就是 Clock.schedule_once() 派上用场的地方。

这是您的 PrintHello class 的修改版本,它遵循了上述建议:

class PrintHello(Screen):
    username = ObjectProperty(None)
    status = ObjectProperty(None)

    def display_hello_status(self):
        # Inform about process of generating hello text.
        self.status.text = "printing hello..."  # this text is never displayed.
        # Pretend something is happening in the background. Actually make it happen on a background thread
        threading.Thread(target=self.do_somehing).start()

    def do_somehing(self):
        print('starting something')
        time.sleep(2)
        print('finished something')
        
        # schedule the GUI update back on the main thread
        Clock.schedule_once(self.something_finished)

    def something_finished(self, dt):
        self.username.text = f"Hello, {self.username.text}!"
        # Display information indicating successful printing.
        self.status.text = "printed!"