Вызов функции по истечении периода времени, прошедшего с момента последнего нажатия клавиши в tkinter

#python #tkinter #keypress #tkinter-entry

#python #tkinter #нажатие клавиши #tkinter-запись

Вопрос:

У меня есть запись в приложении tkinter на Python. Если пользователь изменяет содержимое записи, программа реагирует с помощью onValidate функции.

Теперь я хотел бы выделить весь текст в поле ввода, если пользователь некоторое время не нажимал никаких клавиш.

Ниже я попытался select_all_text после 1000 мс использовать after функцию, но, похоже, это не сработало.

 import tkinter as tk  

class MyEntry(tk.Frame):

    def __init__(self, parent):
        tk.Frame.__init__(self, parent)

        self.vcmd = (self.register(self.onValidate),
                        '%d', '%i', '%P', '%s', '%S', '%v', '%V', '%W')

        self.entry = tk.Entry(self, validate="key", validatecommand=self.vcmd)
        self.entry.pack(side="top", fill="x")

        self.select_all_text()

    def select_all_text(self):
        self.entry.focus()
        self.entry.select_range(0,'end')

    def onValidate(self, d, i, P, s, S, v, V, W):
        self.root.after(1000, self.select_all_text)

        return True

if __name__ == "__main__":
    root = tk.Tk()
    MyEntry(root).pack(fill="both", expand=True)

    root.mainloop()
  

Также таймер должен сбрасываться каждый раз при нажатии новой клавиши, так что select_all_text вызывается только по истечении достаточно длительного времени с момента последнего нажатия клавиши. В приведенном ниже примере select_all_text вызывается после каждого нажатия клавиши, что нежелательно.

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

Как вызвать функцию, если с момента последнего нажатия клавиши в tkinter прошло достаточно много времени?

Ответ №1:

Когда вы сохраняете ссылку на идентификатор, который after возвращается, вы можете отменить его с помощью after_cancel . С помощью этого вы можете отменить запланированную функцию и перепланировать ее при каждом нажатии клавиши:

 class MyEntry(tk.Frame):

    def __init__(self, parent):
        tk.Frame.__init__(self, parent)
        self.parent = parent
        self.after_id = None

        ...

    def onValidate(self, d, i, P, s, S, v, V, W):
        if self.after_id:
            self.parent.after_cancel(self.after_id)
        self.after_id = self.parent.after(1000, self.select_all_text)

        return True
  

Обратите внимание, что я также сохранил корневое окно, которое вы передаете в качестве аргумента MyEntry в self.parent , и использовал его для вызова after дальше.


P.S. На самом деле вам не нужна validatecommand команда Entry для этого (но вы могли бы, если вы уже используете validatecommand в любом случае, конечно). Вы можете привязать функцию к каждому нажатию клавиши:

 class MyEntry(tk.Frame):

    def __init__(self, parent):
        tk.Frame.__init__(self, parent)
        self.parent = parent
        self.after_id = None

        self.entry = tk.Entry(self)
        self.entry.bind('<Key>', self.entry_keypress)

        ...

    def entry_keypress(self, e):
        print(self.after_id)
        if self.after_id:
            self.parent.after_cancel(self.after_id)
        self.after_id = self.parent.after(1000, self.select_all_text)