Подождите, пока пользователь не перестанет печатать в Tkinter


В моей программе я хотел бы обновить свой графический интерфейс, пока пользователь набирает текст. По соображениям ресурса я бы, однако, хотел сделать это только в том случае, если пользователь не набрал что-то за x миллисекунд. Вот пример, который работает, но мне он не очень нравится, потому что он требует двух дополнительных функций и немного многословен.

import tkinter as tk
import random

COLORS =["red", "orange", "yellow", "green", "blue", "violet"]

class Application(tk.Frame): 

    def __init__(self,master):
        self.counter = 0
        self.master = master
        tk.Frame.__init__(self)
        self.pack()

        self.entry = tk.Entry(self)
        self.entry.pack()
        self.entry.bind('<Key>',lambda event: self.handle_wait(event))


    def handle_wait(self,event):
        self.counter += 1
        counter = self.counter
        self.after(1000,lambda: self.handle_wait2(counter) )


    def handle_wait2(self,counter):
        if self.counter == counter:
            self.change_color()

    def change_color(self):
        random_color = random.choice(COLORS)
        self.entry.config(background=random_color)

root = tk.Tk()
app = Application(root)
app.mainloop()
Есть ли лучший способ сделать это?
1 2

1 ответ:

Решение состоит в том, чтобы использовать after для планирования запуска функции после того, как пользователь перестанет печатать. Затем все, что вам нужно сделать, это перезапустить работу каждый раз, когда вы получаете щелчок кнопки.

Сначала создайте переменную для хранения идентификатора, представляющего будущий вызов функции. Вы также можете выбросить self.counter, поскольку это не нужно.

def __init__(...):
    ...
    self._after_id = None
    ...

Затем удалите лямбду из привязки. Это бессмысленно и делает код более сложным, чем он должен быть:

self.entry.bind('<Key>',self.handle_wait)

Наконец, измените свою функцию handle_wait на выглядит так:

def handle_wait(self, event):
    # cancel the old job
    if self._after_id is not None:
        self.after_cancel(self._after_id)

    # create a new job
    self.after(1000, self.change_color)

Вот полный пример, основанный на вашем коде:

import tkinter as tk
import random

COLORS =["red", "orange", "yellow", "green", "blue", "violet"]

class Application(tk.Frame): 

    def __init__(self,master):
        self.master = master
        tk.Frame.__init__(self)
        self.pack()

        self._after_id = None
        self.entry = tk.Entry(self)
        self.entry.pack()
        self.entry.bind('<Key>',self.handle_wait)

    def handle_wait(self,event):
        # cancel the old job
        if self._after_id is not None:
            self.after_cancel(self._after_id)

        # create a new job
        self._after_id = self.after(1000, self.change_color)

    def change_color(self):
        random_color = random.choice(COLORS)
        self.entry.config(background=random_color)

root = tk.Tk()
app = Application(root)
app.mainloop()