Tkinter events Destroy FocusOut

Fragen zu Tkinter.
Antworten
Kniffte
User
Beiträge: 64
Registriert: Dienstag 27. September 2016, 11:05

Moin Zusammen,

Ich habe ein Hauptfenster in dem man über einen Button ein Eingabefenster(Toplevel) öffnen kann.
An das Eingabefenster ist ein <Destroy> Event gebunden und an die Entries jeweils ein <FocusOut> Event.
Es kommt vor, dass ein Nutzer beim Füllen des letzten Feldes den Fokus nicht mehr wechselt und die Methode,
die an das Widget über ein Event gebunden ist, nicht mehr ausgeführt wird.
Ich dachte nun, dass das Feld beim schließen des Fenster ebenfalls den Fokus verlert und so die Methode doch noch
ausgeführt wird. Aber es scheint so nicht zu funktionieren.
Wie kann ich garantieren, dass auch die letzte Eingabe sicher abgeschlossen und die entsprechende Methode ausgeführt wird?
Eine 'trace('w', ...)' möchte ich vermeiden.

Code: Alles auswählen

import tkinter as tk
from tkinter import ttk

class MainWindow(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        self.button_edit = ttk.Button(self,
                                      text="Edit",
                                      command=lambda: DataWindow(self))
        self.button_edit.grid(row=0, column=0, )


class DataWindow(tk.Toplevel):
    def __init__(self, parent):
        tk.Toplevel.__init__(self, parent)
        self.transient(parent)
        self.parent = parent

        self.entry_exducer_diam = ttk.Entry(self, textvariable=tk.StringVar())
        self.entry_exducer_tip_diam = ttk.Entry(self, textvariable=tk.StringVar())
    
        self.entry_exducer_diam.grid(row=0, column=0)
        self.entry_exducer_tip_diam.grid(row=1, column=0)

        ### event bindings ###
        self.bind('<Destroy>', lambda event: self._on_window_closed())
        self.entry_exducer_diam.bind('<FocusOut>',
                                     lambda event: self._on_exducer_diam_changed())
        self.entry_exducer_tip_diam.bind('<FocusOut>',
                                         lambda event: self._on_exducer_tip_diam_changed())

    def _on_window_closed(self):
        # bevor geschlossen wird soll eine der beiden unten aufgeführten Methoden
        # ausgeführt werden, je nachdem welches Entry den Fokus zuletzt hatte
        print('window closed')

    def _on_exducer_diam_changed(self):
        # soll ebenfalls ausgeführt werden, wenn fenster geschlossen wird und
        # entry_exducer_diam den Fokus hatte
        print('exducer diameter changed')

    def _on_exducer_tip_diam_changed(self):
        # soll ebenfalls ausgeführt werden, wenn fenster geschlossen wird und
        # entry_exducer_tip_diam den Fokus hatte
        print('exducer tip diameter changed')

if __name__ == '__main__':
    MainWindow().mainloop()
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

@Kniffte: das einfachste ist doch, die beiden change-Funktionen bei on_window_closed aufzurufen.
Kniffte
User
Beiträge: 64
Registriert: Dienstag 27. September 2016, 11:05

@Sirius:
Der Code ist nur ein kleiner Ausschnitt. Das gesamte Programm hat deutlich mehr Methoden und Eingabefelder. Ich möchte vermeiden, dass Funktionen unnötigerweise mehrmals ausgeführt. Also nur noch die Methode, die am Ende vor schließen des Fensters noch nicht aufgerufen wurde.
Edit: Es kann auch sein, dass bei 10 Eingabefeldern, eben nur 2 inhaltlich verändert wurden vom Nutzer und dann will ich ungern alle 10 Methoden aufrufen, obwohl nur 2 nötig / korrekt wären.
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ist das ein echtes Problem, oder ein gefuehltes? Wenn es *geht* alle aufzurufen ohne das die Benutzerinteraktion leidet, dann solltest du das tun. Einfacher Code schlaegt cleveren Code.

Wenn das nicht der Fall ist, solltest du es trotzdem tun, aber die zehn Methoden nur nach einem vorher/nachher-Vergleich aufrufen. Damit sollten auch Probleme mit dem Focus-Event loesbar sein.
Kniffte
User
Beiträge: 64
Registriert: Dienstag 27. September 2016, 11:05

@__deets__:
Problem ist etwas mehr als 'gefühlt' :)
Also der Nutzer bekommt nach Änderung bestimmter Eingabefelder Rückmeldung in Form von Textzeilen, die in einem Statustextfeld fortlaufend aufgelistet werden. Diese beschreiben was alles als Folge der Nutzereingabe berechnet und geändert wurde. Wenn da nun mehrere Aktionen 'doppelt und dreifach' auftauchen, dann wird das unübersichtlich und die Statusnachrichten verwirren statt zu helfen.

habe auch schon versucht beim schließen des Fensters den Fokus vom zuletzt fokussierten Feld mit tk_focusNext().focus() nochmal zu verschieben,
um so das FocusOut Event des Feldes auszulösen. Aber das will nicht so recht funktionieren. (nur wenn ich das eigentliche schließen des Fensters abfange).

...wäre ja erstmal auch einfacher Code. aber irgendwas übersehe ich hier wohl in der Abarbeitung der Eventreihenfolge?!

Code: Alles auswählen

    def _on_window_closed(self):
        # bevor geschlossen wird soll eine der beiden unten aufgeführten Methoden
        # ausgeführt werden, je nachdem welches Entry den Fokus zuletzt hatte
        self.tk_focusNext().focus()
        print('window closed')
Edit: deinen Vorschlag bzgl. Vorher-/Nachhervergleich hab ich noch nicht so recht verstanden. Meinst du, dass ich Stati speichern sollte, ob eine Funktion bereits aufgerufen wurde?
Zuletzt geändert von Kniffte am Donnerstag 3. August 2017, 10:08, insgesamt 1-mal geändert.
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

@Kniffte: das was __deets__ vorschlägt, solltest Du auf jeden Fall tun, abgleichen, ob sich der Inhalt des Eingabefeldes geändert hat, bevor Du anfängst größere Operationen durchzuführen, denn Fokuswechsel finden viel häufiger statt, als man denkt; vereinfacht gibt es nur eine Methode, die bei jedem Focus-Wechsel aufgerufen wird und immer alle Eingabefelder abfragt und je nach dem, was geändert wurde, reagiert.
Ich weiß nicht, was Du für Operationen überhaupt ausführen willst, aber der Nutzer erwartet, wenn er <ENTER> drückt, dass die aktuelle Eingabe auch übernommen wird, ohne dass er ins nächste Feld wechselt, vielleicht sogar, dass live, bei jedem Tastendruck etwas passiert.
Kniffte
User
Beiträge: 64
Registriert: Dienstag 27. September 2016, 11:05

@Sirius:
Ok verstanden... Vorher-/Nachhervergleich der Feldinhalte und ob überhaupt was geändert wurde.
Ich versuch mich mal daran. Dank euch beiden.
Antworten