Eingabefeld nur mit Zahlen

Fragen zu Tkinter.
Antworten
Count
User
Beiträge: 28
Registriert: Donnerstag 28. November 2019, 12:56

Guten Abend,

ist es möglich bei einem Eingabefeld zu definieren, dass nur Zahlen eingegeben werden können, ohne dass man es erst durch Knopfdruck prüfen lassen muss?
Probiert hab ich einiges schon und bei google auch nichts sinnvolles gefunden.

Danke im Voraus und fröhliche Weihnacht schon mal ^^

Code: Alles auswählen

from tkinter import *
import tkinter as tk
test = tk.Tk()

m1=Label(test, text='Menge (Ist)')
m1.place(x = 7, y = 9, width=70, height=25)
m2=Entry(test, textvariable=int)  					#funktioniert nicht
m2.place(x = 132, y = 9, width=40, height=25)
m3=Label(test, text='l')
m3.place(x = 175, y = 9, width=5, height=25)

test.mainloop()
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Benutz eine IntVar. Und räum mal deine rödeligen Importe auf. Warum das immer bei tkinter Code so mies ist, würde ich wirklich gerne mal wissen.
bfm
User
Beiträge: 88
Registriert: Donnerstag 14. März 2013, 09:42

Hallo,

ein Entry-Widget hat eine Valdidate-Methode.

mfg
Count
User
Beiträge: 28
Registriert: Donnerstag 28. November 2019, 12:56

Danke @bfm
der Hinweis hat mir sehr geholfen

Für die, die auch mal das Problem haben, hier meine Lösung:

Code: Alles auswählen

from tkinter import *
import tkinter as tk

test = tk.Tk()

def only_numbers(char):
 return char.isdigit()
validation = test.register(only_numbers)

m1=Label(test, text='Menge (Ist)')
m1.place(x = 7, y = 9, width=70, height=25)
m2=Entry(test, validate="key", validatecommand=(validation, '%S')) 
m2.place(x = 132, y = 9, width=40, height=25)
m3=Label(test, text='l')
m3.place(x = 175, y = 9, width=5, height=25)

test.mainloop()
Sirius3
User
Beiträge: 18216
Registriert: Sonntag 21. Oktober 2012, 17:20

Das Mischen von richtigem Import (… as tk) und *-Import ist seltsam. Das Mischen von ausführbarem Code und Funktionsdefinitionen macht den Code unleserlich. Da Du sowieso nur str.isdigit aufrufst, kannst Du das register auch direkt übergeben.
Nicht alles muß an Variablen gebunden werden. Labels, die sonst nie wieder gebraucht werden, zum Beispiel. ›place‹ sollte man nicht verwenden, weil das auf jedem System und jedem Monitor anders aussieht, und im Zweifel die GUI unbenutzbar macht.

Code: Alles auswählen

import tkinter as tk

test = tk.Tk()
validation = (test.register(str.isdigit), '%S')
tk.Label(test, text='Menge (Ist)').grid(row=0, column=0)
entry = tk.Entry(test, width=4, validate="key", validatecommand=validation) 
entry.grid(row=0, column=1)
tk.Label(test, text='l').grid(row=0, column=2)
test.mainloop()
Count
User
Beiträge: 28
Registriert: Donnerstag 28. November 2019, 12:56

ja das ist auch eine Möglichkeit @Sirius3

nun ist mir aber aufgefallen, dass man gar keine Dezimalzahlen reinschreiben kann, weil ',' und '.' als nicht Zahl definiert ist
es gibt doch sicher eine elegantere Lösung als alles bis auf ',' und '.' auszugrenzen.

Hat da jemand eine Idee?
bb1898
User
Beiträge: 216
Registriert: Mittwoch 12. Juli 2006, 14:28

Count hat geschrieben: Montag 30. Dezember 2019, 19:36 nun ist mir aber aufgefallen, dass man gar keine Dezimalzahlen reinschreiben kann, weil ',' und '.' als nicht Zahl definiert ist
es gibt doch sicher eine elegantere Lösung als alles bis auf ',' und '.' auszugrenzen.
Die gibt's: der erste Vorschlag von @__deets__, nur mit DoubleVar() statt IntVar(). Ich habe mir gerade mal den Spaß gemacht, ein vorhandenes Buch-Programm dafür abzuwandeln. Aber Achtung, das will seine Dezimalzahlen mit Punkt, nicht mit Komma (sollte sich mit Einsatz von locale ändern lassen), und bei falschen Eingaben gibt's eine Exception und die wird im Kommandozeilenfenster ausgegeben. Das solltest Du beim Ausprobieren berücksichtigen.

Code: Alles auswählen

#!/usr/bin/env python3
# double_entry.py

"""Dezimalzahlen-Eingabe mit Tkinter"""

import tkinter as tk
from tkinter import ttk

class NumberEntryView(tk.Frame):
    """
    Dezimalzahl eingeben und mit Mehrwertsteuer ausgeben.
    Nur, um zu zeigen, dass eine DoubleVar() wirklich eine Zahl und
    keine Zeichenkette liefert.
    """

    def __init__(self, parent):
        super().__init__(parent)
        self.number = tk.DoubleVar()
        self.result_string = tk.StringVar()
        self.result_string.set("Noch kein Wert eingegeben.")
        number_label = ttk.Label(self, text="Dezimalzahl:")
        number_entry = ttk.Entry(self, textvariable=self.number)
        calc_button = ttk.Button(self, text="Berechnung",
                                 command=self.on_calculation)
        result_label = ttk.Label(self, textvariable=self.result_string)
        number_label.grid(row=0, column=0, sticky=tk.W)
        number_entry.grid(row=0, column=1, sticky=(tk.W + tk.E))
        calc_button.grid(row=0, column=2, sticky=tk.E)
        result_label.grid(row=1, column=0, columnspan=3)
        self.columnconfigure(1, weight=1)

    def on_calculation(self):
        value = self.number.get()
        with_tax = value * 1.19
        self.result_string.set(f"Netto: {value}, Brutto: {with_tax}")


class MyApplication(tk.Tk):
    """Double Entry Main Application"""

    def __init__(self):
        super().__init__()
        self.title("MWSt mit Tkinter")
        NumberEntryView(self).pack()


if __name__ == "__main__":
    app = MyApplication()
    app.mainloop()
Vorbild: Alan D. Moore, Python GUI Programming with Tkinter, "Creating a better Hello World Tkinter", S. 55/616 (das sagt mein EBook-Betrachter - Seitenzahlen von EPub-EBooks sind mit Vorsicht zu genießen!). Das Buch als Ganzes ist auf jeden Fall nützlich, so weit es Tkinter betrifft.
Count
User
Beiträge: 28
Registriert: Donnerstag 28. November 2019, 12:56

bei deiner Abwandlung kann man Buchstaben mit eintragen und das soll eben von vornherein nicht gehen
zur Not muss ich es mit 2 separaten Eingabefeldern regeln
dennoch danke und schon mal n guten Rutsch
Sirius3
User
Beiträge: 18216
Registriert: Sonntag 21. Oktober 2012, 17:20

Was heißt zur Not? Du kennst doch schon alles notwendige, um die Aufgabe zu lösen.

Code: Alles auswählen

import tkinter as tk

def validate_number(text):
    try:
        if text:
            _ = float(text.replace(',', '.'))
    except ValueError:
        return False
    return True

test = tk.Tk()
validation = (test.register(validate_number), '%P')
tk.Label(test, text='Menge (Ist)').grid(row=0, column=0)
entry = tk.Entry(test, width=4, validate="key", validatecommand=validation) 
entry.grid(row=0, column=1)
tk.Label(test, text='l').grid(row=0, column=2)
test.mainloop()
bfm
User
Beiträge: 88
Registriert: Donnerstag 14. März 2013, 09:42

Hallo,

ich habe mir mal vor längerer Zeit eine von der ttk-Entry-Klasse abgeleitete eigene Klasse gebastelt. Es war mir zu nervig, immer wieder die gleichen Zeilen an Code zu kopieren, weil es Sachen gibt, die ich einfach in jedem Eingabefeld will. Enter/Return für Tab, maximale Anzahl Zeichen, Welche Zeichen dürfen eingegben werden? Prüfung Datum, Uhrzeit, Nummern, Dezimalen, Auto-Tab usw.

Von der Basis-Klasse leite ich mir dann andere Klassen für Datum, Uhrzeit, Nummern, Dezimale usw ab. Der Code ist zwar vielleicht nicht schön aber selten und tut was er soll. Heute würde ich vielleicht auch einiges anders machen. Man sammelt mit der Zeit seine Erfahrungen und außerdem ist noch kein Meister vom Himmel gefallen.

Hier ein Stück von dem Code: Die interessanten Stellen sind mit -> Kommentaren versehen

Code: Alles auswählen

class basic_entry(ttk.Entry):

    def __init__(self, *args, **kwargs):
        ttk.Entry.__init__(self, *args, **kwargs)
        self.entry_value = tkinter.StringVar()
        self.item_id = 'basic_entry'
        self.item_type = 'basic_entry'
        self.default_value = ''
        self.is_checked = False

	-> Command für Validation als Attribut, damit es jederzeit austauschbar ist
        self.validatecmd = self.register(self.isOkay)
        
        -> Config-Entry -> beim Drücken einer Taste wird die Validation ausgeführt
        self.config(textvariable=self.entry_value, width=10, validate='key', \
            validatecommand=(self.validatecmd, '%s', '%P', '%S', '%d'))
        
        self.bind('<Return>', self.value_check_event)
        self.bind('<KP_Enter>', self.value_check_event)
        self.bind('<Tab>', self.tab_pressed)
        self.bind('<KeyRelease>', self.check_auto_tab)
        self.bind('<FocusIn>', self.focus_in)
        #custom settings
        self.valid_values = ''
        
        -> diese Zeichen sollen zulässig sein
       self.valid_content = '1234567890.,'
        
        self.duplicate = False
        self.empty_allowed = True
        self.auto_tab = False
        self.max_length = 0
        self.justify_right = False
        if self.justify_right:
            self.config(justify='right')
        self.uppercase = False
        #print(self.configure())


    #Eingaben prüfen
    #True = Änderung erlaubt 
    #False = Änderung nicht erlaubt 
    
    -> Validation -> übergebene Werte sind: Wert in Entry vor der Änderung, Wert in Entry nach der Änderung (so würde der Wert aussehen, wenn die Änderung zulässig ist), Wert der Änderung selber, Grund
    def isOkay(self, before, after, change, reason):
        #Änderung des Inhalts is_checked = False
        self.is_checked = False
        #print('vorher: ', before, 'nachher: ', after, 'geaendert: ', change, 'Grund: ', reason)
        #print(type(reason))

        -> reason 1 = Änderung
        if reason=='1':   
            #etwas eingefügt
            #maximale Länge prüfen
            if self.max_length > 0:
                if len(after) > self.max_length:
                    return False
            #gültiger Inhalt leer
            if self.valid_content == '':
                return True

            #gültiger Inhalt prüfen
	   
	   -> Prüfung von Parameter "change" (%S = Wert der Änderung), ob im Attribut self.valid_content (=String) enthalten ist
            if change in self.valid_content:
                    return True
            else:
                return False
        else:
            #etwas gelöscht
            return True
Antworten