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
所以我要实现的是:
- 通过文本和颜色变化通知用户某些进程正在进行。
- 在后台做一些处理,这需要一些时间(由
time.sleep(2)
模拟。
- 显示程序成功完成进程的信息。
问题是:我该怎么做?如何强制 Widget(在本例中为 Label)在方法中间进行更新?
谢谢!
问题是只要你在主线程上有一个方法运行ning,就像你的display_hello_status()
一样,kivy就无法更新GUI。因此,当 display_hello_status()
方法 运行s 时,对 username
和 status
小部件进行了更改,但是 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!"
使用 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
所以我要实现的是:
- 通过文本和颜色变化通知用户某些进程正在进行。
- 在后台做一些处理,这需要一些时间(由
time.sleep(2)
模拟。 - 显示程序成功完成进程的信息。
问题是:我该怎么做?如何强制 Widget(在本例中为 Label)在方法中间进行更新?
谢谢!
问题是只要你在主线程上有一个方法运行ning,就像你的display_hello_status()
一样,kivy就无法更新GUI。因此,当 display_hello_status()
方法 运行s 时,对 username
和 status
小部件进行了更改,但是 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!"