Frame verschwindet nach Button-Klick nicht

Fragen zu Tkinter.
Antworten
Jochen1980
User
Beiträge: 40
Registriert: Montag 15. August 2011, 18:44

Servus, ich blicke gerade nicht, warum a) mein Frame mit dem Button-Klick nicht verschwindet und b) warum mein Progress-Dialog nicht angezeigt wird.

In der Methode cmd_actions_start_curvequickfit_50_40_25_10(self) wird ein Dialog neu angezeigt und der Button-Klick verweist dann auf die Methode cmd_actions_start_curvequickfit_50_40_25_10_set(self). Okay, der Methodenaufruf esult_list_of_lines = self.h2r_helper.start_curvequickfit_50_40_25_10(path_name, msa_name) braucht viel Zeit, jetzt wollte ich gerne da einfach einen Info-Frame einblenden, bei dem steht 'Kiste rechnet gerade' und wollte aber dass der Frame mit dem Button nach dem Klick wieder verschwindet. Aktuell ist es so: nach einem Klick startet die umfassende Berechnung der Dialog mit dem Button-bleibt aber und die OK-Schaltfläche ist im 'Bin angeklickt'-Status. Was läuft da falsch?

Code: Alles auswählen

def cmd_actions_start_curvequickfit_50_40_25_10(self):
        #print("cmd_actions_start_curvequickfit_50_40_25_10()")
        self.actions_start_curvequickfit_50_40_25_10_inputdia_frame = tkinter.Toplevel(self.root)
        tkinter.Label(self.actions_start_curvequickfit_50_40_25_10_inputdia_frame, text="Pfad zum MSA; Name des MSA:").pack()
        self.actions_start_curvequickfit_50_40_25_10_inputdia_msa_path = tkinter.Entry(self.actions_start_curvequickfit_50_40_25_10_inputdia_frame)
        self.actions_start_curvequickfit_50_40_25_10_inputdia_msa_name = tkinter.Entry(self.actions_start_curvequickfit_50_40_25_10_inputdia_frame)
        self.actions_start_curvequickfit_50_40_25_10_inputdia_msa_path.pack()
        self.actions_start_curvequickfit_50_40_25_10_inputdia_msa_name.pack()
        self.actions_start_curvequickfit_50_40_25_10_inputdia_button = tkinter.Button(self.actions_start_curvequickfit_50_40_25_10_inputdia_frame, text="OK", command=self.cmd_actions_start_curvequickfit_50_40_25_10_set)
        self.actions_start_curvequickfit_50_40_25_10_inputdia_button.pack()
        
    def cmd_actions_start_curvequickfit_50_40_25_10_set(self):
        #print("cmd_actions_start_curvequickfit_50_40_25_10_set()")
        path_name = self.actions_start_curvequickfit_50_40_25_10_inputdia_msa_path.get()
        msa_name = self.actions_start_curvequickfit_50_40_25_10_inputdia_msa_name.get()
        #print( "Pfad", path_name)
        #print( "Name", msa_name)
        self.actions_start_curvequickfit_50_40_25_10_inputdia_frame.destroy()
        actions_start_curvequickfit_50_40_25_10_progressdia_frame = tkinter.Toplevel(self.root)
        tkinter.Label(actions_start_curvequickfit_50_40_25_10_progressdia_frame, text="Berechnung läuft ...").pack()
        result_list_of_lines = self.h2r_helper.start_curvequickfit_50_40_25_10(path_name, msa_name)
        actions_start_curvequickfit_50_40_25_10_progressdia_frame.destroy()
        self.actions_start_curvequickfit_50_40_25_10_frame = tkinter.Toplevel(self.root)
        tkinter.Label(self.actions_start_curvequickfit_50_40_25_10_frame, text="CurveQuickFit 50 40 25 10 ...").pack()
        self.actions_start_curvequickfit_50_40_25_10_scrollpane = tkinter.scrolledtext.ScrolledText(self.actions_start_curvequickfit_50_40_25_10_frame, width=80, height=10)
        self.actions_start_curvequickfit_50_40_25_10_output_string = "Auswertung curvequickfit() - Gesamtmsa: \n"
        for element in result_list_of_lines:
            string_to_append = str( element )
            self.actions_start_curvequickfit_50_40_25_10_output_string += string_to_append
            self.actions_start_curvequickfit_50_40_25_10_output_string += "\n"
        self.actions_start_curvequickfit_50_40_25_10_scrollpane.insert("end", self.actions_start_curvequickfit_50_40_25_10_output_string)
        self.actions_start_curvequickfit_50_40_25_10_scrollpane.pack()
        
BlackJack

@Jochen1980: Die Namen sind nicht Dein Ernst oder!?
Jochen1980
User
Beiträge: 40
Registriert: Montag 15. August 2011, 18:44

Naja, ich sehe es so: ich denke Leserlichkeit und Verständlichkeit ist wichtig. Mit Tkinter bin ich noch unerfahren und in meiner Gui-Klasse muss ja die Methode auf den Frame zugreifen können, der in einer anderen Methode erzeugt wurde. Ich speichere also alle Frames als Instanzattribute. Durch die Namenskonvention kann ich neue Anwendungsfälle einfach reinhacken, ohne dass es versehentlich zu Konflikten kommt. Ich fand das Vorgehen für eine schnelle Lösung brauchbar.
BlackJack

@Jochen1980: Gerade weil Lesbarkeit wichtig ist sind diese extrem langen Namen schlecht. Die haben ja fast alle den gleichen Präfix und die wichtigen Informationen, nämlich das kleine Stück wo sie sich unterscheiden. gehen dabei im Rauschen unter. Alles was im Präfix steckt sollte durch den, oder die Namensräume in denen die Namen stecken, also den Kontext, klar werden, und nicht immer und immer wieder redundant wiederholt werden.
Jochen1980
User
Beiträge: 40
Registriert: Montag 15. August 2011, 18:44

Stimmt schon, aber das sind Sachen, die sind dann auch behebbar, da wird dann schon noch der ein oder andere Search&Replace-Lauf ausgeführt. Wichtiger wäre es für mich nun auch zu verstehen, warum sich Tkinter so verhält ... meine Erfahrung mit Java Swing ist, wenn man es nicht richtig gefressen hat, wie das Toolkit arbeitet, dann passiert laufend 'unerklärliches' - ist man aber einmal drin, dann beginnts richtig Spaß zu machen.
Jochen1980
User
Beiträge: 40
Registriert: Montag 15. August 2011, 18:44

Sodala, die Variablen etwas schnuckeliger, leider klappt der Effekt 'Frame nach Buttonklick verschwinden lassen' und 'bis zum Berechnungsende eine temporäre Meldung anzeigen' immer noch nicht.

Der Problem-Button ist: self.ascqf_pre_button
Der Meldungs-Frame der aufgehen soll ist: self.ascqf_tmp_frame

Code: Alles auswählen

def cmd_actions_start_curvequickfit_50_40_25_10(self):
        #print("cmd_actions_start_curvequickfit_50_40_25_10()")
        # ascqf_pre_frame
        self.ascqf_pre_frame = tkinter.Toplevel(self.root)
        tkinter.Label(self.ascqf_pre_frame, text="Pfad zum MSA; Name des MSA:").pack()
        self.ascqf_pre_dia_msa_path = tkinter.Entry(self.ascqf_pre_frame)
        self.ascqf_pre_dia_msa_name = tkinter.Entry(self.ascqf_pre_frame)
        self.ascqf_pre_dia_msa_path.pack()
        self.ascqf_pre_dia_msa_name.pack()
        self.ascqf_pre_button = tkinter.Button(self.ascqf_pre_frame, text="OK", command=self.cmd_actions_start_curvequickfit_50_40_25_10_set)
        self.ascqf_pre_button.pack()
        
    def cmd_actions_start_curvequickfit_50_40_25_10_set(self):
        #print("cmd_actions_start_curvequickfit_50_40_25_10_set()")
        path_name = self.ascqf_pre_dia_msa_path.get()
        msa_name = self.ascqf_pre_dia_msa_name.get()
        self.ascqf_pre_frame.destroy()
        self.ascqf_tmp_frame = tkinter.Toplevel(self.root)
        tkinter.Label(self.ascqf_tmp_frame, text="Berechnung läuft ...").pack()
        result_list_of_lines = self.h2r_helper.start_curvequickfit_50_40_25_10(path_name, msa_name)
        self.ascqf_tmp_frame.destroy()
        self.ascqf_res_frame = tkinter.Toplevel(self.root)
        tkinter.Label(self.ascqf_res_frame, text="CurveQuickFit 50 40 25 10 ...").pack()
        self.ascqf_res_scrollpane = tkinter.scrolledtext.ScrolledText(self.ascqf_res_frame, width=80, height=10)
        self.ascqf_res_output_string = "Auswertung curvequickfit() - Gesamtmsa: \n"
        for element in result_list_of_lines:
            string_to_append = str( element )
            self.ascqf_res_output_string += string_to_append
            self.ascqf_res_output_string += "\n"
        self.ascqf_res_scrollpane.insert("end", self.ascqf_res_output_string)
        self.ascqf_res_scrollpane.pack()
BlackJack

@Jochen1980: Du startest irgendwann die Hauptschleife von Tkinter, die dafür zuständig ist, dass die GUI gezeichnet und aktualisiert wird, und die Ereignisse wie zum Beispiel Mausklicks verarbeitet. Wenn die Schaltfläche gedrückt wird, dann wird aus diesem GUI-Code heraus die in `command` angegebene Funktion aufgerufen. Und solange deren Code läuft, kann natürlich die Hauptschleife nicht laufen. Die geht erst weiter wenn die Flusskontrolle wieder an den Aufrufer, also eben diese Hauptschleife, zurück geht.

Wenn Du in einem Rückruf aus der GUI-Hauptschleife heraus etwas längeres machst, dann ist die GUI für diese Zeit „tot”. Du müsstest die Berechnung also zum Beispiel in einen eigenen Thread (`threading`-Modul) ausführen lassen und beispielsweise mittels `Widget.after()` regelmässig prüfen, ob die Berechnung schon fertig ist. Wichtig dabei ist, dass die GUI immer nur von dem Thread aus manipuliert werden darf, in der die GUI-Hauptschleife läuft!
Jochen1980
User
Beiträge: 40
Registriert: Montag 15. August 2011, 18:44

Oh, habe mich gerade etwas eingelesen und stelle fest, dass das ein komplexeres Thema ist. Schade, Tutorial fand ich auch keines, dass die Anwendung von tkinter Threads und widget.after() zeigt. Dazu bin ich etwas überrascht, dass mir in meinem Python-Buch das modul _thread statt threading erklärt wird ... _thread klingt aber nach innereien?!

Danke falls mir einer einen Link zum Zusammenspiel der drei Bausteine liefern kann oder die wenigen Zeilen auf mein Beispiel umbiegt. Ich habe das nun erst einmal zurückgestellt, bis weiter Anwendungsfälle durch Thread-Einsatz positiv beeinflusst werden.
BlackJack

@Jochen1980: Ja, `_thread` sind Innereien von denen man die Finger lassen sollte. Selbst als das Modul nocht `thread` hiess wurde in dessen Dokumentation deutlich darauf hingewiesen, dass man `threading` verwenden soll.
Antworten