Tkinter'deki bir Button komutuna argümanlar nasıl aktarılır?


176

ButtonPython'da Tkinter ile aşağıdakileri yaptığımı varsayalım :

import Tkinter as Tk
win = Tk.Toplevel()
frame = Tk.Frame(master=win).grid(row=1, column=1)
button = Tk.Button(master=frame, text='press', command=action)

actionDüğmeye bastığımda yöntem çağrılıyor, ancak yönteme bazı argümanlar aktarmak istersem ne olur action?

Aşağıdaki kod ile denedim:

button = Tk.Button(master=frame, text='press', command=action(someNumber))

Bu sadece yöntemi hemen çağırır ve düğmeye basmak hiçbir şey yapmaz.


4
frame = Tk.Frame (master = win) .grid (satır = 1, sütun = 1) # S. şimdi çerçevenin değeri nedir?
noob oddy

Yanıtlar:


265

Şahsen lambdasböyle bir senaryoda kullanmayı tercih ediyorum , çünkü imo daha net ve daha basit ve aynı zamanda çağrılan yöntem üzerinde kontrolünüz yoksa çok fazla sarma yöntemi yazmaya zorlamıyor, ancak bu kesinlikle bir zevk meselesi.

Bunu bir lambda ile böyle yapardınız (fonksiyonel modülde bazı körükleme uygulamaları olduğunu unutmayın, böylece bunu da kullanabilirsiniz):

button = Tk.Button(master=frame, text='press', command= lambda: action(someNumber))

11
Hala sarmalayıcı yöntemler yazıyorsunuz, sadece satır içi yapıyorsunuz.
agf

54
SomeNumber aslında birçok döngü oluşturan bir döngü içindeki değerleri değiştiren bir değişkense bu çalışmaz . Ardından her düğme , button oluşturulduğunda sahip olduğu değere değil, someNumber'a atanmış son değere sahip action () öğesini çağırır . Kullanarak çözüm partialbu durumda çalışır.
Scrontch

1
Bu benim için harika çalıştı. Ancak, OP'lerin neden açıklandığını da açıklayabilir misiniz "This just invokes the method immediately, and pressing the button does nothing"?
Tommy

17
@Scrontch Bahsettiğiniz tuzakta kaç acemi Tkinter kullanıcısının hiç hissetmediğini merak ediyorum! Her halükarda bir yapabilirsiniz yakalamak deyim kullanarak geçerli değerini callback=lambda x=x: f(x)olduğu gibifs = [lambda x=x: x*2 for x in range(5)] ; print [f() for f in fs]
gboffi

1
@Voo yukarıda "eski okul python insanlar muhtemelen lambda için varsayılan parametre atamasına sadık olacak" ile ne demek istiyorsun? Lambda'yı çalıştıramadım ve şimdi kısmi kullandım.
Klamer Schutte

100

Bu aynı zamanda aşağıdaki gibi partialstandart kütüphane functoolsundan da yapılabilir :

from functools import partial
#(...)
action_with_arg = partial(action, arg)
button = Tk.Button(master=frame, text='press', command=action_with_arg)

34
Veya daha da kısa:button = Tk.Button(master=frame, text='press', command=partial(action, arg))
Klamer Schutte

1
Necro'ing için özür dilerim, ancak iki veya daha fazla argüman için bunu nasıl yapardın?
mashedpotatoes

6
action_with_args = partial(action, arg1, arg2... argN)
Dologan

1
Bu yaklaşımı benimserdim çünkü lambda benim için çalışmadı.
Zaven Zareyan

20

Örnek GUI:

Diyelim ki GUI'm var:

import tkinter as tk

root = tk.Tk()

btn = tk.Button(root, text="Press")
btn.pack()

root.mainloop()

Bir Düğmeye Basıldığında Ne Olur

btnBasıldığında , aşağıdaki örnekte çok benzer olan kendi işlevini çağırdığını görün button_press_handle:

def button_press_handle(callback=None):
    if callback:
        callback() # Where exactly the method assigned to btn['command'] is being callled

ile:

button_press_handle(btn['command'])

Sadece düşünebiliriz commandseçenek olarak ayarlanması gerekir istediğimiz yöntemine referans benzer çağrılacak callbackiçinde button_press_handle.


Düğmeye Basıldığında Yöntem Çağırma ( Geri Arama )

Tartışma olmadan

İstesem Yani eğer printbir şey düğmesi Sette gerekir basıldığında:

btn['command'] = print # default to print is new line

Çok dikkat edin eksikliği ait ()olan printbu anlam atlanırsa yönteme: "Bu basıldığında aramak istediğiniz yöntemin adıdır ama . Sadece bu çok anlık demiyorlar" Ancak, herhangi bir argüman iletmedim, printböylece argüman olmadan çağrıldığında yazdırılan her şeyi yazdırdı.

İle bağımsız değişken (ler)

Şimdi düğmeye basıldığında çağrılmak istediğim yönteme de argümanlar iletmek istersem lambda deyimi ile oluşturulabilen anonim işlevleri kullanabilirim, bu durumda printyerleşik yöntem için aşağıdaki gibi :

btn['command'] = lambda arg1="Hello", arg2=" ", arg3="World!" : print(arg1 + arg2 + arg3)

Düğmeye Basıldığında Birden Çok Yöntem Çağırma

Bağımsız Değişken Olmadan

Bunu kullanarak lambdaifade elde edebilirsiniz ama kötü uygulama olarak kabul edilir ve bu yüzden buraya dahil etmeyeceğim. İyi uygulama, multiple_methodsistenen yöntemleri çağıran ve daha sonra düğmeye basmanın geri çağrısı olarak ayarlanan ayrı bir yöntem tanımlamaktır :

def multiple_methods():
    print("Vicariously") # the first inner callback
    print("I") # another inner callback

İle bağımsız değişken (ler)

Diğer yöntemleri çağıran yönteme argüman (lar) ı aktarmak için, yine deyimden faydalanın lambda, ancak önce:

def multiple_methods(*args, **kwargs):
    print(args[0]) # the first inner callback
    print(kwargs['opt1']) # another inner callback

ve sonra ayarlayın:

btn['command'] = lambda arg="live", kw="as the" : a_new_method(arg, opt1=kw)

Geri Aramadan Nesneleri Geri Getirme

Ayrıca callback, returnsadece bunun aksine button_press_handleile içeride çağrıldığı için bu mümkün değildir . O does ama değil her yerde bu birim dışından. Bu nedenle , geçerli kapsamda erişilebilen nesneleri değiştirmeyi tercih etmelisiniz .callback()return callback()return


Global Nesne Değişiklikleri ile Komple Örnek

Aşağıdaki örnek btn, düğmeye her basıldığında metnini değiştiren bir yöntemi çağırır :

import tkinter as tk

i = 0
def text_mod():
    global i, btn           # btn can be omitted but not sure if should be
    txt = ("Vicariously", "I", "live", "as", "the", "whole", "world", "dies")
    btn['text'] = txt[i]    # the global object that is modified
    i = (i + 1) % len(txt)  # another global object that gets modified

root = tk.Tk()

btn = tk.Button(root, text="My Button")
btn['command'] = text_mod

btn.pack(fill='both', expand=True)

root.mainloop()

Ayna


10

Python'un işlev bağımsız değişkenleri için varsayılan değerler sağlama yeteneği bize bir çıkış yolu sağlar.

def fce(x=myX, y=myY):
    myFunction(x,y)
button = Tk.Button(mainWin, text='press', command=fce)

Bkz. Http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/extra-args.html

Daha fazla düğme için bir işlev döndüren bir işlev oluşturabilirsiniz:

def fce(myX, myY):
    def wrapper(x=myX, y=myY):
        pass
        pass
        pass
        return x+y
    return wrapper

button1 = Tk.Button(mainWin, text='press 1', command=fce(1,2))
button2 = Tk.Button(mainWin, text='press 2', command=fce(3,4))
button3 = Tk.Button(mainWin, text='press 3', command=fce(9,8))

4
Bu sorunu çözmez. Hepsi aynı işlevi çağıran ancak farklı argümanlar iletmesi gereken üç düğme oluşturuyorsanız ne olur?
Bryan Oakley

Bir işlev döndüren bir işlev oluşturabilirsiniz.
MarrekNožka

Bunun artık aktif olmadığını biliyorum ama buraya stackoverflow.com/questions/35616411/… adresinden bağlandım , bu lambda ifadelerini kullanmakla aynı şekilde çalışıyor, her düğme için aynı işlevi bir işlev tanımlayabilirsiniz. her düğme için bir lambda ifadesi.
Tadhg McDonald-Jensen

ilk kod örneğini sürekli değişen myXve myYmükemmel çalışan bir döngü içine koyarak çok teşekkür ederim.
Tadhg McDonald-Jensen

3

Matt Thompsons'ın cevabı üzerine inşa: bir sınıf, fonksiyon yerine kullanılabilecek şekilde çağrılabilir hale getirilebilir:

import tkinter as tk

class Callback:
    def __init__(self, func, *args, **kwargs):
        self.func = func
        self.args = args
        self.kwargs = kwargs
    def __call__(self):
        self.func(*self.args, **self.kwargs)

def default_callback(t):
    print("Button '{}' pressed.".format(t))

root = tk.Tk()

buttons = ["A", "B", "C"]

for i, b in enumerate(buttons):
    tk.Button(root, text=b, command=Callback(default_callback, b)).grid(row=i, column=0)

tk.mainloop()

2

Yöntemi hemen çağırmasının ve düğmeye basmanın hiçbir şey yapmaması action(somenumber), değerlendirilmesinin ve dönüş değerinin düğmeye komut olarak atfedilmesidir. Bu nedenle, çalışıp actiongeri döndüğünü söylemek için bir şey yazdırırsa None, sadece actiondönüş değerini değerlendirmek Noneve düğmeye komut olarak vermek için çalışırsınız .

Farklı argümanlara sahip işlevleri çağırmak için düğmelere sahip olmak için, bunu öneremememe rağmen genel değişkenleri kullanabilirsiniz:

import Tkinter as Tk

frame = Tk.Frame(width=5, height=2, bd=1, relief=Tk.SUNKEN)
frame.grid(row=2,column=2)
frame.pack(fill=Tk.X, padx=5, pady=5)
def action():
    global output
    global variable
    output.insert(Tk.END,variable.get())
button = Tk.Button(master=frame, text='press', command=action)
button.pack()
variable = Tk.Entry(master=frame)
variable.pack()
output = Tk.Text(master=frame)
output.pack()

if __name__ == '__main__':
    Tk.mainloop()

Ne yapacağım bir classnesneleri gereken her değişken ve bu gerektiği gibi değiştirmek için yöntemler içerecek yapmaktır :

import Tkinter as Tk
class Window:
    def __init__(self):
        self.frame = Tk.Frame(width=5, height=2, bd=1, relief=Tk.SUNKEN)
        self.frame.grid(row=2,column=2)
        self.frame.pack(fill=Tk.X, padx=5, pady=5)

        self.button = Tk.Button(master=self.frame, text='press', command=self.action)
        self.button.pack()

        self.variable = Tk.Entry(master=self.frame)
        self.variable.pack()

        self.output = Tk.Text(master=self.frame)
        self.output.pack()

    def action(self):
        self.output.insert(Tk.END,self.variable.get())

if __name__ == '__main__':
    window = Window()
    Tk.mainloop()

2
button = Tk.Button(master=frame, text='press', command=lambda: action(someNumber))

Bunu düzeltmek gerektiğine inanıyorum


2

Yapılacak en iyi şey lambda'yı aşağıdaki gibi kullanmaktır:

button = Tk.Button(master=frame, text='press', command=lambda: action(someNumber))

2

Çok geç kaldım, ama burada bunu başarmanın çok basit bir yolu var.

import tkinter as tk
def function1(param1, param2):
    print(str(param1) + str(param2))

var1 = "Hello "
var2 = "World!"
def function2():
    function1(var1, var2)

root = tk.Tk()

myButton = tk.Button(root, text="Button", command=function2)
root.mainloop()

Kullanmak istediğiniz işlevi başka bir işlevde sararsınız ve düğmeye basıldığında ikinci işlevi çağırırsınız.


Kodunuz koyduğu var1ve var2ana modülde olduğu gibi, function1hiç atlayabilir ve printdeyimi koyabilirsiniz function2. Anlamadığım diğer durumlardan bahsediyor musunuz?
Brom

2

Lambdas hepsi iyi ve iyi, ama bunu da deneyebilirsiniz (bir döngü btw için çalışır):

root = Tk()

dct = {"1": [*args], "2": [*args]}
def keypress(event):
    *args = dct[event.char]
    for arg in args:
        pass
for i in range(10):
    root.bind(str(i), keypress)

Bağlama ayarlandığında, bir tuşa basma olayı bağımsız değişken olarak geçirdiği için çalışır. Daha sonra olayı event.char"1" veya "UP" vb almak gibi öznitelikleri çağırabilirsiniz . Olay özelliklerinden başka bir bağımsız değişkene veya birden fazla bağımsız değişkene ihtiyacınız varsa. saklamak için bir sözlük oluşturmanız yeterlidir.


2

Bu sorunla daha önce de karşılaştım. Sadece lambda kullanabilirsiniz:

button = Tk.Button(master=frame, text='press',command=lambda: action(someNumber))

1

Bu şekilde gerçekleştirilecek daha fazla eyleminiz varsa giriş verilerini komut işlevine iletmek için bir lambda kullanın (Genel olarak yapmaya çalıştım, bu yüzden sadece uyarlayın):

event1 = Entry(master)
button1 = Button(master, text="OK", command=lambda: test_event(event1.get()))

def test_event(event_text):
    if not event_text:
        print("Nothing entered")
    else:
        print(str(event_text))
        #  do stuff

Bu, olaydaki bilgileri düğme işlevine iletir. Bunu yazmanın daha fazla Pythonesque yolu olabilir, ama benim için çalışıyor.


1

JasonPy - birkaç şey ...

bir döngüye bir düğme yapıştırırsanız, tekrar tekrar oluşturulacaktır ... bu muhtemelen istediğiniz şey değildir. (belki de) ...

Her zaman son dizini almasının nedeni, program başladığında değil, tıklattığınızda çalıştırılan lambda olaylarıdır. Yaptığınız şeyden% 100 emin değilim ama belki değeri yapıldığında saklamayı deneyin, sonra lambda düğmesi ile daha sonra arayın.

örneğin: (bu kodu kullanmayın, sadece bir örnek)

for entry in stuff_that_is_happening:
    value_store[entry] = stuff_that_is_happening

o zaman diyebilirsiniz ....

button... command: lambda: value_store[1]

Bu yardımcı olur umarım!


1

Bir basit bir yolu yapılandırmak olacaktır buttonile lambdaşu sözdizimi gibi:

button['command'] = lambda arg1 = local_var1, arg2 = local_var2 : function(arg1, arg2)

1

Gelecek nesiller için: benzer bir şey elde etmek için sınıfları da kullanabilirsiniz. Örneğin:

class Function_Wrapper():
    def __init__(self, x, y, z):
        self.x, self.y, self.z = x, y, z
    def func(self):
        return self.x + self.y + self.z # execute function

Düğme daha sonra şu şekilde oluşturulabilir:

instance1 = Function_Wrapper(x, y, z)
button1  = Button(master, text = "press", command = instance1.func)

Bu yaklaşım ayrıca işlev bağımsız değişkenlerini ayarlayarak değiştirmenizi sağlar instance1.x = 3.


1

Kullanmalısın lambda:

button = Tk.Button(master=frame, text='press', command=lambda: action(someNumber))

1

Lambda kullan

import tkinter as tk

root = tk.Tk()
def go(text):
    print(text)

b = tk.Button(root, text="Click", command=lambda: go("hello"))
b.pack()
root.mainloop()

çıktı:

hello
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.