Tkinter - dlaczego w opcji "bind" musi być argument ?

Tkinter - dlaczego w opcji "bind" musi być argument ?
SM
  • Rejestracja:ponad 5 lat
  • Ostatnio:prawie 4 lata
  • Postów:23
0

Witam. Mam takie jedno pytanie dotyczące "bind". W tym momencie uczę się dopiero pierwszego języka i dlatego niektóre rzeczy są dla mnie niezrozumiałe i możliwe że mało merytorycznie opiszę swój "problem".

Mam jedną z linijek kodu:

"butt_oblicz.bind('<Button-1>', xxx)"

Ma ona na celu przypięcie funkcji do tego przycisku. Wcześniej oczywiście mam zdefiniowaną funkcję "xxx". Nie mogę zrozumieć jednak dlaczego muszę podawać dla niej "positional arguments" skoro i tak go nie używam. Dla mnie logiczne powinno być, że napiszę tak:

def xxx():
treść funkcji

a tak naprawdę kod działa tylko w sytuacji gdy jest napisane:

def xxx(nazwa_argumentu_ktory_nie_ma_totalnie_znaczenia):
treść funkcji

JV
  • Rejestracja:ponad 6 lat
  • Ostatnio:około 2 miesiące
  • Postów:242
0

argument to obiekt event, z którego możesz wyciągnąć więcej informacji o przyczynie wywołania twojej funkcji. Jak widgetem jest np. Canvas zamiast Button, to tam się może przydać info o pozycji kursora myszy gdy zaszło wydarzenie np. <Button-1>. Tak to API wygląda i musisz stosować się to kontraktu. Zatem musisz pisać funkcję z jednym argumentem, który po prostu sobie ignorujesz:

Kopiuj
def obsluga(event):
    pass

jak chcesz bez parametrów to przypisz funkcję bezparametrową w momencie tworzenia buttona:

Kopiuj
def click():
    pass
#ver1
bt1 = tk.Button(master=root, text="Click me", command=click)
##ver2
bt1 = tk.Button(master=root, text="Click me")
bt1["command"] = click
EY
  • Rejestracja:ponad 5 lat
  • Ostatnio:około rok
  • Lokalizacja:Piaseczno
  • Postów:5
0

Każdy argument ma znaczenie. Chociaż ja osobiście tkinter strasznie nie lubie. Już wolę pisać w Lua na silniku LOVE2D jakieś programy.


Początkujący w Lua, Python
SM
  • Rejestracja:ponad 5 lat
  • Ostatnio:prawie 4 lata
  • Postów:23
0

Jeżeli chodzi o to wspomniane położenie kursora to wychodzi mi tak:

Kopiuj

def dzialanie_przycisku(zmienna_bez_znaczenia):
    print("Hello")
    print(zmienna_bez_znaczenia)


def okno_aplikacji():
    import tkinter
    okno_glowne = tkinter.Tk()
    butt_oblicz = tkinter.Button(text="Wykonaj działanie")
    butt_oblicz.grid(row=1, column=1)
    butt_oblicz.bind('<Button-1>', dzialanie_przycisku)
    okno_glowne.mainloop()

okno_aplikacji()

Z tego uzyskuję takie informacje:

Kopiuj
Hello
<ButtonPress event state=Mod1 num=1 x=69 y=13>

Czyli pozycję kursora o której wspomniałeś.

Jednak powiedzmy, że chciałbym skorzystać z jakiegoś argumentu i piszę już tego typu kod:

Kopiuj
def dzialanie_przycisku(imie):
   print(f'Hello {imie}')
   print(imie)

def okno_aplikacji():
   import tkinter
   okno_glowne = tkinter.Tk()
   butt_oblicz = tkinter.Button(text="Wykonaj działanie")
   butt_oblicz.grid(row=1, column=1)
   imie = "Mateusz"
   butt_oblicz.bind('<Button-1>', dzialanie_przycisku(imie))
   okno_glowne.mainloop()


okno_aplikacji()

Co w konsekwencji otrzymuję:

Kopiuj
Hello Mateusz
Mateusz

Co oznacza, że wykorzystując ten niepotrzebny argument do własnych celów nie jestem w stanie już uzyskać informacji na temat kliknięcia. Chociaż przyznam, że w tym momencie nie widzę i tak zastosowania dla tego rozwiązania.

JV
  • Rejestracja:ponad 6 lat
  • Ostatnio:około 2 miesiące
  • Postów:242
0

tutaj jest błąd:

Kopiuj
butt_oblicz.bind('<Button-1>', dzialanie_przycisku(imie))

funkcja bind jako drugi parametr oczekuje zmienną typu funkcyjnego, bo później w momencie naciśnięcia przycisku będzie tą twoją funkcję wywoływać. Ty natomiast wstawiłeś wywołanie funkcji, która nie ma instrukcji return. Jak nie ma to znaczy, że taka funkcja w pythonie zwraca None. A skoro bind dostał w podarunku None to nic nie będzie wywołane. Tak jest poprawnie:

Kopiuj
butt_oblicz.bind('<Button-1>', dzialanie_przycisku)

Jak chcesz np. przekazać dodatkowy parametr do tego callback-a, żeby np. ta sama funkcja była wywołana przez kilka przycisków z możliwością rozpoznania który z nich został wywołany to można stosować domknięcia (closures), czyli funkcje, które zwracają funkcje. Tutaj mały przykład:

Kopiuj
def dzialanie_przycisku(imie, event):
    print(f'Hello {imie}')
    print(imie, event)

def okno_aplikacji():
    import tkinter
    from functools import partial
    okno_glowne = tkinter.Tk()
    butt_oblicz = tkinter.Button(text="Wykonaj działanie")
    butt_oblicz.grid(row=1, column=1)
    butt2_oblicz = tkinter.Button(text="Wykonaj działanie 2")
    butt2_oblicz.grid(row=2, column=1)
    imie = "Mateusz"
    imie2 = "Wojtek"
    butt_oblicz.bind('<Button-1>', partial(dzialanie_przycisku, imie))
    butt2_oblicz.bind('<Button-1>', partial(dzialanie_przycisku, imie2))
    okno_glowne.mainloop()

okno_aplikacji()

JV
  • Rejestracja:ponad 6 lat
  • Ostatnio:około 2 miesiące
  • Postów:242
0

Jeszcze jedno, jak chcesz koniecznie mieć obsługę przycisku funkcją bezparametrową to możesz zastosować wrapper:

Kopiuj
def wrapper(fun):
    def dzialanie(event):
        fun() # ignorujemy event
    return dzialanie

def dzialanie_przycisku():
    print(f'Hello bez parametru')

def okno_aplikacji():
    import tkinter
    from functools import partial
    okno_glowne = tkinter.Tk()
    butt_oblicz = tkinter.Button(text="Wykonaj działanie")
    butt_oblicz.grid(row=1, column=1)
    butt_oblicz.bind('<Button-1>', wrapper(dzialanie_przycisku))
    okno_glowne.mainloop()

okno_aplikacji()
SM
  • Rejestracja:ponad 5 lat
  • Ostatnio:prawie 4 lata
  • Postów:23
0
jvoytech napisał(a):

funkcja bind jako drugi parametr oczekuje zmienną typu funkcyjnego, bo później w momencie naciśnięcia przycisku będzie tą twoją funkcję wywoływać. Ty natomiast wstawiłeś wywołanie funkcji, która nie ma instrukcji return. Jak nie ma to znaczy, że taka funkcja w pythonie zwraca None. A skoro bind dostał w podarunku None to nic nie będzie wywołane.

Przyznam, że nie do końca rozumiem. Nie mam tam return w definicji funkcji ale przecież bez problemu zwraca mi to co powinno. Jestem dopiero żółtodziobem w tym temacie ale return chyba stosuje się tam gdzie wykorzystuje się jakieś obliczenia, których chciałbym dalej skorzystać a jeżeli mam zwykłe "print" to ten return jest zbędny.

PRZYKŁAD 1

Kopiuj
def moja_funkcja(imie):
    print(f'Witaj {imie} !')

moja_funkcja("Stefan")

Co daje:

Kopiuj
Witaj Stefan !

PRZYKŁAD 2

Kopiuj
def square(e1):
    e1*e1
result = square(2)
print(result)

Co daje

Kopiuj
NONE

PRZYKŁAD 3

Kopiuj
def square(e1):
    return e1*e1
result = square(2)
print(result)

Co daje w tym wypadku

Kopiuj
4
JV
  • Rejestracja:ponad 6 lat
  • Ostatnio:około 2 miesiące
  • Postów:242
0
sad_madman napisał(a):
jvoytech napisał(a):

funkcja bind jako drugi parametr oczekuje zmienną typu funkcyjnego, bo później w momencie naciśnięcia przycisku będzie tą twoją funkcję wywoływać. Ty natomiast wstawiłeś wywołanie funkcji, która nie ma instrukcji return. Jak nie ma to znaczy, że taka funkcja w pythonie zwraca None. A skoro bind dostał w podarunku None to nic nie będzie wywołane.

Przyznam, że nie do końca rozumiem. Nie mam tam return w definicji funkcji ale przecież bez problemu zwraca mi to co powinno. Jestem dopiero żółtodziobem w tym temacie ale return chyba stosuje się tam gdzie wykorzystuje się jakieś obliczenia, których chciałbym dalej skorzystać a jeżeli mam zwykłe "print" to ten return jest zbędny.

Chodzi o to, że jak wywołujesz

Kopiuj
button.bind('<Button-1>', obsluga_przycisku())

to tak jakbyś napisał:

Kopiuj
obsluga_przycisku()
button.bind('<Button-1>', None)

czyli przed wywołaniem bind wywołujesz funkcję obsługa_przycisku() a to jest bez sensu. Ta funkcja ma być wywołana za każdym razem jak się ten przycisk naciśnie. W uproszczeniu to mniej więcej wewnętrznie działa tak:

Kopiuj
class Widget:
    def bind(self, event, callback):
        self.cb = event, callback
    def run(self, event):
        if self.callback != None:
            self.calback(event)

później metoda run() będzie wewnętrznie wywołana jak nastąpi zdarzenie Button-1. W tej metodzie będzie wołany callback o ile go dostarczysz, tzn. musi to być obiekt funkcyjny z jednym parametrem wejściowym

SM
  • Rejestracja:ponad 5 lat
  • Ostatnio:prawie 4 lata
  • Postów:23
0

Ja mogę przekazać zmienną w takim przypadku:

Kopiuj

def dzialanie_przycisku(imie, event):
    print(f'Hello {imie}')
    print(imie, event)

def dzialanie_przycisku_2(event):
    imie3 = wpisz_imie.get()
    print(f'Hello {imie3, event}')


import tkinter
from functools import partial
okno_glowne = tkinter.Tk()
butt_oblicz = tkinter.Button(text="Drukuje imie1")
butt_oblicz.grid(row=1, column=1)
butt2_oblicz = tkinter.Button(text="Drukuj imie2")
butt2_oblicz.grid(row=2, column=1)
butt3_oblicz = tkinter.Button(text="Drukuj wpisane imie")
butt3_oblicz.grid(row=3, column=1)

wpisz_imie = tkinter.Entry(okno_glowne)
wpisz_imie.grid(row=4, column=1)

imie = "Mateusz"
imie2 = "Wojtek"

butt_oblicz.bind('<Button-1>', partial(dzialanie_przycisku, imie))
butt2_oblicz.bind('<Button-1>', partial(dzialanie_przycisku, imie2))
butt3_oblicz.bind('<Button-1>', dzialanie_przycisku_2)
okno_glowne.mainloop()

Ten kod mi działa ponieważ zmienna "wpisz_imie" jest globalna. Jeżeli jednak chciałbym kod ująć w funkcji albo przerzucić kod "eventu" do oddzielnego modułu to już nie mam do niej dostępu.

JV
  • Rejestracja:ponad 6 lat
  • Ostatnio:około 2 miesiące
  • Postów:242
0

ok, no to wersja z kodem obsługi w innym module:

Kopiuj
# moj_modul.py

def kod_eventu(entry, event):
    imie3 = entry.get()
    print(f'Hello {imie3, event}')
Kopiuj
# tkapp.py
import tkinter
from functools import partial

from moj_modul import kod_eventu

okno_glowne = tkinter.Tk()

butt3_oblicz = tkinter.Button(text="Drukuj wpisane imie")
butt3_oblicz.grid(row=3, column=1)

wpisz_imie = tkinter.Entry(okno_glowne)
wpisz_imie.grid(row=4, column=1)

butt3_oblicz.bind('<Button-1>', partial(kod_eventu, wpisz_imie))

okno_glowne.mainloop()
SM
  • Rejestracja:ponad 5 lat
  • Ostatnio:prawie 4 lata
  • Postów:23
0

Dziękuję. Kod działa ale przyznam, że jest to dla mnie czarna magia. Będę musiał sobie na spokojnie te kody przeanalizować lub ewentualnie wrócić do nich jak moja wiedza z zakresu Pythona się zwiększy.
Nawet kurcze dopisałem sobie kolejny przycisk wraz z polem i podpiąłem także pod funkcję "kod_eventu" i wszystko działa jak powinno.

Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)