Parameterübergabe in TKinter?

Fragen zu Tkinter.
Antworten
xturbo77
User
Beiträge: 39
Registriert: Montag 9. September 2002, 20:05
Kontaktdaten:

Ich kenn mich mit GUI Programmierung (Java/Swing) zwar etwas aus, aber bei TKinter ist mir so einiges noch sehr schleierhaft :oops:

Kann mir bitte jemand mal erklären wie ich einer Funktion, die ich über einen Button aufrufen möchte Parameter übergeben kann?

Folgendes Beispiel:

Code: Alles auswählen

from Tkinter import *

def say_hi():
    name = entry.get()
    print "Hi, ", name

root = Tk()
    
frame = Frame(root)
frame.pack()

entry = Entry(frame)
entry.pack()

button = Button(frame, text="QUIT", fg="red", command=frame.quit)
button.pack(side=LEFT)

hi_there = Button(frame, text="Hello", command=say_hi)
hi_there.pack(side=LEFT)

root.mainloop()
Der Button soll die Funktion say_hi aufrufen mit einem Paramter name. Normalerweise würde ich etwas wie folgendes erwarten:

Code: Alles auswählen

def say_hi(name):
    print "Hi, ", name

Code: Alles auswählen

hi_there = Button(frame, text="Hello", command=say_hi("Hugo"))
Wie kann ich unter TKinter so ein Konstrukt bewerkstelligen???

Das obige Beispiel funktioniert ja nur deshalb weil der Name entry (das Eingabefeld) global ist.
Folgendes z.b. würde ja nichtmehr funktionieren:

Code: Alles auswählen

from Tkinter import *

def say_hi():
    name = entry.get()
    print "Hi, ", name

def body(master):
    
    frame = Frame(master)
    frame.pack()

    entry = Entry(frame)
    entry.pack()

    button = Button(frame, text="QUIT", fg="red", command=frame.quit)
    button.pack(side=LEFT)

    hi_there = Button(frame, text="Hello", command=say_hi)
    hi_there.pack(side=LEFT)

root = Tk()
body(root)
root.mainloop()
Jetzt ist entry nichtmehr global sondern nur innerhalb der body Funktion verfügbar. Es kann doch nicht sein dass ich in diesem Fall gezwungen bin entry global zu definieren, oder?!
Voges
User
Beiträge: 564
Registriert: Dienstag 6. August 2002, 14:52
Wohnort: Region Hannover

Hallo!
xturbo77 hat geschrieben:

Code: Alles auswählen

hi_there = Button(frame, text="Hello", command=say_hi("Hugo"))
Wie kann ich unter TKinter so ein Konstrukt bewerkstelligen???
Darüber stolpert irgendwann jeder Tkinter-Newbie. Warum obiges nicht geht, ist klar? say_hi() wird nur ein einziges mal ausgeführt und zwar wenn Button() ausgeführt wird. Und da say_hi() None zurückgibt, kann man auch gleich command=None schreiben ;-)

Das berühmt berüchtigte lambda ist die Lösung:

Code: Alles auswählen

def say_hi(name):
    print "Hi, ", name
...
    hi_there = Button(frame, text="Hello", command= lambda: say_hi(entry.get()))
Jan
xturbo77
User
Beiträge: 39
Registriert: Montag 9. September 2002, 20:05
Kontaktdaten:

Thx!
Ich frage mich warum man diesen Hinweis in keinem Tkinter Tutorial findet?!?! :cry:
Gast

das wollte ich auch schon lange mal wissen.....

nur: was ist lambda genau? kannst du das erklären? und warum es ohne net geht ist mir auch net wirklich klar, aber vielleicht sitze ich grad auf der...... :D

mfg

roland
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

Hi rolgal,

lambda ist eine namenlose Funktion und die kannst Du überall verwenden, wo du sonst eine Funktion einsetzen würdest.

Beispiel:

Code: Alles auswählen

>>> def f_add(a,b):
...     return a + b
... 
>>> l_add = lambda a, b: a+b
>>> f_add(5,7)
12
>>> l_add(5,7)
12
lambda kann andere Funktionen aufrufen und so wie in eurem Beispiel als Wrapper für eine Funktion dienen. Heufig wird lambda auch bei map() und reduce() verwendet, man spart sich da dann eine extra Funktion zu definieren.
Beispiel:

Code: Alles auswählen

>>> liste = range(1,10)
>>> reduce(lambda a, b: a * b - b, liste)
-260649
>>> def funktion(a, b): return a*b-b
... 
>>> reduce(funktion, liste)
-260649

Gruß

Dookie
Gast

cooles teil diese lambda funktion,

danke dir wiedermal für deine anschauliche erläuterung,

mfg

roland
Gast

cooles teil diese lambda funktion,

danke dir wiedermal für deine anschauliche erläuterung,

mfg

roland
lbuega
User
Beiträge: 75
Registriert: Dienstag 15. April 2003, 08:51
Wohnort: Weissach

Hallo Leute,
hab diese Beiträge dank der "Suche"-Funktion gefunden. Hat mir viel gebracht. Vor allem das Beispiel von Voges mit dem Aufruf einer Funktion in einer Funktion in lambda: :o
Voges hat geschrieben: Das berühmt berüchtigte lambda ist die Lösung:

Code: Alles auswählen

def say_hi(name):
    print "Hi, ", name
...
    hi_there = Button(frame, text="Hello", command= lambda: say_hi(entry.get()))
Jan
Super Sache, vielen Dank! :D
Teerwalze
User
Beiträge: 1
Registriert: Freitag 25. Oktober 2019, 12:46

*PUSH* Beitrag ist 15 Jahre alt aber für mich gerade "präsent"

Ich bin ein Python-Anfänger und beschäftige mich gerade mit Tkinter. Habe ein GUI gebaut mit Labels und diversen (vielen) Eingabefeldern, deren Werte ich nun schlank auslesen will.. Mein Code ist analog zu oben und ich zerbreche mir gerade den Kopf, warum ich als Command beim Klick auf den "Auswerten" (im Beispiel "hi_there" Button, bei mir heißt er "Erfassen") nicht einfach die Auswertungs-Formel ("say_hi" bzw. "fetch(entries)") aufrufen kann sondern mit Lamda arbeiten muss.

Ohne Lambda liefert "say_hi" nix zurück, aber mit Lambda passt alles. Warum ist das so? Ich komme einfach nicht drauf.

Viele Grüße,
Franzi

***

Mein Code sieht wie folgt aus:

Code: Alles auswählen

fields = 'Bereich', 'relevante Zelle', 'Codewort 1', 'Farbe 1', 'Codewort2', 'Farbe 2', 'Codewort 3', 'Farbe 3'

def fetch(entries):
   print(entries)
   for entry in entries:
      field = entry[0]
      text  = entry[1].get()
      print('%s: "%s"' % (field, text)) 
   #fenster.destroy;

def makeform_grid(fenster, fields):
    entries=[]
    i = 0
    for field in fields:
        print(field)
        
        if i == 0 or i == len(field)-1:
            lab = Label(fenster, text=field, pady=20)
                
        else:
            lab = Label(fenster, text=field, padx=0)
                
        if i %2 == 0:
            ent = Entry(fenster)
            lab.grid(row=i, column=0)
            ent.grid(row=i, column=1)
            entries.append((fields[i],ent))
            
        if i % 2 != 0:
            lab = Label(fenster, text=field, padx=0)
            ent = Entry(fenster)
            lab.grid(row=i-1,column=4)
            ent.grid(row=i-1,column=5)
            entries.append((fields[i],ent))
            
        print(i)
        i = i +1
    return entries;

fenster = Tk()
ents = makeform_grid(fenster,fields)

print(ents)
        
Button(fenster, text="Quit", command=fenster.destroy).grid(row=12,column=0,pady=20)
b1 = Button(fenster, text="Erfassen", command=(lambda e=ents : fetch(e)))
b1.grid(row=12,column=1)
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

`command` braucht ein Funktionsobjekt und nicht den Rückgabewert des Funktionsaufrufs. `partial` wäre hier klarer, statt `lambda`.

Zum Code: Eingerückt wird immer mit 4 Leerzeichen pro Ebene, nicht mal 3 und mal 4. Wenn man Tuple in einer Liste hat, dann kann man sie per Unpacking beim for-Aufruf auch gleich in zwei Variablen speichern.
Konstanten schreibt man per Konvention groß, also `FIELDS`.
Wenn man einen Index zum Zählen braucht, gibt es `enumerate`. Wenn eine if-Bedingung genau das Gegenteil der vorherigen ist, dann kann man `else:` benutzen. Die Code-Dopplungen solltest Du aber besser vermeiden und mehr mit Variablen arbeiten.

Die ; sind unnötig.
Statt `from tkinter import *` solltest Du am Anfang `import tkinter as tk` stehen haben und alle Namen per tk.xyz ansprechen.
Alles ab `fenster` gehört in eine main-Funktion.

Benutze keine Abkürzungen als Variablenamen.

Code: Alles auswählen

import tkinter as tk
from functools import partial

FIELDS = [
    'Bereich', 'relevante Zelle',
    'Codewort 1', 'Farbe 1',
    'Codewort2', 'Farbe 2',
    'Codewort 3', 'Farbe 3'
]

def fetch(entries):
    print(entries)
    for field, text in entries:
       print('%s: "%s"' % (field, text.get())) 


def makeform_grid(fenster, fields):
    entries=[]
    for i, field in enumerate(fields):
        row, column = divmod(i, 2)
        tk.Label(fenster, text=field).grid(row=row, column=column*2)
        entry = tk.Entry(fenster)
        entry.grid(row=row, column=column*2+1)
        entries.append((field, entry))
    return entries


def main():
    fenster = tk.Tk()
    entries = makeform_grid(fenster, FIELDS)
    tk.Button(fenster, text="Quit", command=fenster.destroy).grid(row=12,column=0,pady=20)
    tk.Button(fenster, text="Erfassen", command=partial(fetch, entries)).grid(row=12,column=1)
    fenster.mainloop()

if __name__ == '__main__':
    main()
Antworten