rozdział 2: WSTĘP DO TKINTER ostatnia modyfikacja: 18.03.17
Literatura przedmiotu Niestety, brak polskojęzycznych źródeł wyczerpująco opisujących programowanie w TkInter. Na szczęście, bezpłatnych źródeł angielskojęzycznych jest pod dostatkiem.
http://effbot.org/tkinterbook/ wprowadzenie krok po kroku do programowania w TkInter sporo przykładów, niestety, również trochę błędów
http://staff.washington.edu/rowen/tkintersummary.html szybka przechadzka po najistotniejszych elementach TkInter sporo przykładów, jednak skomentowanych dość lakonicznie
http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/index.html obszerny i usystematyzowany opis TkInter (niestety, bardzo ubogi w przykłady) dostępny również pod postacią PDF
http://tkinter.unpythonic.net/wiki/frontpage Wikia programowania TkInter w Pythonie bardzo rozbudowana, ciągle aktualizowana
http://thinkingtkinter.sourceforge.net szczegółowy i dogłębny kurs programowania w TkInter
http://www.tkdocs.com portal w całości poświęcony programowaniu w TkInter
Zaczynamy! TkInter jest (w sensie semantyki Pythona) modułem, udostępniającym zestaw funkcji, stałych i obiektów program GUI używający TkInter składa się zazwyczaj z następujących czterech elementów: 1. import modułu tkinter 2. stworzenia okna głównego programu 3. dodanie do okna koniecznych widżetów 4. uruchomienie kontrolera zdarzeń
Importujemy cały TkInter... import tkinter
Oczywiście, możemy też wykonać import z nadaniem nazwy pobocznej (aliasu): import tkinter as tk
Albo jeśli mamy mocne nerwy możemy importować wyłącznie te byty, które są nam potrzebne: from tkinter import Button
Tak też można: from tkinter import *
A oto pierwszy, kompletnie bezużyteczny program z użyciem TkInter: import tkinter okieneczko = tkinter.tk() okieneczko.mainloop() 02_01.py
Konstruktor Konstruktorklasy klasytk Tktworzy tworzyobiekt obiekt reprezentujący reprezentującytzw tzw okno oknogłówne główne (ang. (ang.root rootwindow); window);po popowrocie powrociezz konstruktora konstruktoraokno oknojuż jużistnieje, istnieje,ale alejeszcze jeszczego go nie niewidać widać import tkinter okieneczko = tkinter.tk() okieneczko.mainloop()
Metoda Metodamainloop() mainloop()klasy klasytk Tkuruchamia uruchamia kontroler kontrolerzdarzeń zdarzeń okno oknopojawia pojawiasię sięna na ekranie ekraniei izaczyna zaczynażyć żyćwłasnym własnymżyciem życiem import tkinter okieneczko = tkinter.tk() okieneczko.mainloop()
jak na razie nasze okno nie jest szczególnie rozgarnięte jedyną rzeczą, którą potrafi zrobić, jest reagowanie na klikanie dekoracji okna (uzewnętrznią się zachowania domyślne) na początek spróbujemy nakłonić je do zmiany tytułu (na razie ma nieatrakcyjny tytuł domyślny - tk ) czynność tę wykona dla nas metoda title() obiektu okna głównego zmianę tytułu można wykonać wielokrotnie w dowolnych momentach
Metoda Metodatitle() title()klasy klasytk Tkustawia ustawiatytuł tytułokna okna import tkinter okieneczko = tkinter.tk() okieneczko.title("okieneczko") okieneczko.mainloop() 02_02.py
teraz spróbujemy do naszego okna wstawić przycisk (button) przycisk widoczny na ekranie jest reprezentowany przez obiekt klasy Button przycisk (tak jak każdy widżet) należy kolejno: utworzyć (zrobi to konstruktor klasy widżetu) umieścić w oknie głównym (zrobi to jedna z metod obiektu widżetu)
Konstruktor Konstruktorklasy klasybutton Buttontworzy tworzyobiekt obiekt reprezentujący reprezentującyprzycisk przycisk import tkinter okieneczko = tkinter.tk() okieneczko.title("okieneczko") guziczek = tkinter.button(okieneczko, text="pa!") guziczek.place(x=10, y=10) okieneczko.mainloop() 02_03.py
Pierwszym Pierwszymargumentem argumentemkonstruktora konstruktora każdego każdegowidżetu widżetujest jestobiekt obiektokna, okna,do do którego któregobędzie będzienależeć należećwidżet widżet(!!!) (!!!) import tkinter okieneczko = tkinter.tk() okieneczko.title("okieneczko") guziczek = tkinter.button(okieneczko, text="pa!") guziczek.place(x=10, y=10) okieneczko.mainloop()
Poprzez Poprzezparametr parametrtext textokreślamy określamytytuł tytuł (nazwę) (nazwę)nowego nowegoprzycisku przycisku import tkinter okieneczko = tkinter.tk() okieneczko.title("okieneczko") guziczek = tkinter.button(okieneczko, text="pa!") guziczek.place(x=10, y=10) okieneczko.mainloop()
Metoda Metodaplace() place()klasy klasybutton Buttonumieszcza umieszcza przycisk przyciskna nawskazanej wskazanejpozycji pozycjiwewnątrz wewnątrz okna okna(w (wtym tymprzypadku przypadkuodległości odległości mierzymy mierzymypikslami!) pikslami!) import tkinter okieneczko = tkinter.tk() okieneczko.title("okieneczko") guziczek = tkinter.button(okieneczko, text="pa!") guziczek.place(x=10, y=10) okieneczko.mainloop()
Uwaga! kartezjański układ współrzędnych wygląda tak: y x
A okienkowy układ współrzędnych wygląda tak: x y
przycisk już mamy teraz sprawimy, żeby jego kliknięcie kończyło pracę naszego okna najpierw napiszemy event handler, czyli określimy, co chcemy zrobić, gdy przycisk zostanie kliknięty będzie to funkcja, którą w odpowiednim momencie wywoła kontroler zdarzeń taka funkcja, przeznaczona do wywołania przez kogoś innego, bywa nazywana callback (nie do przetłumaczenia, niestety, opisowo jest to tzw. wywołanie zwrotne ) co do zasady, nie wolno własnoręcznie wywoływać własnego callbacka pełną kontrolę nad nim sprawuje kontroler zdarzeń i tylko on callback dla zdarzenia kliknięcia przycisku jest bezparametrową funkcją (nie zawsze tak jest, inne zdarzenia mogą wymagać innej postaci callbacka) zakończenie pracy okna głównego osiąga się poprzez wywołanie jego bezparametrowej metody destroy() metoda destroy() powoduje opuszczenie metody mainloop() okna głównego
def Klik(): global okieneczko okieneczko.destroy() Zapewniamy Zapewniamysobie sobiedostęp dostępdo doglobalnej globalnej zmiennej zmiennejprzechowujące przechowująceodnośnik odnośnikdo do głównego głównegookna okna(nie (niezawsze zawszekonieczne, konieczne, ale alepoprawia poprawiaczytelność czytelnośćkodu) kodu)
def Klik(): global okieneczko okieneczko.destroy() Wywołujemy Wywołujemymetodę metodędestroy() destroy()zzobiektu obiektu okna oknagłównego; głównego;metoda metodatataprzerywa przerywa działanie działaniezarządcy zarządcyzdarzeń zdarzeńi ikończy kończy pracę pracęprogramu programu
kolejnym krokiem jest dowiązanie świeżo utworzonego callbacka do juz posiadanego widżetu Button jest to sposób na poinformowanie zarządcy zdarzeń, co ma zrobić, kiedy przycisk zostanie kliknięty dowiązanie callbacka do widżetu najwygodniej wykonuje się w chwili kreowania tego drugiego, ale możliwe są również inne sposoby jeden i ten sam callback może być dowiązany do więcej niż jednego widżetu (o ile ma to sens)
Parametr command Parametr commandkonstruktora konstruktorabutton Button Programowanie wizualne wiąże naszą funkcję z obiektem przycisku; wiąże naszą funkcję z obiektem przycisku; jeśli jeśliklikniemy klikniemyten tenprzycisk, przycisk,tatafunkcja funkcja zostanie zostanieautomatycznie automatyczniewywołana wywołana import tkinter def Klik(): global okieneczko okieneczko.destroy() okieneczko = tkinter.tk() okieneczko.title("okieneczko") guziczek = tkinter.button(okieneczko, text="pa!", command=klik) guziczek.place(x=10, y=10) okieneczko.mainloop() 02_04.py
oczywiście, byłoby wskazane zapytać użytkownika, czy jest pewien, że chce się pożegnać z naszym oknem na szczęście, TkInter jest w tej kwestii bardzo pomocny do dyspozycji mamy klasę o obrazowej nazwie messagebox, służącą do wyświetlania komunikatów i zadawania pytań messagebox tworzy i obsługuje tzw. okna dialogowe (nazwa mówi sama za siebie) okno dialogowe jest przykładem tzw. okna modalnego (okno modalne to okno, które przechytuje całe skupienie aplikacji w chwili, gdy na ekranie pojawia się okno modalne, wszystkie zdarzenia okna głównego stają się głuche
def Klik(): global okieneczko odp = tkinter.messagebox.askquestion("pytanko", "Na pewno?") if odp=='yes': okieneczko.destroy() Metoda Metodaaskquestion() askquestion()klasy klasymessagebox messageboxtworzy tworzy okienko okienkodialogowe dialogoweootytule tytuleokreślonym określonymwwpierwszym pierwszym argumencie argumenciei itreści treścipytania pytaniaokreślonej określonejwwdrugim drugim argumencie argumencie
def Klik(): global okieneczko odp = tkinter.messagebox.askquestion("pytanko", "Na pewno?") if odp=='yes': okieneczko.destroy() Metoda Metodaaskquestion() askquestion()zwraca zwraca'yes', 'yes',jeśli jeśliużytkownik użytkownik kliknie kliknie Tak Tak albo albo'no', 'no',jeśli jeśliużytkownik użytkownikkliknie kliknie Nie Nie lub lub zamknie zamknieokienko okienkobez bezudzielania udzielaniaodpowiedzi odpowiedzi
A oto kompletny program: import tkinter from tkinter import messagebox def Klik(): global okieneczko odp = messagebox.askquestion("pytanko", "Na pewno?") if odp=='yes': okieneczko.destroy() okieneczko = tkinter.tk() okieneczko.title("okieneczko") guziczek = tkinter.button(okieneczko, text="pa!", command=klik) guziczek.place(x=10, y=10) okieneczko.mainloop() 02_05.py
Rozmieszczanie widżetów: TkInter oferuje 3 sposoby rozmieszczanie widżetów w oknie macierzystym place() ręcznie - z dokładnością do piksla pack() automatycznie - widżet za widżetem, bez możliwości dokładnego określenia lokalizacji grid() półautomatycznie rozmieszczanie widżetów w siatce z samoczynym wyrównaniem każda z powyższych metod to tzw. geometry manager
Manager place()
place( opcje_umieszczania ) height, width określenie pożądanych wymiarów widżetu height wysokość width szerokość brak takiego argumentu spowoduje, że wymiary widżetu zostaną ustalone automatycznie
place( opcje_umieszczania ) x, y określenie położenia widżetu w oknie x współrzędna pozioma y współrzędna pionowa
Trzy przyciski rozmieszczone przy pomocy place() import tkinter as tk o = tk.tk() g1 = tk.button(o, text="przycisk #1") g2 = tk.button(o, text="przycisk #2") g3 = tk.button(o, text="przycisk #3") g1.place(x=10, y=10) g2.place(x=20, y=40) g3.place(x=30, y=70) o.mainloop() 02_06.py
Dygresja: jeśli dostęp do pewnego widżetu nie będzie ci potrzebny, możesz zrezygnować z tworzenia przechowującej go zmiennej np. tak:
Wywołanie place() wprost z nowo utworzonego obiektu import tkinter as tk o = tk.tk() tk.button(o, text="przycisk #1").place(x=10, y=10) tk.button(o, text="przycisk #2").place(x=20, y=40) tk.button(o, text="przycisk #3").place(x=30, y=70) o.mainloop() 02_07.py
To samo, ale z użyciem width i heigth import tkinter as tk o = tk.tk() tk.button(o,text="p #1").place(x=10,y=10,width=150) tk.button(o,text="p #2").place(x=20,y=40,height=15) tk.button(o,text="p #3").place(x=30,y=70) o.mainloop() 02_08.py
Manager pack()
pack( opcje_pakowania ) side określenie kierunku, w którym odbywa się pakowanie widżetu: TOP (domyślnie) ku górze okna macierzystego BOTTOM ku dołowi LEFT ku lewej RIGHT ku prawej
pack( opcje_pakowania ) fill określenie kierunku, w którym widżet będzie rozciągany ponad rozmiar domyślny: NONE (domyślnie) nie rozciągaj X rozciągaj w poziomie Y rozciągaj w pionie BOTH rozciągaj w pionie i poziomie
Trzy przyciski rozmieszczone przy pomocy pack() import tkinter as o = tk.tk() g1 = tk.button(o, g2 = tk.button(o, g3 = tk.button(o, g1.pack() g2.pack() g3.pack() o.mainloop() tk text="przycisk #1") text="przycisk #2") text="przycisk #3") 02_09.py
A teraz drobna modyfikacja z użyciem side import tkinter as tk o = tk.tk() g1 = tk.button(o, text="przycisk #1") g2 = tk.button(o, text="przycisk #2") g3 = tk.button(o, text="przycisk #3") g1.pack(side=tk.left) g2.pack() g3.pack() o.mainloop() 02_10.py
Manager grid()
grid( opcje_siatki ) column, row określenie położenia widżetu column kolumna siatki (domyślnie 0, czyli skrajna lewa) row rząd siatki (domyślnie najwyższy wolny)
grid( opcje_siatki ) columnspan, rowspan określenie rozciągania widżetu columnspan liczba kolumn zajętych przez widget (domyślnie 1, jeśli więcej, to widżet jest rozciągany w poziomie) rowspan liczba rzędów zajętych przez widżet (jak wyżej, ale w pionie)
grid( opcje_siatki ) sticky określenie przylegania widżetu, czyli recepta na to, co zrobić z widżetem, jeśli jest mniejszy, niż komórka siatki domyślnie widżet jest centrowany może być równy N, E, S, W, NE, NW, SE, SW (tzw. współrzędne kompasowe)
Rozmieszczenie w siatce z użyciem column i row import tkinter as tk o = tk.tk() g1 = tk.button(o, text="przycisk #1") g2 = tk.button(o, text="przycisk #2") g3 = tk.button(o, text="przycisk #3") g1.grid(row=0,column=0) g2.grid(row=1,column=1) g3.grid(row=2,column=2) o.mainloop() 02_11.py
To samo plus columnspan import tkinter as tk o = tk.tk() g1 = tk.button(o, text="przycisk #1") g2 = tk.button(o, text="przycisk #2") g3 = tk.button(o, text="przycisk #3") g1.grid(row=0,column=0) g2.grid(row=1,column=1) g3.grid(row=2,column=0,columnspan=2) o.mainloop() 02_12.py
A teraz......bardziej złożony przykład, połączony z prezentacją kilku widżetów dostępnych w TkInter (program nie do końca działa poprawnie, ale pokazujemy go tylko jako ilustrację konstrukcji interfejsu)
import tkinter as tk o = tk.tk( ) tk.label(o,text="etykietka:").pack() tk.button(o,text="przycisk").pack() przełącznik=0 tk.checkbutton(o,text="przełącznik",variable=przełącznik).pack() tk.entry(o,width=30).pack() tk.radiobutton(o,text="kawa",variable=przełącznik,value=1).pack() tk.radiobutton(o,text="herbata",variable=przełącznik,value=2).pack() o.mainloop() 02_13.py
Programowanie wizualne Tworzymy Tworzymyetykietę etykietęi inatychmiast natychmiastjąjąpakujemy pakujemy do dookna oknamacierzystego macierzystego import tkinter as tk o = tk.tk( ) tk.label(o,text="etykietka:").pack() tk.button(o,text="przycisk").pack() przełącznik=0 tk.checkbutton(o,text="przełącznik",variable=przełącznik).pack() tk.entry(o,width=30).pack() tk.radiobutton(o,text="kawa",variable=przełącznik,value=1).pack() tk.radiobutton(o,text="herbata",variable=przełącznik,value=2).pack() o.mainloop() 02_14.py
Wszystko Wszystkojasne jasne prawda? prawda? import tkinter as tk o = tk.tk( ) tk.label(o,text="etykietka:").pack() tk.button(o,text="przycisk").pack() przełącznik=0 tk.checkbutton(o,text="przełącznik",variable=przełącznik).pack() tk.entry(o,width=30).pack() tk.radiobutton(o,text="kawa",variable=przełącznik,value=1).pack() tk.radiobutton(o,text="herbata",variable=przełącznik,value=2).pack() o.mainloop()
Ta będzie Programowanie wizualne Tazmienna zmienna będzieodzwierciedlać odzwierciedlaćstan stan kolejnych kolejnychwidżetów widżetów (będą (będątotowidżety, widżety,które któremogą mogązmieniać zmieniaćstan) stan) import tkinter as tk o = tk.tk( ) tk.label(o,text="etykietka:").pack() tk.button(o,text="przycisk").pack() przełącznik=0 tk.checkbutton(o,text="przełącznik",variable=przełącznik).pack() tk.entry(o,width=30).pack() tk.radiobutton(o,text="kawa",variable=przełącznik,value=1).pack() tk.radiobutton(o,text="herbata",variable=przełącznik,value=2).pack() o.mainloop()
Pierwszy Pierwszyzznich nichtoto Checkbutton, Checkbutton,który którymoże może być byćwłączony włączonyalbo albowyłączony; wyłączony;parametr parametr variable variablewskazuje wskazujezmienną, zmienną,która która odzwierciedla odzwierciedlastan stanprzełącznika przełącznika(obecnie (obecnie0); 0); widżet widżetpakujemy pakujemygo good odrazu razu import tkinter as tk o = tk.tk( ) tk.label(o,text="etykietka:").pack() tk.button(o,text="przycisk").pack() przełącznik=0 tk.checkbutton(o,text="przełącznik",variable=przełącznik).pack() tk.entry(o,width=30).pack() tk.radiobutton(o,text="kawa",variable=przełącznik,value=1).pack() tk.radiobutton(o,text="herbata",variable=przełącznik,value=2).pack() o.mainloop()
Entry Programowanie wizualne Entry to tojednowierszowe jednowierszowepole poleedycyjne edycyjneoo zadanej zadanejszerokości szerokości(tu (tu30 30znaków, znaków,nie niepiksli!); piksli!); od odrazu razupakujemy pakujemyjejedo dookna oknamacierzystego. macierzystego. import tkinter as tk o = tk.tk( ) tk.label(o,text="etykietka:").pack() tk.button(o,text="przycisk").pack() przełącznik=0 tk.checkbutton(o,text="przełącznik",variable=przełącznik).pack() tk.entry(o,width=30).pack() tk.radiobutton(o,text="kawa",variable=przełącznik,value=1).pack() tk.radiobutton(o,text="herbata",variable=przełącznik,value=2).pack() o.mainloop()
Drugi Drugizzwielostanowych wielostanowychwidżetów widżetówtoto RadioButton RadioButton (mamy (mamyich ichdwie dwiesztuki); sztuki);fakt fakt powiązania powiązaniazztątąsamą samązmienną zmienną(parametr (parametr variable) variable)czyni czynizznich nichgrupę; grupę;parametr parametrvalue value przypisuje przypisujewartość wartośćkażdemu każdemuzznich nich import tkinter as tk o = tk.tk( ) tk.label(o,text="etykietka:").pack() tk.button(o,text="przycisk").pack() przełącznik=0 tk.checkbutton(o,text="przełącznik",variable=przełącznik).pack() tk.entry(o,width=30).pack() tk.radiobutton(o,text="kawa",variable=przełącznik,value=1).pack() tk.radiobutton(o,text="herbata",variable=przełącznik,value=2).pack() o.mainloop()
Do Dodzieła! dzieła! import tkinter as tk o = tk.tk( ) tk.label(o,text="etykietka:").pack() tk.button(o,text="przycisk").pack() przełącznik=0 tk.checkbutton(o,text="przełącznik",variable=przełącznik).pack() tk.entry(o,width=30).pack() tk.radiobutton(o,text="kawa",variable=przełącznik,value=1).pack() tk.radiobutton(o,text="herbata",variable=przełącznik,value=2).pack() o.mainloop()
A teraz......wprowadzimy trzy modyfikacje: 1. skrócimy zapis używając import * 2. pokażemy działanie opcji fill 3. wprowadzimy do okna ramkę
Programowanie wizualne import import**wykluczy wykluczykonieczność koniecznośćkwalifikacji kwalifikacji from tkinter import * o = Tk( ) r = Frame(o, height=30, width=100, bg="blue") Label(o,text="Etykietka:").pack() Button(o,text="Przycisk").pack(fill=X) przełącznik=0 Checkbutton(o,text="Przełącznik",variable=przełącznik).pack() Entry(o,width=30).pack() Radiobutton(o,text="Kawa",variable=przełącznik,value=1).pack() Radiobutton(o,text="Herbata",variable=przełącznik,value=2).pack() r.pack() o.mainloop() 02_15.py
Ramka totopotencjalnie Ramkawizualne potencjalnieniewidoczny niewidocznyelement element Programowanie GUI GUI sprawimy, sprawimy,że żesię sięukaże ukażenadając nadającmu mu kolor kolorparametrem parametrembg bg(background) (background) from tkinter import * o = Tk( ) r = Frame(o, height=30, width=100, bg="blue") Label(o,text="Etykietka:").pack() Button(o,text="Przycisk").pack(fill=X) przełącznik=0 Checkbutton(o,text="Przełącznik",variable=przełącznik).pack() Entry(o,width=30).pack() Radiobutton(o,text="Kawa",variable=przełącznik,value=1).pack() Radiobutton(o,text="Herbata",variable=przełącznik,value=2).pack() r.pack() o.mainloop()
ProgramowanieRozciągniemy wizualne Rozciągniemyprzycisk przyciskwwpoziomie poziomie from tkinter import * o = Tk( ) r = Frame(o, height=30, width=100, bg="blue") Label(o,text="Etykietka:").pack() Button(o,text="Przycisk").pack(fill=X) przełącznik=0 Checkbutton(o,text="Przełącznik",variable=przełącznik).pack() Entry(o,width=30).pack() Radiobutton(o,text="Kawa",variable=przełącznik,value=1).pack() Radiobutton(o,text="Herbata",variable=przełącznik,value=2).pack() r.pack() o.mainloop()
Kolory: zawsze wtedy, gdy pewien parametr/właściwość określa kolor widżetu lub jego elementu, można użyć napisu o postaci: "#RRGGBB", gdzie RR,GG,BB są wartościami szesnastkowymi (00..FF 0..255) określającymi nasycenie koloru: R czerwonego, G zielonego, B niebieskiego (jest to tzw. addytywne mieszanie barw w modelu RGB) "#000000" czerń "#FFFFFF" biel "#00FF00" czysta zieleń "#00FFFF" turkus (?) angielskiej nazwy koloru podstawowego ("white", "black", "red", "green", "blue", "cyan", "yellow", "magenta") jednej z ponad 750 predefiniowanych nazw kolorów rozpoznawanych przez TkInter, których pełna lista dostępna jest pod adresem: http://www.tcl.tk/man/tcl8.4/tkcmd/colors.htm
from tkinter import * o = Tk( ) Frame(o, height=30, width=100, bg="yellow").pack(); Frame(o, height=30, width=100, bg="#ff00ff").pack() Frame(o, height=30, width=100, bg="dark slate blue").pack(); o.mainloop() 02_16.py