lambda i funkcja z listy

0

Witam
Wymyśliłem sobie, że parametry dla "Buttonów" będą w liście. Jednym z parametrów będzie funkcja dla bind butona.
Jako parametr chcę przekazać object TextInput. W poniższym programie wszystkie buttony bindują do ostatniej funkcji nr 3.
Obszedłem ten problem wyrzucając lambdę i stosując id dla butonów oraz wspólną funkcję gdzie sprawdzam to id, a tam przekierowywuję do właściwej funkcji. No ale jest ta dodatkowa funkcja i frustracja dlaczego nie działa w wersji pierwotnej.

from kivy.app import runTouchApp,stopTouchApp
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button

def f_nr1(a):
	#print(txt.text)
	print('1')

def f_nr2(a):
	#print(txt.text)
	print('2')

def f_nr3(a):
	#print(txt.text)
	print('3')

lLst=[['1',f_nr1,(100,300)],['2',f_nr2,(300,100)],['3',f_nr3,(400,400)]]

lay=FloatLayout()
txt=TextInput(text='Radek',size_hint=(1,None))
txt.height=txt.line_height*1.5
for idx,elm in enumerate(lLst):
	btn=Button(text=elm[0],size_hint=(.2,.2),pos=elm[2])
	btn.bind(on_release=lambda *_: elm[1](txt))
	lay.add_widget(btn)

lay.add_widget(txt)
runTouchApp(lay)

Pozdrawiam
Radek Głębicki

1

Mógłbyś jakoś jaśniej, jaki jest problem; i prosimy o: https://stackoverflow.com/help/minimal-reproducible-example

3
Radosław Głębicki napisał(a):

Witam

Wymyśliłem sobie, że parametry dla "Buttonów" będą w liście. Jednym z parametrów będzie funkcja dla bind butona.
Jako parametr chcę przekazać object TextInput. W poniższym programie wszystkie buttony bindują do ostatniej funkcji nr 3.
Obszedłem ten problem wyrzucając lambdę i stosując id dla butonów oraz wspólną funkcję gdzie sprawdzam to id, a tam przekierowywuję do właściwej funkcji. No ale jest ta dodatkowa funkcja i frustracja dlaczego nie działa w wersji pierwotnej.

lLst=[['1',f_nr1,(100,300)],['2',f_nr2,(300,100)],['3',f_nr3,(400,400)]]
# ...
txt.height=txt.line_height*1.5
for idx,elm in enumerate(lLst):
	btn=Button(text=elm[0],size_hint=(.2,.2),pos=elm[2])
	btn.bind(on_release=lambda *_: elm[1](txt))

Dzieje się to z bardzo prostego powodu, mianowicie dlatego że używasz zmiennych globalnych! W Twoim przykładzie elm to zmienna globalna. Zmień to na zmienne lokalne i problem zniknie.

Wyjaśnienie:

Kiedy robisz lambdę lambda *_: elm[1](txt), to ona zawsze się odniesie do zmiennej globalnej elm. Twoja pętla iteruje po lLst, i po wyjściu z pętli elm wskazuje na ostatni element listy. Twoja lambda robi to co ma robić, strzela do [1] w elm, a po wyjściu z pętli elm to jest to co zostało w tej zmiennej po wyjściu z pętli.

def bind(btn: Button, func: callable):
    btn.bind(on_release=lambda *_: func(txt))


for idx, elm in enumerate(lLst):
    btn = Button(text=elm[0], pos=elm[2])
    bind(btn, elm[1])
    lay.append(btn)

PS: A najlepiej, w ogóle nie używaj zmiennych globalnych.

0
Radosław Głębicki napisał(a):

Ale nie da się pytać o problem z kivy podając minimal bez kivy.

Tylko że Twój problem mógł mieć związek z kivy, ale mógł też być całkiem od niego niezależny (co się okazało), bez minimal repo nikt nie mógł tego wiedzieć, chyba że by zgadywał.

Jeśli chcesz pomóc z kivy to miej kivy. I przecież to jest minimal. Mniej się nie da. Czy wy tego nie rozumiecie. Bo ja nie rozumiem tego pytania o minimal jeśli dodałem kod w wersji minimal.

Da się, i nawet Ci pokażę. I nie, to co Ty nie dostarczyłeś to nie było "minimal", dałeś nam kod w którym jest defekt (wołanie funkcji zawsze trzeciej), ale w kodzie który dostarczyłeś są też rzeczy niewymagane do jego reprodukcji (kivy, i inny niepotrzebny kod). Więc z definicji nie jest "minimal".

Lista kroków, "Jak zrobić minimal reproducible repo wg. TomRiddle":

  1. Bierzesz swój kod z pierwszego posta
  2. Wywalasz importy do kivy z niego (oczywiście aplikacja przestaje działać, ale to nic)
  3. Wywalasz lay=FloatLayout()
  4. Wywalasz txt=TextInput(text='Radek',size_hint=(1,None))
  5. Wywalasz runTouchApp(lay)
  6. Teraz, żeby zreprodukować zachowanie o którym mówisz (czyli że każdy przycisk woła zawsze trzecią funkcję) musimy dopisać "spoof" Buttona - udawany przycisk
class Button:
    def __init__(self):
      self.on_release = None

    def bind(self, on_release):
      self.on_release = on_release
  1. Zamienię też lay na list, żeby łatwo się dało wołać przyciski.
  2. Wywołam on_release() programatycznie

Wychodzi nam taki kod, trochę podobny do Twojego z początkowego postu, prawda?:

class Button:
    def __init__(self, text, pos):
        self.on_release = None

    def bind(self, on_release: callable) -> None:
        self.on_release = on_release


def f_nr1(a):
    print('1')


def f_nr2(a):
    print('2')


def f_nr3(a):
    print('3')


lLst = [
    ['1', f_nr1, (100, 300)],
    ['2', f_nr2, (300, 100)],
    ['3', f_nr3, (400, 400)]
]

lay = []

for idx, elm in enumerate(lLst):
    btn = Button(text=elm[0], pos=elm[2])
    btn.bind(on_release=lambda *_: elm[1](True))
    lay.append(btn)

lay[0].on_release()
lay[1].on_release()
lay[2].on_release()

I jak go włączysz, to widzimy w konsoli zachowanie o którym mówisz, czyli wołanie funkcji dla przycisku 0, 1 oraz dwa, daje nam zawsze trójki

C:\Users\Riddle\PycharmProjects\test> python .\file.py
3
3
3

I taki kod każdy może sobie wkleić w swojego pythona i uruchomić, bez żadnej dodatkowej biblioteki. Widać tutaj jasno żę problem nie leży w kivy, i nigdy nie była potrzebna do rozwiązania tego problemu.

PS: Nie jest jeszcze "minimal", bo nadal tu są niepotrzebne rzeczy (jak enumarate(), elm[0], pos=elm[2]), mówiąc "niepotrzebne" mam na myśli niewymagane do reprodukcji buga, ale nie wadzą aż tak, bo można je bezproblemowo uruchomić na komputerze każdego kto ma pythona.

1
Radosław Głębicki napisał(a):

[...] Wyobraź sobie, że mam problem z przyciskami w kivy i daję Twój powyższy kod z dopiskiem "Mam problem z Kivy". I co? Jak wtedy będzie wyglądał pierwszy post. Zamieść minimalny kod z kivy! [...]

Nie wiem czy na prawdę myślisz że to kivy jest kluczowe w tym przykładzie, czy po prostu głupio Ci się przyznać teraz.

Twój problem z lambdą, nie ma żadnego związku z kivy. Tak chciałeś napisać program w kivy, i tak użyłeś jej w kodzie; ale Twój błąd polegał na użyciu zmiennych globalnych, który to można pokazać również kodem bez kivy.

Mam nadzieję że znalazłeś odpowiedź na swoje pytanie.

1 użytkowników online, w tym zalogowanych: 0, gości: 1