Tkinter Popup Fenster richtig schliessen?

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
PythonMan2019
User
Beiträge: 13
Registriert: Montag 18. Februar 2019, 14:29

Hallo

habe da ein relativ triviales Problem:

Ein Popup Fenster soll aufgehen und Werte in die entsprechenden Felder eingegeben werden.
Danach kann man das Fenster wieder mit OK schliessen.
Nach dem Mainloop() habe ich die Funktion aufgerufen.
Aber es taucht eine Fehlermeldung auf.....

Der Code:

Code: Alles auswählen

from Tkinter import *


_A_P_=""
_List = []

def RFD_ChoicePopup():
   
   _A_P_ = _A_P.get()

   _List=[_A_P_]
    
   master.quit()
   try:
      master.destroy()
   except:
      pass
   return _List
   

master = Tk()

Label(master, text="Checkpoints / Variant:\n ",font = ("arial", 11, "bold")).grid (row=0, column=0, columnspan=2, sticky=W)
Label(master, text="ACP \n").grid (row=1, column=0, sticky=E)


_A_P = Entry(master)

_A_P.insert(10,"Miller")

_A_P.grid(row=1, column=1)


Button(master,text='OK',command=RFD_ChoicePopup).grid(row=10,column=0,sticky=W,pady=5)

master.geometry("300x330+750+350")

mainloop()

print RFD_ChoicePopup()
Fehlermeldung:

Traceback (most recent call last):
File "C:\Program Files (x86)\Common Files..\Pythonwin\pywin\framework\scriptutils.py", line 307, in RunScript
debugger.run(codeObject, __main__.__dict__, start_stepping=0)
File "C:\Program Files (x86)\Common Files\..s\Pythonwin\pywin\debugger\__init__.py", line 60, in run
_GetCurrentDebugger().run(cmd, globals,locals, start_stepping)
File "C:\Program Files (x86)\Common Files\...\Pythonwin\pywin\debugger\debugger.py", line 631, in run
exec cmd in globals, locals
File "C:\Users\...\Script1.py", line 42, in <module>
print RFD_ChoicePopup()
File "C:\Users\a...\Script1.py", line 9, in RFD_ChoicePopup
_A_P_ = _A_P.get()
File "C:\Program Files (x86)\Common Files\....\Python25\Lib\lib-tk\Tkinter.py", line 2371, in get
return self.tk.call(self._w, 'get')
TclError: invalid command name ".48604384"
>>>
Kann mir einer sagen, warum ich die Funktion nach dem mainloop nicht ausführen kann?


Grüße
Sirius3
User
Beiträge: 18216
Registriert: Sonntag 21. Oktober 2012, 17:20

@PythonMan2019: Python2 ist veraltet und Python2.5 erst recht. Warum verwendest Du so eine alte Version?

Sternchenimporte vermeiden. Sinnvolle Namen vergeben. _A_P-irgendwas hat mich fünfmal lesen gekostet, bis ich verstanden habe, was Du da eigentlich machst. Alles was mit einem _ anfängt, bedeutet, dass die Variable nicht verwendet wird. Das erste _A_P wird aber auch gar nicht verwendet.
Das globale _List ist auch nicht aussagekräftiger, wird aber auch gar nicht verwendet.
Bei Dir ist das Gegenteil der Fall. Variablennamen und Funktionsnamen schreibt man klein_mit_unterstrich.
Keine nackten Excepts und keine globalen Variablen benutzen. _A_P und master tauchen auf magische Weise in RFD_ChoicePopup auf, was sie nicht sollten.
Rückgabewerte bei Ereignisbearbeitung sind unsinnig, weil sie nicht weiter verwendet werden. Eine Liste mit nur einem Element ist auch irgendwie unsinnig.

Du rufst get auf einem TK-Widget auf, nachdem Du den master per destroy zerstört hast. Das geht nicht.

Hast Du Dir soetwas vorgestellt?

Code: Alles auswählen

import Tkinter as tk

def popup():
    master = tk.Tk()
    master.geometry("300x330+750+350")
    tk.Label(master, text="Checkpoints / Variant:", font=("arial", 11, "bold")).grid(row=0, column=0, columnspan=2, sticky=tk.W)
    tk.Label(master, text="ACP").grid(row=1, column=0, sticky=tk.E)
    name = tk.Entry(master)
    name.insert(10, "Miller")
    name.grid(row=1, column=1)
    tk.Button(master, text='OK', command=master.quit).grid(row=10, column=0, sticky=tk.W, pady=5)
    master.mainloop()
    result = name.get()
    master.destroy()
    return result

def main():
    print popup()

if __name__ == '__main__':
    main()
Zuletzt geändert von Sirius3 am Dienstag 23. April 2019, 10:50, insgesamt 1-mal geändert.
Benutzeravatar
__blackjack__
User
Beiträge: 13919
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@PythonMan2019: Nach der Hauptschleife ist das Programm zuende und alle GUI-Elemente sind zerstört. Es macht auch gar keinen Sinn die Funktion an der Stelle aufzurufen, denn ohne das die Hauptschleife läuft, funktioniert diese Funktion nicht. Auch ein Rückgabewert macht bei dieser Funktion keinen Sinn, denn der wird an die Hauptschleife zurückgegeben und die erwartet keine Rückgabewerte von Funktionen die als Rückruffunktion für `command` aufgerufen werden. Du hast eine falsche Vorstellung wie GUI-Programme funktionieren. Nicht mehr Du hast den Programmfluss direkt unter Kontrolle, sondern Du kannst nur auf Ereignisse reagieren. Das führt unweigerlich dazu das man für jedes nicht-triviale GUI-Programm objektorientierte Programmierung braucht, weil man sich über Rückrufe hinweg Daten und Zustand merken muss.

Sonstiges: Es macht keinen Sinn heute noch ein neues Programm mit Python 2 anzufangen: https://pythonclock.org/

Sternchen-Importe sind Böse™. Bei `Tkinter` holt man sich so fast 190 Namen in das Modul von denen nur ein ganz kleiner Bruchteil tatsächlich verwendet wird. Es wird schwerer nachzuvollziehen wo Namen her kommen und es besteht die Gefahr von Namenskollisionen.

Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst. Alles was eine Funktion oder Methode ausser Konstanten benötigt sollte als Argument(e) übergeben werden, also *keine* globalen Variablen.

Einen Namen der später an ein `Entry` gebunden wird, sollte vorher nicht an eine leere Zeichenkette gebunden werden. Manchmal *braucht* man das, dass man einen Namen definieren möchte, aber das endgültige Objekt erst später erzeugt – dann kann man `None` als ”Platzhalter” verwenden. Aber in diesem Fall gibt es absolut keinen Grund dafür. Es schaut eher so aus als wenn der Programmablauf nicht verstanden wurde. Das gleiche gilt für `_List` auf Modulebene: Das wird nirgends verwendet, kann also weg. Sollte da sowieso nicht stehen, genau wie das `Entry`, weil auf Modulebene keine Variablen gehören.

Die Namen sind fast alle überarbeitungswürdig. Namen schreibt man in Python klein_mit_unterstrichen. Ausnahmen: Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase). Und man verwendet keine kryptischen Abkürzungen. Zudem haben Grunddatentypen wie `list` nichts im Namen verloren. Man ändert während der Programmentwicklung öfter mal den Datentyp und dann hat man entweder falsche, irreführende Namen im Programm, oder muss alle betroffenen Namen anpassen. Das ist fehleranfällig und macht nur unnötig Arbeit.

Keine nackten ``except``\s verwenden ohne die konkrete(n) Ausnahme(n) die man da erwartet und behandeln will. Und *jede* Ausnahme einfach komplett zu *ignorieren* ist eigentlich *nie* eine sinnvolle Behandlung.

Warum fügst Du etwas am Index 10 in ein garantiert leeres Eingabefeld ein?
“Java is a DSL to transform big Xml documents into long exception stack traces.”
— Scott Bellware
PythonMan2019
User
Beiträge: 13
Registriert: Montag 18. Februar 2019, 14:29

Hallo

danke für die hilfreichen Informationen. Werde diese beherzigen!!!
Ja, ihr habt recht. Ich bin gerade noch dabei die ganzen Funktionen von Tkinter zu lernen.
Ich gehe alles nochmal step-by-step durchs Tutorial.
Habt ihr denn ein geeignetes Tutorial?

Grüße
PythonMan2019
User
Beiträge: 13
Registriert: Montag 18. Februar 2019, 14:29

Hallo

das Popup Fenster habe ich soweit angepasst und es funktioniert gut.
Einziges Problem ist, wenn ich auf das Kreuz oben rechts im Fenster drücke um das Fenster abzubrechen, taucht folgende Fehlermeldung:
Traceback (most recent call last):
File "C:\Users\User\Documents\...\Pythonwin\pywin\framework\scriptutils.py", line 311, in RunScript
debugger.run(codeObject, __main__.__dict__, start_stepping=0)
File "C:\Users\User\Documents\...\Pythonwin\pywin\debugger\__init__.py", line 60, in run
_GetCurrentDebugger().run(cmd, globals,locals, start_stepping)
File "C:\Users\User\...\Pythonwin\pywin\debugger\debugger.py", line 631, in run
exec cmd in globals, locals
File "C:\Users\User\..\Script1.py", line 21, in <module>
main()
File "C:\Users\User\..\Script1.py", line 18, in main
print popup()
File "C:\Users\User\..\Script1.py", line 13, in popup
result = name.get()
File "C:\Users\User\..\Lib\lib-tk\Tkinter.py", line 2371, in get
return self.tk.call(self._w, 'get')
TclError: invalid command name ".117426256"
Wie kriege ich es hin, dass ich das Fenster schliessen kann, ohne dass irgendetwas übernommen wird und er einfach abbricht?

Gruss
Sirius3
User
Beiträge: 18216
Registriert: Sonntag 21. Oktober 2012, 17:20

Es ist doch schonmal gut, dass eine Exception geworfen wird, wenn das Fenster einfach so geschlossen wird. So kannst Du gültige Eingaben von Abbruch unterscheiden. Für den Anwender wäre noch eine aussagekräftigere Exception gut, also indem Du sie an der passenden Stelle abfängst und eine benutzerdefinierte wirfst.
PythonMan2019
User
Beiträge: 13
Registriert: Montag 18. Februar 2019, 14:29

Hallo

Danke. Hab es nun mit try: except: hinbekommen!

Grüße
Benutzeravatar
__blackjack__
User
Beiträge: 13919
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Wobei das ziemlich sicher schlecht/falsch gelöst ist. Selbst wenn `TclError` explizit behandelt wird, sollte man einfach nicht den Wert abfragen wenn das Fenster dazu bereits zerstört ist. Sauberer wäre es sich für das Schliessen des Fensters über das Kreuz in der Fensterleiste (oder auch auf anderen Wegen) zu registrieren und das wie ein Abbrechen des Eingabedialogs zu behandeln. Also so wie `tkinter.simpledialog.Dialog` das macht. Was man dann vielleicht auch gleich benutzen könnte, bevor man das Rad neu erfindet. Oder vielleicht sogar eine der `ask*()`-Funktionen aus dem Modul, falls die ausreichen.
“Java is a DSL to transform big Xml documents into long exception stack traces.”
— Scott Bellware
PythonMan2019
User
Beiträge: 13
Registriert: Montag 18. Februar 2019, 14:29

Hallo

ich versuche gerade den OK Button mit der "Enter" Taste zu verbinden, aber irgendwie klappt es nicht :-(
Was mache ich falsch? ich möchte nur mit der Enter Taste das Fenster wieder schliessen!

Code: Alles auswählen

import Tkinter as tk

master = tk.Tk()

def onReturn(*event):
    #xGet = entry.get()
    #print xGet
    master.destroy
   
entry = tk.Entry(master,bd=1)
entry.pack()

button = tk.Button(master, text='Enter', command=onReturn)
button.pack()
button.focus_set()
master.bind('<Return>', onReturn)

tk.mainloop()
Grüsse
Stefan
PythonMan2019
User
Beiträge: 13
Registriert: Montag 18. Februar 2019, 14:29

Kann mir da keiner weiterhelfen?

Gruss
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich sehe schonmal einen Fehler im onReturn. Da denkst du nur du rufst auf, tust es aber nicht.
PythonMan2019
User
Beiträge: 13
Registriert: Montag 18. Februar 2019, 14:29

Verstehe nicht was du meinst?
PythonMan2019
User
Beiträge: 13
Registriert: Montag 18. Februar 2019, 14:29

Verstehe nicht was du meinst?
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ah, Wort vergessen. Du rufst destroy nicht auf. Dazu fehlen die Klammern.
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi PythonMan2019

So sollte es funktionieren:

Code: Alles auswählen

import Tkinter as tk

def onReturn(event=None):
    #xGet = entry.get()
    #print xGet
    master.destroy()

master = tk.Tk()

entry = tk.Entry(master, bd=1)
entry.pack()

button = tk.Button(master, text='Enter', command=onReturn)
button.pack()
button.focus_set()
master.bind('<Return>', onReturn)

master.mainloop()
Gruss wuf :-)
Take it easy Mates!
PythonMan2019
User
Beiträge: 13
Registriert: Montag 18. Februar 2019, 14:29

Hallo

Danke für die Korrektur. Mit dem code klappts.
Aber sobald ich diesen zeiler hinzufüge

Code: Alles auswählen

tk.Label(master, text="Checkpoints / Variant:\n ",font = ("arial", 11, "bold")).grid (row=0, column=0, columnspan=2, sticky=tk.W)
Geht das popup in die knie. Es öffnet sich mit leerem inhalt und lässt sich nicht mehr schliessen. Ich muss dann das script killen.
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi PythonMan2019

So funktioniert es wieder. Beim mischen von pack & grid ist Vorsicht geboten:

Code: Alles auswählen

import Tkinter as tk

def onReturn(event=None):
    #xGet = entry.get()
    #print xGet
    master.destroy()

master = tk.Tk()

tk.Label(master, text="Checkpoints / Variant:\n ",font = ("arial", 11, "bold")
    ).pack()
    
entry = tk.Entry(master, bd=1)
entry.pack()

button = tk.Button(master, text='Enter', command=onReturn)
button.pack()
button.focus_set()
master.bind('<Return>', onReturn)

master.mainloop()
Gruss wuf :-)
Take it easy Mates!
Antworten