2016-05-25 3 views
2

In meinem Programm möchte ich meine GUI aktualisieren, während der Benutzer tippt. Aus Ressourcengründen würde ich es aber nur dann gerne machen, wenn der Benutzer für x Millisekunden nichts eingegeben hat. Hier ist ein Beispiel, das funktioniert, aber ich mag es nicht sehr, weil es zwei zusätzliche Funktionen benötigt und ein wenig wortreich ist.Warten Sie, bis der Benutzer die Eingabe beendet hat. Tkinter

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() 

Gibt es einen besseren Weg, es zu tun?

+3

Sie können möglicherweise die optionalen Validierungsfunktionen eines 'Entry'-Widgets verwenden, um die Zeit aufzuzeichnen, zu der der letzte Tastendruck eingegeben wurde, und dann diesen Wert in einer' after() 'Callback-Funktion prüfen. Es gibt Details zur 'Eingabe'-Widget-Validierung [hier] (http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/entry-validation.html). – martineau

Antwort

2

Die Lösung besteht darin, after zu verwenden, um die Ausführung der Funktion zu planen, nachdem der Benutzer die Eingabe beendet hat. Dann müssen Sie den Job nur bei jedem Klick neu starten.

Erstellen Sie zuerst eine Variable, um eine ID zu speichern, die den zukünftigen Funktionsaufruf darstellt. Sie können self.counter auch wegwerfen, da es nicht benötigt wird.

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

Als nächstes entfernen Lambda in Ihrer Bindung. Es ist sinnlos und macht den Code komplexer als es sein muss:

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

Schließlich ändern Sie Ihre handle_wait Funktion wie folgt aussehen:

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) 

Hier ist ein komplettes Beispiel basierend auf Ihren Code:

+0

Super! Vielen Dank. Die Lösung mit der Cancel-Funktion ist sehr elegant und ich hatte keine Ahnung, dass sie existiert. Sehr hilfreich – Jannick

+0

Hallo, vielen Dank für diesen nützlichen Code Bryan. Ich versuche dasselbe ohne eine Klasse zu machen. Sie haben 'after (1000, self.change_color)' an '.self' gebunden Wenn ich versuche, es an das Eingabe-Widget oder root zu binden, friert es 1000 für jeden' ​​ '... ist es notwendig, dies an einen Frame zu binden Widget wie Sie haben oder was ist der beste Weg, um dies zu lösen? – Rhys

+0

@Rhys: Ich verstehe Ihre Terminologie nicht ganz. Der 'after'-Befehl muss einen Verweis auf eine Funktion erhalten, die aufgerufen werden soll. Es spielt keine Rolle, ob diese Funktion Teil einer Klasse ist oder nicht. Sie binden es niemals an ein Eingabe-Widget oder ein anderes Widget - Sie binden es an eine Funktion. –