Niech Tego Dotkną! Tkinter Od Podstaw
- Jarosław Piszczała
- 23 Apr, 2020
- 23 Apr, 2020
Programowanie w Python jest trochę niewdzięczne. Piszesz różne skrypty, logikę, ciekawe rozwiązania. Dla osób, które nie znają się na programowaniu, to będzie nic, póki nie zobaczą wizualnych efektów. Kiedy dla nas w dużej mierze interfejs oparty o wiersz poleceń (CLI) jest wystarczający, to są momenty gdy jedynym słusznym rozwiązaniem jest implementacja aplikacji okienkowej z graficznym interfejsem (GUI). Jest wiele różnych GUI, z których można korzystać w Python. Ponieważ stawiam duży nacisk na biblioteki wbudowane, omówimy bibliotekę Tkinter!
Dlaczego Tkinter
Jest wiele różnych bibliotek do tworzenia aplikacji okienkowych w Python. Do najczęściej wspominanych należy zdecydowanie PyQT
. Innymi rozwiązaniami są także wxPython
czy kivy
. Problem z nimi jest taki, że posiadają wiele dodatkowych zależności, a co niektóre wymagają do pracy zewnętrznych narzędzi. Tkinter
jest wbudowany, więc nie wymaga instalacji żadnych paczek Pythonowych do jego działania, i to jedna z jego największych niezaprzeczalnych zalet.
Najwięcej sprzeciwów względem to surowy wygląd, oraz ograniczenia. Trudno się nie zgodzić z tymi uwagami. O ile do większych aplikacji biznesowych zdecydowanie polecałbym użycie innych bibliotek, o tyle, aby stworzyć prosty interfejs graficzny do naszego kodu, nie ma lepszego rozwiązania. Dodatkowo wiedza wyniesiona z używania Tkinter
pozwoli na łatwiejsze przejście na inne biblioteki do tworzenia GUI.
Podstawy
Aby uruchomić aplikację okienkową, wystarczy nam nie więcej niż trzy linie kodu. Po pierwsze importujemy klasę Tk
z pakietu tkinter
. Po drugie tworzymy obiekt klasy Tk
. Jest to główna klasa aplikacji, element najwyższego rzędu. To do obiektu tej klasy będziemy dodawać elementy naszego interfejsu. Na koniec nie pozostało nic innego jak uruchomić naszą aplikację, wywołując funkcje mainloop
na obiekcie klasy Tk
.
Skoro pierwszy krok mamy za sobą, czas na pewne udoskonalenia. Skupimy się na największych podstawach, aby jak najszybciej dojść do działającej aplikacji. Może się do tego przydać ustalenie nazwy naszej aplikacji poprzez metodę title
, dzięki temu nazwiemy naszą aplikację App
. Po drugie równie przydatne może okazać się ustawienie rozmiaru okna. Zrobimy to poprzez metodę geometry
, która jako argument przyjmuje string w formacie SZEROKOŚĆxWYSOKOŚĆ
, w poniższym przypadku będzie to 600px
szerokości na 400px
wysokości.
Skoro mamy już okno aplikacji, czas dodać do niej elementy!
Label
Aby dodać tekst w naszej aplikacji, skorzystamy z klasy Label
. Większość obiektów w Tkinter jako pierwszy argument przyjmować będzie element, w którym dany obiekt zostanie umieszczony. Dlatego też, jako pierwszy argument przekazujemy obiekt naszego okna root
.
Następnie definiujemy właściwości tego obiektu. Dla nas najistotniejszy to argument text
, do którego przypiszemy wartość widoczną w oknie. Możemy zdefiniować także jego rozmiar za pomocą argumentu font
. Dodatkowo za pomocą argumentu fg
tekst ten możemy pokolorować. Możemy to zrobić za pomocą heksadecymalnej reprezentacji kolorów RGB (#RRGGBB
), lub za pomocą słowa. W poniższym przykładzie będzie to napis Look at me!
o kolorze niebieskim.
Ostatni element, to metoda pack
. Używamy jej, aby ustalić miejsce naszego obiektu w aplikacji. Dla ułatwienia zostawimy na razie domyślną wartość TOP
. Spowoduje to, że nasze obiekty będą wstawiane w jednej kolumnie od góry do dołu.
Button
Skoro wiemy już jak pisać tekst w naszej aplikacji, czas na element sterujący. Będzie to przycisk, a więc zaimportujemy klasę Button
. Tworzenie obiektu tej klasy wygląda podobnie do obiektu klasy Label
. Jako pierwszy argument przekazujemy okno naszej aplikacji, a następnie cechy obiektu. Argument text
już poznaliśmy. Width
pozwoli ustawić szerokość naszego przycisku. Domyślnie szerokość dopasowywałaby się do szerokości naszego tekstu.
Najważniejszym argumentem jest tutaj command
który służy do definiowania jaką komendę chcemy wywołać poprzez wciśnięcie przycisku. Przekazujemy tutaj funkcje bezargumentową. W poniższym przykładzie wyświetlać ona będzie napis Wow!
w terminalu po każdorazowym wciśnięciu.
Konfiguracja
Cechy naszych obiektów możemy zmieniać także po ich utworzeniu. Robimy to poprzez metodę config
. Jej argumenty są takie same, jak argumenty przy inicjalizacji obiektu. Poniższy przykład pokazuje inny sposób zdefiniowania komendy naszego przycisku. Definiujemy ją tutaj dopiero po utworzeniu obiektu.
Do czego przydać nam może się taka metoda? Do modyfikacji obiektów w czasie działania aplikacji - czyli do aktualizacji tego, co w niej widzimy!
Komenda z argumentami
Teraz gdy możemy skonfigurować komendę po utworzeniu obiektu, możemy do tej komendy przekazać nasz obiekt. Ponieważ command
musi być funkcją bezargumentową mamy dwa rozwiązania. Możemy zbudować funkcje, która przyjmie funkcje oraz jej argumenty, a na wyjściu zwróci funkcje bezargumentową wywołującą przekazaną przez nas funkcje z argumentami.
Drugim rozwiązaniem jest skorzystanie z funkcji lambda
. W tym przypadku jako komendę przekazujemy funkcje lambda, która ukrywać będzie wywołanie funkcji click_action
z obiektem przycisku jako argumentem. W środku tej funkcji także nastąpiła zmiana, po wciśnięciu przycisku zmienimy jego nazwę z Click me!
na Wow!
.
Sposób, w jaki będziesz definiować komendy, jest zależny od Ciebie i od tego, które rozwiązanie jest dla Ciebie czytelniejsze i prostsze! Ja w kolejnych przykładach będę korzystać z opcji wykorzystującej funkcję anonimową lambda
.
Rozwinięcie komendy
Aby było ciekawiej, stwórzmy tutaj jakąś dodatkową logikę. Po pierwsze definiujemy zmienną clicks
, która przechowywać będzie ilość kliknięć naszego przycisku. Następnie zmodyfikujemy naszą funkcję, aby z niej korzystała. Wykorzystamy do tego instrukcje global
, która spowoduje, że w funkcji korzystać będziemy ze zmiennej globalnej. Następnie powiększymy ją o jeden, ponieważ udało nam się kliknąć przycisk! Na koniec nie pozostało nic innego, jak skorzystać z tej zmiennej w nazwie naszego przycisku. Zobaczmy, jak teraz zachowa się aplikacja.
Układ
Domyślnie w Python elementy są układane jeden pod drugim. Na tym etapie nie będziemy się skupiać na zmianie tego zachowania. W myśl frontendową najpierw zróbmy, czego potrzebujemy, a potem przyjdzie czas na stylowanie!
Kolejność użycia metody pack
ma w takim razie znaczenie. Na tej podstawie ustalona zostanie kolejność wystąpienia tych obiektów w naszej aplikacji. Ponieważ najpierw użyliśmy metody pack
na obiekcie klasy Button
, to przycisk zostanie zmapowany w aplikacji jako pierwszy, a nasz tekst jako drugi.
Przypadek użycia Tkinter
Uważasz, że to za mało, aby tworzyć coś konkretnego? Udowodnię, że jest inaczej. Za pomocą wyłącznie powyższych elementów i właściwości zagramy w Kamień, papier i nożyce z komputerem! Zanim weźmiemy się do tworzenia gui w tkinter
, zaimplementujmy rekwizyty, z których skorzystamy.
Logika
Po pierwsze, potrzebujemy prostej logiki do gry. Stwórzmy zatem listę możliwych ruchów.
Przy prostym formacie rozgrywki mamy trzy opcje: wygramy, przegramy albo remis. Remis jest wtedy gdy użytkownik oraz komputer wybiorą ten sam symbol.
Następnie mamy prostą zasadę zwycięstwa. Papier ma przewagę nad kamieniem, kamień nad nożyczkami, a te ostatnie nad papierem. Najłatwiej będzie opisać to za pomocą słownika. W każdym innym przypadku przegrywamy. Nasza funkcja wskazywać będzie status pierwszego gracza - czy wygrał (True
), przegrał (False
) lub zremisował (None
).
Aby dodać losowości, skorzystamy z biblioteki random
, a dokładniej metody choice
, która zwracać będzie losowy element z kolekcji dostępnych symboli.
Klasa TK
Skoro logika jest gotowa, czas połączyć ją z GUI. Stwórzmy zatem okno aplikacji, oraz zdefiniujmy obiekt klasy Label
, który zachęci gracza do gry, a następnie będzie informować o wyniku.
Następnie dodajmy trzy przyciski. To z ich pomocą decydować będziemy o tym, jaki ruch wykonamy w tej grze!
Możecie zauważyć, że tekst naszych przycisków się rozjeżdża. Na ten moment zaakceptujemy tę niedoskonałość, poprawa ustawienia elementów czy tekstu to temat na osobny artykuł.
Połączenie logiki z graficznym interfejsem
Nie przypisałem obiektów klasy Button
do zmiennych, ponieważ w tej grze przy mojej implementacji nie będę potrzebować dalszego dostępu do tych obiektów. Czas spiąć logikę gry z GUI. Służyć do tego będzie funkcja play_cmd
, która przyjmie symbol użytkownika. Za pomocą global
uzyskamy dostęp do naszej labelki, której treść będziemy zmieniać w zależności od wyniku rozgrywki.
Po dopięciu powyższej funkcji do naszych przycisków, kod naszej gry wyglądać będzie następująco:
Gra wygląda.. dość surowo. Ale działa! I to jest dla nas najważniejsze! Liczą się efekty, które potem można rozbudowywać i ulepszać. Aby potrenować wiedzę nabytą, możesz dodać statystykę zwycięstw, informacje o symbolu pokazanym przez komputer, lub rozszerzyć grę do wersji “Paper rock scissors lizard spock”!
Masz pomysł, jaką grę można by wykonać, wykorzystując powyższą wiedzę? Podziel się tym w komentarzach! W kolejnym artykule omówimy jak rozstawiać elementy w oknie, aby z pomocą tkinter móc zagrać z komputerem w kółko i krzyżyk :)