#python #kivy
#python #kivy
Вопрос:
Я пытаюсь работать с событиями часов, т.Е. С schedule_once и т. Д.,
В моем тестовом коде я пытаюсь обновить текст метки с помощью schedule_once . По какой-то неизвестной причине текст метки не обновляется. но выполняется цикл for.
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty
from kivy.clock import Clock
from functools import partial
from time import sleep
kv = '''
Some:
target: target
BoxLayout:
orientation: 'vertical'
size: root.width, root.height
Label:
id: target
text: 'Text will be changed'
font_size: 32
size_hint: 1, .5
Button:
text: 'Change'
on_press: root.click()
'''
class Some(Widget):
target = ObjectProperty(None)
def click(self):
for i in range(10):
sleep(1)
print(i)
Clock.schedule_once(lambda dt: partial(self.update_text, i), 10-i)
def update_text(self, k):
self.target.text = str(k)
class TestingApp(App):
def build(self):
return Builder.load_string(kv)
if __name__ == "__main__":
TestingApp().run()
Пожалуйста, помогите мне и скажите, как это реализовать.
Ответ №1:
Интересная проблема с парой проблем.
- Самая простая проблема заключается в том, что вы вызываете
Clock.schedule_once()
сtimeout
«10 — i». Это приведет к тому, что все запланированные выполнения будут выполняться примерно в одно и то же время. Это то, что вы намеревались? - Ваш
click()
метод выполняется в основном потоке, таким образом удерживая этот поток на времяfor
цикла в этом методе. Это предотвращает любые обновления графического интерфейса до тех пор, покаclick()
метод не вернется. - Когда вы используете a
lambda
в цикле и пытаетесь использовать переменную цикла (в вашем случаеi
) вlambda
,i
переменная не вычисляется до техlambda
пор, пока не будет вычислено, что оценка выполняется после завершения цикла, поэтомуi
in yourlambda
всегда будет вычисляться9
. - Когда вы используете метод в a
lambda
, вы должны включить()
после имени метода. Странно, потому что везде вы бы просто использовали имя метода.
Итак, вы можете изменить свой Clock.schedule_once()
вызов на:
Clock.schedule_once(lambda dt, j=i: partial(self.update_text, j)(), 10 i)
В приведенной выше строке j=i
создается еще один аргумент для lambda
, который вычисляется при lambda
создании. Также ()
добавлено в конец partial
, как указано выше.
Однако этот код все еще удерживает основной поток в течение 10 секунд. Если вы просто хотите Label
обновлять каждую секунду, вам не нужно использовать sleep
в этом цикле, просто укажите timeout
аргумент Clock.schedule_once()
, который планирует изменение по желанию. Вот модифицированная версия вашего click()
метода, которая объединяет все вышеперечисленное:
def click(self):
for i in range(10):
Clock.schedule_once(lambda dt, j=i: partial(self.update_text, j)(), i)
Использование lambda
и partial
вместе может привести к путанице. Вот другая версия вашего кода, которая использует Clock.schedule_interval()
вместо этого. Для меня этому коду легче следовать:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty, StringProperty
from kivy.clock import Clock
kv = '''
Some:
target: target
BoxLayout:
orientation: 'vertical'
size: root.width, root.height
Label:
id: target
text: root.txt
font_size: 32
size_hint: 1, .5
Button:
text: 'Change'
on_press: root.click()
'''
class Some(Widget):
target = ObjectProperty(None)
txt = StringProperty('Text will be changed')
def click(self):
self.current_update = 0
self.stop_update = 9
self.clock_event = Clock.schedule_interval(self.update_text, 1)
def update_text(self, dt):
self.txt = str(self.current_update)
self.current_update = 1
if self.current_update > self.stop_update:
self.clock_event.cancel()
class TestingApp(App):
def build(self):
return Builder.load_string(kv)
if __name__ == "__main__":
TestingApp().run()