2014-12-20 3 views
6

Ich versuche herauszufinden, wie man einen Bindebefehl in einem Dialogfenster unittest. Ich versuche dies mit tkinter event_generate. Es funktioniert nicht so, wie ich es erwarte. Für diese StackOverflow-Frage habe ich einen Code mit einem einzigen Anruf auf event_generate eingerichtet. Manchmal funktioniert diese Linie und manchmal ist es so, als ob die Linie gar nicht existiert.Tkinter's event_generate Befehl ignoriert

Die bind in der __init__ Methode des Dialog sieht wie folgt aus:

 self.bind('<BackSpace>', #Print "BackSpace event generated." 
      lambda event: print(event.keysym, 'event generated.')) 

Jede Aktion im Dialog wird auf seiner beenden Methode zurückrufen (Der Dialog basiert auf Frederik Lundhs Beispiel Dialog in ‚An Introduction to Tkinter ‘.)

def terminate(self, event=None): 
     print('terminate called') # Make sure we got here and the next line will be called 
     self.event_generate('<BackSpace>') 
     self.parent.focus_set() 
     self.destroy() 

Wenn der Dialog den Code jeder Benutzeraktion aufgerufen wird wird bis Aufruf terminate beenden. In jedem Fall "terminate called" und "BackSpace event generated". sind angezeigt. Dies beweist, dass der Aufruf von event_generate korrekt eingerichtet ist.

parent = tk.Tk() 
dialog = Dialog(parent) 
dialog.wait_window() 

Falls es relevant Ich sollte erwähnen, dass ich Lundhs Aufruf self.wait_window aus seinem Dialog des __init__ Methode an den Aufrufer bewegt. Während dies die saubere Verkapselung seines Dialogs unterbricht, scheint es für automatisierte Untestests notwendig zu sein. Andernfalls zeigt der Komponententest den Dialog an und wartet auf Benutzereingaben. Ich mag diese Lösung nicht, aber mir ist keine Alternative bekannt.

Das Problem, das ich habe, ist, wenn wait_window durch einen direkten Aufruf an die terminate Methode ersetzt wird. Dies ist die Art von Dingen, die ich in Unittests erwarten könnte, nämlich meinen GUI-Code zu testen, ohne die Hauptschleife von tkinter oder wait_window auszuführen.

parent = tk.Tk() 
dialog = Dialog(parent) 
dialog.terminate() 

Diese druckt nur „Terminate genannt“ und druckt nicht „Backspace Ereignis generiert hat.“. Der Aufruf an event_generate scheint keine Wirkung zu haben. Wenn ich dem Aufruf im Debugger folge, kann ich sehen, dass tkinter's event_generate() mit den richtigen Argumenten aufgerufen wird. self = {Dialog} .99999999, sequence = {str}'<BackSpace>', kw = {dict}{} In Anbetracht der Warnung in den TkCmd man Seiten über den Fensterfokus habe ich verifiziert, dass der Dialog mit der Bindung in seiner __init__ Methode fokussiert ist.

Tkinter führt den Rückruf nicht aus. Warum?

EDIT: Dieser nackte Knochen Code zeigt update funktioniert. Es funktioniert jedoch nur, wenn es in __init__ aufgerufen wird, bevor event_generate vom Hauptprogramm aufgerufen wird. (Dieses Puzzle wurde als separate question angehoben)

class UpdWin(tk.Tk): 
    def __init__(self): 
     super().__init__() 
     self.bind('<BackSpace>', 
        lambda event: print(event.keysym, 'event generated.')) 
     self.update() # Update works if placed here 


app = UpdWin() 
app.event_generate('<BackSpace>') 
# app.update() # Update doesn't work if placed here 

Antwort

4

event_generate wird standardmäßig verarbeiten alle sofort Ereignisrückrufe. Wenn Sie das Update jedoch nicht vor dem Aufruf von event_generate aufrufen, ist das Fenster nicht sichtbar und tkinter wird wahrscheinlich alle Ereignisse ignorieren. Sie können steuern, wann das generierte Ereignis mit dem Attribut when verarbeitet wird. Standardmäßig ist der Wert "jetzt", aber eine andere Option ist "Ende", was bedeutet, dass er an die Ereigniswarteschlange angehängt wird, nachdem irgendwelche Ereignisse (wie Neuzeichnungen) verarbeitet wurden.

Eine vollständige Dokumentation über das when Attribut ist auf dem Tcl/Tk Manpage event_generate: http://tcl.tk/man/tcl8.5/TkCmd/event.htm#M34

+0

ich dies nicht zur Arbeit kommen kann. Ich habe versucht, 'parent.update()' hinter 'dialog.terminate()' zu setzen. Ich habe auch versucht, 'self.parent.update()' unmittelbar nach 'self.event_generate ('')' in der 'terminate'-Methode zu setzen, falls' destroy 'mehr tun würde, als sichtbare Elemente zu entfernen. Ich habe bemerkt, dass die Fenster in beiden Fällen kurz auf dem Bildschirm angezeigt wurden. – lemi57ssss

+1

Wenn das Widget zerstört wird, bevor es das Ereignis verarbeiten kann (dh wenn Sie 'dialog.terminate()' aufrufen und dann 'update()') aufrufen, wird das Ereignis wahrscheinlich ignoriert, weil das Fenster nicht mehr existiert. –

+0

Ich habe es geschafft, Update-Funktion zu bekommen, aber auf eine sehr eigenartige Weise. Es scheint nur zu funktionieren, wenn 'update' die Ereigniswarteschlange in' _init__' vor einem nachfolgenden Aufruf von 'event_generate' leert. Ich bin verwirrt und besorgt, da ich es vorziehen würde, den Code nicht zu ändern, um das Testen zu erleichtern. Sehen Sie meine Bearbeitung für eine nackte Knochen-Code-Illustration. – lemi57ssss

Verwandte Themen