При программировании с использованием kivy, вы должны иметь в виду, что все события GUI происходят в главном потоке, и весь ваш код (если вы не принимаете меры предосторожности) также будет выполняться в главном потоке.Таким образом, ваш цикл for
выполняется в главном потоке, и если вы добавите sleep
в этот цикл, он также будет выполняться в основном потоке.Так что этот код делает основной поток занятым.В результате все ваши trigger_action
вызовы (и любые другие вызовы, которые изменяют GUI) должны ждать, пока ваш код перестанет удерживать основной поток, прежде чем они смогут повлиять на GUI.Таким образом, в конце цикла for
графический интерфейс получает шанс наверстать упущенное, и все ожидаемые эффекты кнопок графического интерфейса предварительно формируются и отображаются как одно длинное нажатие кнопки.В общем, вы хотите, чтобы любой метод, вызываемый событием графического интерфейса пользователя (например, нажатие кнопки), возвращался как можно быстрее или создавал новый поток для длительной обработки.
Итак, вот способ сделатьваш код работает:
from kivy.app import App
from kivy.clock import Clock
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
class TestApp(App):
def build(self):
layout = BoxLayout()
self.b1 = Button(text="Button 1", on_press=self.on_press_button_1)
self.b1.click_count = 0
self.max_clicks = None
self.clock_event = None
self.b2 = Button(text="Button 2", on_press=self.on_press_button_2)
layout.add_widget(self.b1)
layout.add_widget(self.b2)
return layout
def on_press_button_1(self, *args):
print("on press button 1")
if self.max_clicks is not None and self.clock_event is not None:
self.b1.click_count += 1
if self.b1.click_count >= self.max_clicks:
self.clock_event.cancel()
self.clock_event = None
self.max_clicks = None
def on_press_button_2(self, *args):
self.max_clicks = 5
self.b1.click_count = 0
self.clock_event = Clock.schedule_interval(lambda dt: self.b1.trigger_action(), 1)
if __name__ == "__main__":
TestApp().run()
При этом используется Clock.schedule_interval
, который планирует что-то для запуска в главном потоке, но немедленно возвращается, что позволяет графическому интерфейсу работать нормально.Clock.schedule_interval()
вызывает указанный метод с аргументом dt
(время с момента планирования события), но trigger_action()
не хочет такого аргумента, поэтому я использовал lambda
.См. документация .click_count
, max_clicks
и clock_event
используются только для управления отменой события.