Eingabefeld löschen wenn Validierung fails

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
muekno
User
Beiträge: 20
Registriert: Dienstag 28. Januar 2020, 18:20

ich habe folgenden Code:

Code: Alles auswählen

"""
	erstes test project
	erst benötigte Module laden
"""
import tkinter as tk		# für die GUI
from tkinter import ttk		# bessere widgets für die GUI

root = tk.Tk()
root.title('User im System')
root.geometry("800x400+50+50")
root.minsize(width=850, height=400)
root.maxsize(width=1000, height=600)

""" check if age in range """
def check_age():
	try:
		value = int(ent_age.get())
		if value < 10 or value > 99:
			lbl_age_info.configure(text=f"Alter {value} ungültig")
		else:
			lbl_age_info.configure(text="OK")
			return True
	except:
		ValueError: lbl_age_info.configure(text="Nur Zahlen")
		lbl_age_info.configure(text="Nur Ziffern")
	ent_age.focus_set()
#	ent_age.delete(0, END)
	return False


arial_20 =("Arial",18)

""" create Widgets """

""" Age """
lbl_age = ttk.Label(root, text="Alter: ", width=10, font=arial_20,padding=5)
lbl_age.grid(row=0, column=0, padx='5', pady='5')

ent_age = ttk.Entry(root,width=2,font=arial_20, validatecommand=check_age, validate="focusout")
ent_age.grid(row=0, column=1, padx='5', pady='5',sticky="w")

lbl_age_info = ttk.Label(root, text="zwischen 10 und 99", width=20, font=arial_20, padding=5)
lbl_age_info.grid(row=0, column=3, padx='5', pady='5')

"""" Gender """
lbl_gender = ttk.Label(root, text="Geschlecht: (m/w/d)", font=arial_20, padding=5)
lbl_gender.grid(row=1, column=0, padx='5', pady='5')
#Adding combobox drop down list

cbx_gender = ttk.Combobox(root,values=["m","w","d"],width=1,font=arial_20)
cbx_gender.configure(font=arial_20)
cbx_gender.grid(row=1, column=1, padx='5', pady='5', sticky='w')

root.mainloop()

In der Prüffunktion solle das Eingabefeld gelöscht werden eigentlich mit der auskommentierten Zeile
....
lbl_age_info.configure(text="Nur Ziffern")
ent_age.focus_set()
# ent_age.delete(0, END)
return False
....

Nur wenn ich die drinn lasse,, bekomme ich Laufzeitfehler und auch PyCharm mekert

Code: Alles auswählen

Exception in Tkinter callback
Traceback (most recent call last):
  File "/usr/local/Cellar/python@3.9/3.9.19_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/tkinter/__init__.py", line 1892, in __call__
    return self.func(*args)
  File "/Users/mk/Pycharm/class_test/test.py", line 27, in check_age
    ent_age.delete(0, END)
NameError: name 'END' is not defined


Habe schon anderes versucht und Stunden im Internet recherchiert

Wie bekomme ich das hin, dass im Fehlerfall das feld wieder leer ist

Dank und Gruß

Rainer
Benutzeravatar
__blackjack__
User
Beiträge: 14032
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@muekno: Wo denkst Du denn das `END` definiert worden wäre? Irgendwelche Namen haben ja nicht auf magische Weise einen Wert. Alles was nicht im builtins-Modul definiert ist, gibt es nicht in einem Modul bis es definiert oder von woanders importiert wurde. Was ja letztlich auch eine Definition ist.

Sonstige Anmerkungen: Zeichenketten sind keine Kommentare. An bestimmten Stellen sind es DocStrings, dann müssen die aber auch an der von der Syntax vorgesehenen Stelle stehen. Bei Funktionen und Methoden (und Klassen) beispielsweise als erstes *im* Block, nicht davor.

Kommentare sollen dem Leser einen Mehrwert über den Code geben. Faustregel: Kommentare beschreiben nicht *was* der Code macht, denn das steht da bereits als Code, sondern warum er das macht. Sofern das nicht offensichtlich ist. Offensichtlich ist in aller Regel auch was in der Dokumentation von Python und den verwendeten Bibliotheken steht.

Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.

Funktionen und Methoden bekommen alles was sie ausser Konstanten benötigen, als Argument(e) übergeben. Hier kommt man noch mit `functools.partial()` aus, aber bei jeder nicht-trivialen GUI braucht man objektorientierte Programmierung (OOP), also muss eigene Klassen schreiben können.

Namen sollten keine kryptischen Prä- oder Suffixe haben, also kein `ent_`, `lbl_` oder `cbx_` beispielsweise. Wenn man ein `age_entry` meint, sollte man nicht `ent_age` schreiben. Also auch die richtige Reihenfolge der Worte verwenden, denn ein Alterseingabefeld ist was anderes als ein Eingabefeldalter. Man muss auch nicht alles an einen Namen binden.

`geometry()` ist keine gute Idee. Man gibt weder die Grösse noch die Platzierung auf dem Desktop vor. Die Grösse ergibt sich automatisch aus dem Inhalt, und die Position auf dem Desktop legt die Fensterverwaltung fest, nach den Wünschen des Benutzers. Üblicherweise dort wo Platz ist und nicht immer an der selben Stelle. Auch bei anderen Widgets gibt man keine Breite an, beispielsweise bei `Label` ergibt sich die Grösse ja auch aus dem Inhalt.

Ebenfalls unsinnig ist es bei jedem Widget eine Schriftart- und Grösse anzugeben. Insbesondere wenn das dann auch noch überall *gleich* ist. Das ist eine Systemeinstellung die der Benutzer macht, nichts was sich einzelne Anwendungen anmassen sollten.

Statt ``if value < 10 or value > 99:`` würde man in Python ``if not 10 <= value <= 99:`` schreiben.

Die Werte für die beiden Altersgrenzen kommen zweimal im Programm vor, in verschiedenen Funktionen, und einmal als Zahlen und einmal innerhalb eines Textes. Da würde man Konstanten für definieren, damit keine Gefahr besteht das Text und tatsächliche Prüfung andere Werte verwenden.

Bei der oberen Altersbegrenzung könnte es übrigens Ärger mit dem Gleichstellungsbeauftragten geben — das ist Altersdiskriminierung. 👴

Beim ``exept:`` ist ja offensichtlich was falsch. Das geht nur durch den Compiler wegen der doofen Typannotationen.

Zwischenstand:

Code: Alles auswählen

"""
Erstes Testprojekt.
"""

import tkinter as tk
from functools import partial
from tkinter import ttk

MIN_AGE = 10
MAX_AGE = 99  # XXX Age discrimination?


def check_age(age_entry, age_info_label):
    """
    Check if age is in range.
    """
    try:
        value = int(age_entry.get())
        if not MIN_AGE <= value <= MAX_AGE:
            age_info_label.configure(text=f"Alter {value} ungültig")
        else:
            age_info_label.configure(text="OK")
            return True
    except ValueError:
        age_info_label.configure(text="Nur Ziffern")

    age_entry.focus_set()
    age_entry.delete(0, tk.END)
    return False


def main():
    root = tk.Tk()
    root.title("User im System")

    ttk.Label(root, text="Alter:", padding=5).grid(
        row=0, column=0, padx=5, pady=5
    )
    age_entry = ttk.Entry(root, width=3, validate="focusout")
    age_entry.grid(row=0, column=1, padx=5, pady=5, sticky=tk.W)
    age_info_label = ttk.Label(
        root, text=f"zwischen {MIN_AGE} und {MAX_AGE}", padding=5
    )
    age_info_label.grid(row=0, column=2, padx=5, pady=5)
    age_entry["validatecommand"] = partial(
        check_age, age_entry, age_info_label
    )

    ttk.Label(root, text="Geschlecht:", padding=5).grid(
        row=1, column=0, padx=5, pady=5
    )
    gender_combobox = ttk.Combobox(root, values=["m", "w", "d"], width=2)
    gender_combobox.grid(row=1, column=1, padx=5, pady=5, sticky=tk.W)

    root.mainloop()


if __name__ == "__main__":
    main()
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
Antworten