Entryfeld auslesen mit .get()

Fragen zu Tkinter.
Antworten
Kahnbein.Kai
User
Beiträge: 104
Registriert: Mittwoch 24. Juni 2015, 14:12
Wohnort: Bochum

Hallo,
ich versuche verzweifelt ein Entryfeld mit der Methode .get() auszulesen. Es klappt leider nicht.
Die Fehlermeldung:

Code: Alles auswählen

kai@Kai-Desktop:~/Dokumente/Python/Interface$ python3 bsp.py
Exception in Tkinter callback
Traceback (most recent call last):
  File "/usr/lib/python3.6/tkinter/__init__.py", line 1705, in __call__
    return self.func(*args)
  File "bsp.py", line 9, in berechnung
    print(eingabe1.get())
AttributeError: 'NoneType' object has no attribute 'get'
Hier ist der Code

Code: Alles auswählen

import tkinter as tk
master = tk.Tk()
master.title("Dreisatz berechnen")


eingabe1 = tk.IntVar(master, 4)

def berechnung():
    print(eingabe1.get())

eingabe1 = tk.Entry(master, textvariable=eingabe1).grid(row=1,column=0)

tk.Button(master, text="Berechnung", command=berechnung).grid(row=3, column=0)

tk.mainloop()
Wenn ich die Zeile "print(eingabe1.get())" außerhalb der Funktion schreibe, wird die Zahl 4 richtig ausgeben. Warum funktioniert das nicht in Verbindung mit der Funktion ?

Gruß Kai
Kahnbein.Kai
User
Beiträge: 104
Registriert: Mittwoch 24. Juni 2015, 14:12
Wohnort: Bochum

Ha, wie der Ochs vorm Berg, ich habe die Variable "Eingabe1" hinterher wieder überschrieben :(
Peinlich....

Wenn man den Variablennamen des Entryfeldes ändert klappt es :)



Kann also gelöscht werden :)
Benutzeravatar
__blackjack__
User
Beiträge: 14085
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Kahnbein.Kai: Den Namen an den das `Entry`-Objekt gebunden wird braucht man nicht ändern, das kann man ganz einfach an *gar keinen Namen* binden. Machst Du bei dem `Button`-Objekt ja auch nicht.

Das hat da auf Modulebene alles nix zu suchen und für jede nicht-triviale GUI braucht man mindestens eine eigene Klasse. Hier könnte man bis hier hin noch mit `funtools.partial()` auskommen, damit `berechne()` das `IntVar`-Objekt als Argument übergeben bekommt.

Namen nummerieren ist bäh.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Sirius3
User
Beiträge: 18289
Registriert: Sonntag 21. Oktober 2012, 17:20

Also so:

Code: Alles auswählen

import tkinter as tk
from functools import partial

def berechnung(eingabe):
    print(eingabe.get())

def main():
    master = tk.Tk()
    master.title("Dreisatz berechnen")
    eingabe = tk.IntVar(master, 4)
    tk.Entry(master, textvariable=eingabe).grid(row=1, column=0)
    tk.Button(master, text="Berechnung", command=partial(berechnung, eingabe)).grid(row=2, column=0)
    master.mainloop()

if __name__ == "__main__":
    main()
Kahnbein.Kai
User
Beiträge: 104
Registriert: Mittwoch 24. Juni 2015, 14:12
Wohnort: Bochum

Vielen Danke für die Aufklärung.

Ich sollte mir mal eine "leere" Pyrhondatei mit der der main() Klasse und der __init__ Anweisung speichern. Im Internet sind die Beispiele immer ohne zu finden. Das mit der partial Funktion ist so eine Sache, wenn man gut Programmieren kann, ist das sicherlich der schönere und besser weg. Als Anfänger jedoch würde ich ein eher aufgeschlüsselten Code bevorzugen, zwecks Nachvollziehbarkeit.

Gruß und schönes Wochenende
Kai
Sirius3
User
Beiträge: 18289
Registriert: Sonntag 21. Oktober 2012, 17:20

Oft findet man halt nur Programmfragmente, die ein bestimmtes Problem lösen sollen. Da ist dann eine main-Funktion überflüssig, weil es in größeren Code eingebaut werden muss.
Nicht die partial-Funktion ist komplex, sondern Callback-Funktionen. Und jede andere Art der Parameterübergabe ist für Anfänger und Fortgeschrittene weniger nachvollziehbar.
Es sei denn, man verwendet eine Klasse und vermeidet so die Parameter ganz.
Benutzeravatar
__blackjack__
User
Beiträge: 14085
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Um GUIs zu programmieren, oder allgemein irgend etwas das Rückruffunktionen verwendet, muss man entweder Closures oder Klassen verstanden haben. Falls man sich über Aufrufe hinweg was merken will/muss, dann sind es Klassen. Seit dem Python ``nonlocal`` kennt, gingen hier auch Closures, aber das habe ich noch nie irgendwo jemanden machen sehen, und es würde IMHO auch gegen „There should be one-- and preferably only one --obvious way to do it.“ verstossen.

Und Closures oder Klassen zu lernen ist IMHO auch nicht so wirklich eine Wahl denn man lernt beides besser wenn man auch das jeweils andere Konzept kennt und versteht wie man Klassen mit Closures umsetzen kann und Closures mit Klassen.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Kahnbein.Kai
User
Beiträge: 104
Registriert: Mittwoch 24. Juni 2015, 14:12
Wohnort: Bochum

ok, ab jetzt immer mit main Funktion ;)

Das Programm läuft soweit mit globalen Variablen. Ich möchte es mit lokalen Variablen (außer ausgabe1) zum laufen bekommen, das klappt leider nicht.
Das Ausgabenlabel wird nicht aktualisiert.

Hier ist der Code:

Code: Alles auswählen

import tkinter as tk

master = tk.Tk()
master.title("Dreisatz berechnen")


ausgabe1 = tk.IntVar()

def berechnung(e1,e2,e3,wahl):
    if wahl.get()== 1:
        ausgabe1.set((e3.get()*e2.get())/e1.get())
        print(e1.get())
    else:
        ausgabe1.set((e2.get()*e1.get())/e3.get())
    return ausgabe1

def main():

    wahl = tk.IntVar()
    eingabe1 = tk.IntVar(master,10)
    eingabe2 = tk.IntVar(master,100)
    eingabe3 = tk.IntVar(master,20)

    tk.Radiobutton(master, text="Proportional", padx = 20, variable=wahl,value=1).grid(row=0,column=0, columnspan=2)
    tk.Radiobutton(master, text="Antiproportional",padx = 20, variable=wahl, value=2).grid(row=0,column=2, columnspan=1)

    tk.Button(master, text="Berechnung", command=berechnung(eingabe1,eingabe2,eingabe3,wahl)).grid(row=3, column=0)   

    tk.Entry(master, textvariable=eingabe1).grid(row=1,column=0)
    tk.Label(master, text="=").grid(row=1,column=1)
    tk.Entry(master, textvariable=eingabe2).grid(row=1,column=2)

    tk.Entry(master, textvariable=eingabe3).grid(row=2,column=0)
    tk.Label(master, text="=").grid(row=2,column=1)
    tk.Label(master, textvariable=ausgabe1).grid(row=2, column=2)

    master.mainloop()
    
if __name__ == "__main__":
	main()
Hier ist ein Bild der Maske mit den verschiedenen Felder:
Bild

Gruß Kai
Sirius3
User
Beiträge: 18289
Registriert: Sonntag 21. Oktober 2012, 17:20

Ich hatte Dir doch schon geschrieben, dass Du functools.partial brauchst.

Code: Alles auswählen

import tkinter as tk
from functools import partial

def berechnung(ausgabe, e1, e2, e3, wahl):
    if wahl.get() == 1:
        ausgabe.set((e3.get()*e2.get())/e1.get())
        print(e1.get())
    else:
        ausgabe.set((e2.get()*e1.get())/e3.get())

def main():
    master = tk.Tk()
    master.title("Dreisatz berechnen")
    ausgabe = tk.IntVar(master)
    wahl = tk.IntVar(master)
    eingabe1 = tk.IntVar(master, 10)
    eingabe2 = tk.IntVar(master, 100)
    eingabe3 = tk.IntVar(master, 20)
    tk.Radiobutton(master, text="Proportional", padx=20, variable=wahl,value=1).grid(row=0,column=0, columnspan=2)
    tk.Radiobutton(master, text="Antiproportional",padx=20, variable=wahl, value=2).grid(row=0,column=2, columnspan=1)
    tk.Button(master, text="Berechnung", command=partial(berechnung, ausgabe, eingabe1,eingabe2,eingabe3,wahl)).grid(row=3, column=0)   
    tk.Entry(master, textvariable=eingabe1).grid(row=1,column=0)
    tk.Label(master, text="=").grid(row=1,column=1)
    tk.Entry(master, textvariable=eingabe2).grid(row=1,column=2)
    tk.Entry(master, textvariable=eingabe3).grid(row=2,column=0)
    tk.Label(master, text="=").grid(row=2,column=1)
    tk.Label(master, textvariable=ausgabe).grid(row=2, column=2)
    master.mainloop()
    
if __name__ == "__main__":
    main()
Kahnbein.Kai
User
Beiträge: 104
Registriert: Mittwoch 24. Juni 2015, 14:12
Wohnort: Bochum

Ok, super, jetzt klappt es.

Jetzt bin ich verwirrt. Warum bekommt die Funktion 'berechnung()' das 'IntVar' Argument nur mit partial übergeben ? Die Dokus die ich zu partial finde, beschreiben immer "fehlende" Argumente die man hinterher hinzufügen kann. Ich kann doch benötigten Argumente sofort übergeben.

Warum werden denn in Klassen Parameter ganz vermieden ? Die gibt es doch da auch, oder verstehe ich das falsch ?

Gruß Kai
Kahnbein.Kai
User
Beiträge: 104
Registriert: Mittwoch 24. Juni 2015, 14:12
Wohnort: Bochum

Ich habe es nochmal umgeschrieben mit einer Klasse.
Sieht echt besser aus, ihr habt recht.

Gruß Kai

Code: Alles auswählen

import tkinter as tk


class Dreisatz(tk.Tk):

    def __init__(self):
        super().__init__()
        self.eingabe1 = tk.IntVar(self)
        self.eingabe2 = tk.IntVar(self)
        self.eingabe3 = tk.IntVar(self)
        self.wahl = tk.IntVar(self)
        self.ausgabe1 = tk.IntVar(self)
    
 
        self.title("Dreisatz berechnen")

        tk.Radiobutton(self, text="Proportional", padx = 20, variable=self.wahl,value=1).grid(row=0,column=0)
        tk.Radiobutton(self, text="Antiproportional",padx = 20, variable=self.wahl, value=2).grid(row=0,column=2)

        tk.Button(self, text="Berechnung", command=self.berechnung).grid(row=3, column=0, columnspan = 3)   

        tk.Entry(self, textvariable=self.eingabe1).grid(row=1,column=0)
        tk.Label(self, text="=").grid(row=1,column=1)
        tk.Entry(self, textvariable=self.eingabe2).grid(row=1,column=2)

        tk.Entry(self, textvariable=self.eingabe3).grid(row=2,column=0)
        tk.Label(self, text="=").grid(row=2,column=1)
        tk.Label(self, textvariable=self.ausgabe1).grid(row=2, column=2)

    def berechnung(self):
        if self.wahl.get()== 1:
            self.ausgabe1.set((self.eingabe3.get()*self.eingabe2.get())/self.eingabe1.get())
        else:
            self.ausgabe1.set((self.eingabe2.get()*self.eingabe1.get())/self.eingabe3.get())
  
def main():
    master = Dreisatz()
    master.mainloop()
    
    
if __name__ == "__main__":
    main()
Sirius3
User
Beiträge: 18289
Registriert: Sonntag 21. Oktober 2012, 17:20

Callbacks brauchen eine Funktion ohne Parameter, `berechnung` hat aber 5 davon. Daher muß man mit partial eine Funktion definieren, deren 5 Parameter mit Werten vorbelegt sind.

Das ganze mit Klasse:

Code: Alles auswählen

import tkinter as tk

class Fenster:
    def __init__(self):
        master = tk.Tk()
        master.title("Dreisatz berechnen")
        self.master = master
        self.ausgabe = tk.IntVar(master)
        self.wahl = tk.IntVar(master)
        self.eingabe1 = tk.IntVar(master, 10)
        self.eingabe2 = tk.IntVar(master, 100)
        self.eingabe3 = tk.IntVar(master, 20)
        tk.Radiobutton(master, text="Proportional", padx=20, variable=self.wahl,value=1).grid(row=0, column=0, columnspan=2)
        tk.Radiobutton(master, text="Antiproportional", padx=20, variable=self.wahl, value=2).grid(row=0, column=2, columnspan=1)
        tk.Button(master, text="Berechnung", command=self.berechnung).grid(row=3, column=0)   
        tk.Entry(master, textvariable=self.eingabe1).grid(row=1, column=0)
        tk.Label(master, text="=").grid(row=1, column=1)
        tk.Entry(master, textvariable=self.eingabe2).grid(row=1, column=2)
        tk.Entry(master, textvariable=self.eingabe3).grid(row=2, column=0)
        tk.Label(master, text="=").grid(row=2, column=1)
        tk.Label(master, textvariable=self.ausgabe).grid(row=2, column=2)

    def mainloop():    
        self.master.mainloop()

    def berechnung(self):
        if self.wahl.get() == 1:
            self.ausgabe.set((self.eingabe3.get()*self.eingabe2.get())/self.eingabe1.get())
        else:
            self.ausgabe.set((self.eingabe2.get()*self.eingabe1.get())/self.eingabe3.get())

def main():
    fenster = Fenster()
    fenster.mainloop()

if __name__ == "__main__":
    main()
Kahnbein.Kai
User
Beiträge: 104
Registriert: Mittwoch 24. Juni 2015, 14:12
Wohnort: Bochum

Danke, ich glaube ich habe es jetzt ein wenig mehr verstanden. :)

Schönen Abend noch !
Antworten