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

Posted

技术标签:

【中文标题】Kivy 标签文本未更新 - 仅在方法的最后一次更改时更新【英文标题】:Kivy Label text not updating - only updates at the last change in the method 【发布时间】:2021-02-28 23:10:36 【问题描述】:

使用 Kivy 1.11.1 和 Python 3.7.6。

我有这个特殊的问题,我无法在任何地方找到答案。

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

您可以在下面找到有问题的方法的代码。

ma​​in.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

所以我想要实现的是:

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

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

谢谢!

【问题讨论】:

请发minimal reproducible example。 @JohnAnderson 我按照建议实现了该问题的独立版本。感谢您的帮助! 【参考方案1】:

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

解决方法是快速从display_hello_status() 方法返回,让kivy 反映您所做的更改。 background 实际上可以使用Thread 在后台运行。然后Thread 可以在主线程上安排另一个 GUI 更新。

通常,您应该避免在主线程上进行冗长的处理。由于事件(如按下按钮)而运行的任何内容都将在主线程上运行。因此,在另一个线程(或在某些情况下甚至是另一个进程)上进行该处理允许 kivy 使 GUI 保持最新和响应。除此之外,您对 GUI 所做的任何更改都应在主线程上完成。这就是Clock.schedule_once() 派上用场的地方。

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

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!"

【讨论】:

以上是关于Kivy 标签文本未更新 - 仅在方法的最后一次更改时更新的主要内容,如果未能解决你的问题,请参考以下文章

更新另一个类中的 kivy 标签文本

Kivy 标签未更新

使用屏幕管理器更新 kivy 标签

Kivy:标签文本在 for 循环期间不更新

Kivy ObjectProperty 更新标签文本

Python Kivy:self.ids 不更新标签文本