Как запланировать действие с помощью метода 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 секунды, как здесь:

Пример метода after() в Tkinter

Причина в том, что функция 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

Похожие посты
Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *