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

Posted

技术标签:

【中文标题】Kivy:标签文本在 for 循环期间不更新【英文标题】:Kivy: Label text does not update during for-loop 【发布时间】:2017-07-20 13:28:35 【问题描述】:

我在 for 循环期间尝试更新标签文本时遇到问题。有类似的条目(例如:Update properties of a kivy widget while running code),但它们似乎并不完全适合我的问题(或者我错过了重点……)。 我运行以下代码:

*.py:

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty
#from time import sleep

class MyBox(BoxLayout):
    tobeupd = StringProperty()

    def __init__(self,*args,**kwargs):
        super(MyBox,self).__init__(*args,**kwargs)
        self.tobeupd = '#'

    def upd_ltxt(self):
        for i in range(1,10):
            self.tobeupd = str(i)
            print(self.tobeupd)
            input('Write something: ')  # new line, see edit below
            #sleep(0.5) 

class updApp(App):
    def build(self):
        return MyBox()

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

*.kv

<MyBox>:
    orientation: 'horizontal'
    cols: 2
    Label:
        text: root.tobeupd
    Button:
        text: 'Start Update'
        on_release: root.upd_ltxt()

虽然“打印”语句会定期更新 shell,但标签文本仅在 for 循环结束时更新。 谁能向我解释为什么 Kivy 会这样工作以及我如何克服这个问题?

编辑:根据 PM2Ring 和 Gugas,我更改了代码以避免睡眠功能。如果我要求用户在循环继续之前输入一些内容,问题仍然存在。这些值在 shell 中更新,但不在标签上。

【问题讨论】:

我不知道 Kivy,但在 GUI 代码中调用 time.sleep 通常是个坏主意,因为它会阻塞 一切,因此 GUI 引擎无法获得有机会更新布局。如果您需要延迟,则必须使用 GUI 框架为此目的提供的功能/方法。 @PM2Ring 其实是对的,系统很可能会更改标签,并且在被休眠后它不会更新,尝试不使用休眠语句并检查问题是否仍然存在。跨度> @PM2ring, Gugas:我编辑了代码,但问题仍然存在。有什么想法吗? input 函数在等待用户输入时阻塞,所以它与调用sleep 的效果相同,因此 Kivy 仍然没有机会更新布局。正如我之前所说,我不了解 Kivy,但我认为它具有强制更新布局的功能。 This answer 可能是相关的。 你永远不会将input 的结果分配给任何东西,我可以告诉你。 【参考方案1】:

您可以为此使用threading。 当您在 kivy 中执行循环或等待输入时,主线程正在等待,并且不会在应用程序上更新任何内容。 threading 会阻止这种情况。 使用threading 在主线程之外创建另一个线程。 示例:

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty
from kivy.lang import Builder
import threading

Builder.load_string('''

<MyBox>:
    orientation: 'horizontal'
    cols: 2
    Label:
        text: root.tobeupd
    Button:
        text: 'Start Update'
        on_release: root.upd_ltxt()

''')

class MyBox(BoxLayout):
    tobeupd = StringProperty()

    def __init__(self,*args,**kwargs):
        super(MyBox,self).__init__(*args,**kwargs)
        self.tobeupd = '#'

    def upd_ltxt(self):
        threading.Thread(target=self.update_label).start()

    def update_label(self):
        for i in range(1,10):
            print(self.tobeupd)
            self.tobeupd = str(i)
            input('Write something: ')  # new line, see edit below



class updApp(App):
    def build(self):
        return MyBox()

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

现在值得一提的是,即使第一个尚未完成,您也可以继续按下按钮并启动线程。这可能是不受欢迎的行为。 这可以通过在线程开始时禁用按钮并在结束时再次启用它来防止。

在kv中给按钮一个id:

Button:
    id: updatebutton
    text: 'Start Update'
    on_release: root.upd_ltxt()

在线程中这样做:

def update_label(self):

    self.ids.updatebutton.disabled = True

    for i in range(1,10):
        self.tobeupd = str(i)
        input('Write something: ')

    self.ids.updatebutton.disabled = False

【讨论】:

【参考方案2】:

您还可以使用 Kivys clock Class,它是一个事件调度程序。它将安排一个事件,这是一个函数。例如,更新您的标签文本。

from kivy.clock import Clock   

def to_be_called_back(self,dt):
    print("This function should be periodically executed")

def do_the_loop(self):
    Clock.schedule_interval(self.to_be_called(),0.5)

这里函数to_be_called() 将每0.5 秒调用一次。 dt 变量代表 deltatime,显然是 Clock 类需要的(没有它,我的代码会出现问题)

我仍然会将do_the_loop() 函数放入单独的线程中。但这就是 kivy 为它提供的。 If you want to know more about the clock Class head over here.

【讨论】:

【参考方案3】:

我相信你需要做的就是把

root.update()

每当您希望 GUI 更新时,因此在循环中,在文本更改之后。像魅力一样在 tkinter 中工作。您还可以通过将 root(或 main)更改为文本区域的名称来限制对特定文本区域的更新。

【讨论】:

这是Kivy 不是tkinter 他们没有相同的术语。

以上是关于Kivy:标签文本在 for 循环期间不更新的主要内容,如果未能解决你的问题,请参考以下文章

在 Kivy for Python 中按下按钮时更新标签的文本

在 Kivy for Python 中按下按钮时更新标签的文本

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

Kivy:如何在不关闭弹出窗口的情况下更新弹出标签文本

Kivy 更新动态标签文本

如何在 Kivy、Python 中更新标签的文本