Как запланировать действие с помощью метода after() в Tkinter
Метод after() в Tkinter используется для планирования действия после истечения времени ожидания.
Знакомство с методом after() в Tkinter
Все виджеты Tkinter имеют метод after() со следующим синтаксисом:
widget.after(delay, callback=None)
Метод after() вызывает функцию обратного вызова один раз после задержки в миллисекундах (мс) в основном цикле Tkinter.
Если вы не предоставляете обратный вызов, метод after() ведет себя как функция time.sleep(). Однако метод after() использует в качестве единицы измерения миллисекунду вместо секунды.
Пример.
Давайте посмотрим следующую программу:
import tkinter as tk from tkinter import ttk import time class App(tk.Tk): def __init__(self): super().__init__() self.title('Tkinter after() Demo') self.geometry('300x100') self.style = ttk.Style(self) self.button = ttk.Button(self, text='Wait 3 seconds') self.button['command'] = self.start self.button.pack(expand=True, ipadx=10, ipady=5) def start(self): self.change_button_color('red') time.sleep(3) self.change_button_color('black') def change_button_color(self, color): self.style.configure('TButton', foreground=color) if __name__ == "__main__": app = App() app.mainloop()
Программа состоит из кнопки. При нажатии на кнопку:
- Сначала цвет кнопки становится красным.
- Затем программа засыпает на 3 секунды.
- Наконец, цвет кнопки становится черным.
Однако, когда вы запустите программу и нажмете кнопку, вы заметите, что цвет кнопки вообще не изменится. Кроме того, окно застынет на 3 секунды, как здесь:
Причина в том, что функция sleep() приостановила выполнение основного потока. Поэтому Tkinter не смог обновить GUI.
Чтобы исправить эту проблему, можно использовать метод after() для планирования действия, которое обновляет цвет кнопки вместо приостановки выполнения основного потока. Например:
import tkinter as tk from tkinter import ttk import time class App(tk.Tk): def __init__(self): super().__init__() self.title('Tkinter after() Demo') self.geometry('300x100') self.style = ttk.Style(self) self.button = ttk.Button(self, text='Wait 3 seconds') self.button['command'] = self.start self.button.pack(expand=True, ipadx=10, ipady=5) def start(self): self.change_button_color('red') self.after(3000,lambda: self.change_button_color('black')) def change_button_color(self, color): self.style.configure('TButton', foreground=color) print(color) if __name__ == "__main__": app = App() app.mainloop()
Вывод:
Зачем использовать метод after() в Tkinter
Программа Python может иметь один или несколько потоков. Когда вы запускаете приложение Tkinter, оно выполняется в главном потоке.
Основной цикл Tkinter должен начинаться из основного потока. Он отвечает за обработку событий и обновление GUI. Если запустить длительную задачу в основном потоке, графический интерфейс зависнет и не будет реагировать на пользовательские события.
Чтобы предотвратить блокировку основного потока длительной задачей, можно запланировать действие, которое не будет выполнено ранее указанного времени, с помощью метода after(). Tkinter выполнит обратный вызов в основном потоке, когда основной поток не занят.
Практический пример
Следующая программа отображает цифровые часы. Она использует метод after() для обновления текущего времени каждую секунду:
import tkinter as tk from tkinter import ttk import time class DigitalClock(tk.Tk): def __init__(self): super().__init__() # configure the root window self.title('Digital Clock') self.resizable(0, 0) self.geometry('250x80') self['bg'] = 'black' # change the background color to black self.style = ttk.Style(self) self.style.configure( 'TLabel', background='black', foreground='red') # label self.label = ttk.Label( self, text=self.time_string(), font=('Digital-7', 40)) self.label.pack(expand=True) # schedule an update every 1 second self.label.after(1000, self.update) def time_string(self): return time.strftime('%H:%M:%S') def update(self): """ update the label every 1 second """ self.label.configure(text=self.time_string()) # schedule another timer self.label.after(1000, self.update) if __name__ == "__main__": clock = DigitalClock() clock.mainloop()
Выход:
Как это работает.
Следующий метод возвращает текущее время в строковом формате:
def time_string(self): return time.strftime('%H:%M:%S')
Метод __init__() использует метод after() для планирования действия, которое обновляет текущее время в метке каждую секунду:
self.label.after(1000, self.update)
В методе update() обновите текущее время в метке и запланируйте еще одно обновление через одну секунду:
def update(self): """ update the label every 1 second """ self.label.configure(text=self.time_string()) # schedule another timer self.label.after(1000, self.update)
Обратите внимание, что эта программа использует шрифт Digital 7 с сайта 1001fonts.com