Python – întoarcerea de la un callback Tkinter (Programare, Python, Callback, Tkinter, Return)

Benjamin Hodgson a intrebat.
a intrebat.

Cum pot obține un obiect returnat de la o funcție care este executată ca un callback Tkinter?

import Tkinter as Tk
from functools import partial

def square(x):
    return x*x

root = Tk.Tk()
var = Tk.IntVar(root, value=0) #the variable the gets passed to the class call
menu = Tk.OptionMenu(root, var, *[0,1,2,3,4,5]) #a drop-down list to choose a value for the variable
menu.pack()
button = Tk.Button(root, text='click', command = partial(square,var.get())) #a button that calls the class
button.pack()
root.mainloop()

Evident, acesta este un exemplu simplificat. În realitate, funcția apelată de buton va returna obiecte, pe care doresc să le adaug la o listă de obiecte care vor fi păstrate în spațiul principal de nume Python pentru operațiuni ulterioare.

Oricum, aici utilizatorul poate alege un argument pentru funcție folosind o interfață grafică și poate apăsa un buton care va executa funcția. Cu toate acestea, valoarea de retur a funcției pare condamnată să se piardă în eter, deoarece callback-ul nu acceptă returnări. Poate fi depășit acest aspect fără a se utiliza un element urât global în definiția funcției square(x)?

Comentarii

  • De fapt, cred că apelul la var.get() în interiorul funcției partial ar putea să nu funcționeze, deoarece va fi evaluată atunci când butonul este creat, nu atunci când se face clic pe el. Totuși, nu lăsați acest lucru să tulbure problema – întrebarea mea se referă de fapt la întoarcerea de la square(x) în interiorul butonului. –  > Por Benjamin Hodgson.
  • Puteți să ocoliți această problemă înlocuind apelul către partial cu un lambda funcție: command = lambda x=var.get(): square(x). De asemenea, puteți configura o funcție var.trace(). –  > Por Benjamin Hodgson.
3 răspunsuri
Bryan Oakley

Noțiunea de „returnare” a valorilor din callback-uri nu are sens în contextul unui program condus de evenimente. Callback-urile sunt apelate ca rezultat al unui eveniment, deci nu există niciun loc unde să se returneze o valoare.

Ca regulă generală, callback-urile dvs. ar trebui să apeleze întotdeauna o funcție, mai degrabă decât să utilizeze functools.partial sau lambda. Cele două sunt bune atunci când sunt necesare, dar dacă folosiți un stil de codare orientat pe obiecte, acestea sunt adesea inutile și duc la un cod care este mai dificil de întreținut decât este necesar.

De exemplu:

def compute():
    value = var.get()
    result = square(value)
    list_of_results.append(result)

button = Tk.Button(root, text='click', command = compute)
...

Acest lucru devine mult mai ușor și puteți evita variabilele globale dacă vă creați aplicația ca o clasă:

class App(...):
    ...
    def compute():
        ...
        result = self.square(self.var.get())
        self.results.append(result)

Comentarii

  • list_of_results.append() linie din primul dvs. exemplu mă face să mă simt un pic stingherit. Mi se pare ciudat ca o funcție să modifice variabile din afara domeniului său de aplicare, indiferent dacă o numiți sau nu în mod explicit o global variabilă. Implementarea întregului program ca o clasă este totuși o idee bună. –  > Por Benjamin Hodgson.
  • @poorsod: Nu există altă opțiune decât să modifice variabilele din afara domeniului său local. Acesta este modul în care funcționează interfețele grafice. După cum am spus, callback-urile nu pot returna valori apelantului lor, deoarece nu au un apelant (în afară de bucla de eveniment). Cu toate acestea, când vă gândiți că callback-ul se întâmplă „în domeniul de aplicare al obiectului aplicației”, are mai mult sens. Callback-ul nu face decât să modifice un atribut al propriului său obiect. –  > Por Bryan Oakley.
ZeroFunter

Îmi pare rău că am întârziat 6 ani, dar de curând am descoperit o modalitate bună de a face acest lucru fără a face codul dezordonat și greu de întreținut.Este cam ceea ce a spus DaveTheScientist, dar vreau doar să dezvolt puțin.De obicei, în Tkinter, dacă doriți ca un buton să apeleze o funcție, faceți următoarele:

exampleButton = Button(root, text = 'Example', command = someFunc)

Aceasta va apela pur și simplu someFunc de fiecare dată când butonul este apăsat. Dacă această funcție, totuși, acceptă argumente, trebuie să folosiți lambdas și să faceți ceva de genul acesta:

exampleButton = Button(root, text = 'Example', command = lambda: someFunc(arg1, arg2))

Linia de cod de mai sus va rula someFunc și va utiliza variabilele arg1 și arg2 ca argumente pentru această funcție. Acum, ceea ce ați putea face într-un program în care, de multe ori, ați avea nevoie ca funcțiile executate de butoane să returneze valori, este să creați o nouă funcție care să fie apelată de fiecare buton.

Această funcție primește ca prim argument funcția pe care doriți ca butonul să o execute, iar apoi argumentele funcției respective.

def buttonpress(function, *args):
    value = function(*args)

Apoi, atunci când creați butonul, faceți următoarele:

exampleButton = Button(root, text = 'Example', command = lambda: buttonpress( someFunc, arg1, arg2 ))

Aceasta va rula funcția dată (în acest caz, someFunc) și va stoca valoarea returnată în câmpul value variabilă. De asemenea, are avantajul că puteți utiliza oricâte argumente doriți pentru funcția pe care o execută butonul dumneavoastră.

DaveTheScientist

Doar creați o funcție reală care să fie apelată de butonul dvs., în loc să puneți totul în linie așa.

button=Tk.Button(parent, text='click', command=someFxn)

def someFxn(): your code

Apoi, în funcția ta, apelează doar var.get(), fă calculele și apoi fă ceva cu valoarea.