Język Python pozwala na proste tworzenie programów wykorzystujących graficzny interfejs użytkownika dzięki modułowi Tkinter.
Zacznijmy od prostego przykładu:
from Tkinter import *
okno = Tk()
etykieta = Label(okno, text='Hej!')
etykieta.pack()
okno.mainloop()
Powyższy program stworzy nowe okno (okno = Tk()), umieści w nim etykietę (kontrolkę wyświetlającą napis, w tym przypadku „Hej!”) (etykieta = Label(okno, text='Hej!')), dopasuje wielkość etykiety do długości napisu (etykieta.pack()), a następnie wyświetli okno z etykietą i przejdzie do trybu oczekiwania na akcję użytkownika (okno.mainloop()). Po uruchomieniu, powinniśmy zobaczyć na ekranie nowe okno zawierające napis „Hej!”. Program jest tak prosty, że jedyną sensowną akcją użytkownika jest zamknięcie okna poprzez kliknięcie na krzyżyk w prawym górnym rogu okna.
Spróbujemy teraz zmienić wygląd okna poprzez zmianę wybranych opcji wyświetlanych komponentów.
from Tkinter import *
okno = Tk()
etykieta = Label(okno, text="Hej!", font=("Arial",24,"italic"),
foreground="yellow", background="blue")
etykieta.pack(expand=YES, fill=BOTH)
okno.mainloop()
Opcja font określa czcionkę użytą do wyświetlania tekstu (krój, wielkość oraz styl – w tym przypadku pochylona czcionka Arial o rozmiarze 24). Opcja foreground określa kolor, w którym wyświetlany jest tekst, natomiast background kolor tła kontrolki. Tabela 1 zawiera listę najczęściej używanych opcji, pogrupowanych według rodzaju.
Tabela 1. Opcje komponentów graficznych
Parametr(y) |
Znaczenie |
background foreground |
Kolor tła/tekstu |
activebackground activeforeground |
Kolor tła/tekstu, |
selectbackground selectforeground |
Kolor zaznaczonego tła/tekstu |
width height |
Szerokość/wysokość |
padx pady |
Margines poziomy i pionowy |
text |
Napis na kontrolce |
image |
Obraz wyświetlany na kontrolce |
relief |
Typ ramki (FLAT, GROOVE, RAISED, RIDGED, SOLID, SUNKEN) |
borderwidth |
Grubość ramki |
font |
Czcionka wyświetlanego tekstu |
command |
Funkcja, która wykona się po kliknięciu |
underline |
Numer litery – skrótu klawiaturowego |
takefocus |
Czy kontrolka może być wybrana tabulatorem |
Metoda pack również pozwala na określenie opcji jej działania. Nadanie opcji expand wartości YES nakazuje powiększanie kontrolki w przypadku zmiany rozmiaru okna, natomiast wartość BOTH opcji fill oznacza, że kontrolka ma zajmować całą dostępną powierzchnię okna w obu kierunkach (to znaczy, w poziomie i pionie). Tabela 2 zawiera listę najczęściej używanych opcji metody pack.
Tabela 2. Opcje układania komponentów graficznych
Parametr(y) |
Przyjmowane wartości |
Znaczenie |
side |
LEFT, RIGHT, TOP, BOTTOM |
Strona okna, po której zostanie umieszczona kontrolka (lewo, prawo, góra, dół) |
expand |
YES, NO |
Czy kontrolka ma powiększyć się w przypadku zmiany rozmiaru okna |
fill |
X, Y, BOTH |
W których kierunkach kontrolka może powiększyć się (w poziomie, w pionie, w obu) |
anchor |
N, NW, W, SW, S, SE, E, NE |
Punkty zakotwiczenia kontrolki (jak kierunki świata – północ (góra), południe (dół), itd.) |
padx pady |
Liczba pikseli |
Odstęp poziomy i pionowy |
Jeżeli uruchomimy teraz zmieniony program, zobaczymy niebieskie okno z dużym żółtym napisem „Hej!”. Jeżeli powiększymy okno (przeciągając jego krawędzie), zobaczymy, że etykieta nadal zajmuje jego całą powierzchnię; sam napis znajduje się cały czas pośrodku okna.
Kolejny przykład pokaże nam, jak obsługiwać zdarzenia. Ponownie zmodyfikujmy program:
from Tkinter import *
def zamknij(zdarzenie):
okno.quit()
okno.destroy()
okno = Tk()
etykieta = Label(okno, text="Hej!", font=("Arial",24,"italic"),
foreground="yellow", background="blue")
etykieta.pack(expand=YES, fill=BOTH)
przycisk = Button(okno, text = "Zamknij mnie !",
font=("Courier",16,"bold"), background="red")
przycisk.bind("<Button-1>", zamknij)
przycisk.pack(fill=X)
okno.mainloop()
Funkcja zamknij służy do obsługi zdarzenia (przekazanego w parametrze zdarzenie). Najpierw przerywa tryb nasłuchiwania zdarzeń (okno.quit()), a następnie zamyka okno i zwalnia zajęte przez nie zasoby (okno.destroy()).
Prócz etykiety umieszczamy teraz w oknie drugą kontrolkę, przycisk. Domyślnie, kontrolki dodawane później umieszczane są w oknie pod tymi dodanymi wcześniej. Ustalamy jego tekst, czcionkę oraz kolor tła. Następnie przypisujemy funkcji zamknij obsługę zdarzenia typu „<Button-1>” (wciśnięcie lewego przycisku myszy) dla przycisku.
Jeżeli teraz uruchomimy program, zauważymy, że przy powiększaniu okna, przycisk rozszerza się jedynie w poziomie, a jego wysokość pozostaje niezmienna. Po kliknięciu w przycisk, okno zostanie zamknięte.
Tabela 3 zawiera listę podstawowych typów zdarzeń. Opis zdarzenia ma ogólną postać „<modyfikator-typ-szczegóły>”, z których tylko typ jest zawsze wymagany. Szczegóły dookreślają o jakie zdarzenie chodzi, np.: „<Button-1>” to wciśnięcie pierwszego (lewego) przycisku myszy, a „KeyPress-return” to wciśnięcie klawisza ENTER. Modyfikator może rozszerzać lub zawężać zakres obsługiwanych zdarzeń – patrz tabela 4.
Tabela 3 Podstawowe typy zdarzeń
Zdarzenie |
Opis |
Zdarzenie |
Opis |
Activate |
Aktywowanie |
FocusIn |
Wejście do kontrolki (np. do pola edycji) |
Button |
Wciśnięty przycisk myszy |
FocusOut |
Wyjście z kontrolki |
ButtonRelease |
Zwolniony przycisk myszy |
KeyPress |
Wciśnięcie klawisza |
Configure |
Zmiana rozmiaru |
KeyRelease |
Zwolnienie klawisza |
Deactivate |
Dezaktywowanie |
Leave |
Opuszczenie wskaźnikiem myszy |
Destroy |
Usunięcie |
Motion |
Ruch wskaźnika myszy |
Enter |
Najechanie wskaźnikiem myszy |
Map, Unmap |
Obiekt umieszczony w siatce / zdjęty z niej |
Expose |
Odkrycie |
Visibility |
Obiekt stał się widoczny |
Tabela 4 Modyfikatory typów zdarzeń
Modyfikator |
Znaczenie |
Alt |
Przytrzymany klawisz ALT |
Any |
Dowolne zdarzenie z grupy (np. "<Any-KeyPress>" – wciśnięcie dowolnego klawisza) |
Control |
Przytrzymany klawisz CTRL |
Double |
Dwukrotne zdarzenie w krótkim czasie (np. dwukrotne wciśnięcie przycisku myszy) |
Lock |
Włączony klawisz CAPS LOCK |
Shift |
Przytrzymany klawisz SHIFT |
Triple |
Trzykrotne zdarzenie w krótkim czasie |
Przykładowo, przypiszmy funkcję zamknij do obsługi wciśnięcia dowolnego klawisza w oknie:
okno.bind("<Any-KeyPress>", zamknij)
Powyższą linię należy umieścić bezpośrednio nad linią:
przycisk.bind("<Button-1>", zamknij)
Jeżeli teraz uruchomimy program, zauważymy, że wciśnięcie dowolnego klawisza zamyka go.
Przejdziemy teraz do bardziej skomplikowanego przykładu. Kolejny program, nazwijmy go ‘sympatyk.py’, wyświetlać będzie komplementy pod adresem osoby podanej przez użytkownika. Kluczowymi komponentami będą:
· pole tekstowe do wpisywania imienia
· pole wyboru płci komplementowanej osoby
· przyciski z epitetami
· przycisk kończący przygotowywanie komplementów.
Przyjrzyjmy się poniższemu kodowi:
from Tkinter import *
global ap
def zamknij():
print ap.imie.get(),'jest',
if ap.opis:
if ap.kobieta.get() == YES:
for i in range(0,len(ap.opis)):
ap.opis[i]=ap.opis[i][:-1]+'a'
if len(ap.opis) == 1:
print ap.opis[0]
else:
for i in range(0,len(ap.opis)-1):
print ap.opis[i]+',',
print 'a przede wszystkim',ap.opis[len(ap.opis)-1]
else:
print "jaki jest"
ap.quit()
ap.master.destroy()
def dopisz(jaki):
ap.opis.append(jaki)
class Aplikacja(Frame):
def __init__(aplikacja, nadrzedna=None):
Frame.__init__(aplikacja, nadrzedna)
aplikacja.grid()
aplikacja.przygotuj()
aplikacja.opis=[]
def przygotuj(aplikacja):
aplikacja.imie = Entry (aplikacja)
aplikacja.imie.insert(0,"On")
aplikacja.imie.grid(row=0,column=0,columnspan=3,sticky=E+W)
aplikacja.kobieta = IntVar()
aplikacja.plec = Checkbutton(aplikacja, text='Kobieta',
variable=aplikacja.kobieta)
aplikacja.plec.grid(row=1,column=0,columnspan=3,sticky=E+W)
aplikacja.przyciski=()
epitety=['wielki','wspanialy','drogi','mocarny','genialny',
'perfekcyjny','nieomylny','idealny','niezastapiony']
for i in range(9):
aplikacja.przyciski=aplikacja.przyciski+\
(Button ( aplikacja, text=epitety[i],
command=lambda x=epitety[i]:dopisz(x) ),)
aplikacja.przyciski[i].grid(row=2+i/3,
column=i%3,sticky=E+W)
aplikacja.koniec = Button ( aplikacja, text="Koniec",
command=zamknij )
aplikacja.koniec.grid(row=5,column=0,columnspan=3,sticky=E+W)
ap = Aplikacja()
ap.master.title("Sympatyk")
ap.mainloop()
Zmienna globalna ap to cała aplikacja okienkowa służąca do przygotowania komplementów. Funkcja zamknij zamyka aplikację ap, przetwarza listę epitetów (opis) do postaci nadającej się do wyświetlenia (w tym zmienia końcówkę przymiotników dla osoby płci żeńskiej – ap.opis[i]=ap.opis[i][:-1]+'a') i wyświetla gotowy komplement.
Funkcja dopisz dodaje kolejny epitet do listy. Klasa Aplikacja opiera się na widgecie Frame (ramka), który pozwala wygodnie grupować inne komponenty. Funkcja przygotuj tworzy pole tekstowe do wpisywania imienia (imie), zmienną pamiętającą płeć osoby (kobieta), pole wyboru płci (plec), dziewięć przycisków z epitetami (przyciski) oraz przycisk zakończenia działania (koniec). Zwróćmy uwagę na sposób przypisania funkcji obsługującej kliknięcie myszą do przycisków (opcja command), a w szczególności na użycie formy lambda do przypisania różnych funkcji przyciskom z różnymi epitetami.
Komponenty rozmieszczane są w ramce z wykorzystaniem metody grid. Metoda ta dzieli obszar wyświetlania na siatkę pól. Poszczególne pola przypisujemy określonej kontrolce poprzez opcje row (numer wiersza) i column (numer kolumny). Opcja sticky pozwala określić granice obszaru, których trzymać ma się kontrolka: lewą (W), prawą (E), górną (N) i dolną (S). Opcje rowspan i columnspan pozwalają przypisać pojedynczej kontrolce więcej niż jedno pole siatki, łącząc je w większy obszar.
Metoda ap.master.title pozwala zmienić nazwę głównego okna aplikacji. Jeżeli zamkniemy aplikację od razu po uruchomieniu (klikając na przycisk ‘Koniec’), w konsoli IDLE powinniśmy zobaczyć tekst:
On jest jaki jest
Jeżeli uruchomimy aplikację ponownie, wpiszemy w miejsce słowa ‘On’ imię ‘Xawery’ i klikniemy po jednym razie na każdy z przycisków z epitetami, a następnie na przycisk ‘Koniec’, otrzymamy następujący rezultat:
Xawery jest wielki, mocarny, nieomylny, idealny, genialny, wspanialy, drogi, perfekcyjny, a przede wszystkim niezastapiony
Uruchommy program raz jeszcze, by sprawdzić jego działanie przy zmianie płci. Wpiszmy jako imię ‘Rysia’ i zaznaczmy pole wyboru ‘Kobieta’. Następnie kliknijmy na któryś z przycisków z epitetami, a następnie na przycisk ‘Koniec’; otrzymamy następujący rezultat:
Rysia jest wspaniala
Jak widać, zmiana końcówek przymiotników funkcjonuje prawidłowo.
W powyższym przykładzie użyliśmy kilku nowych komponentów graficznych. Tabela 5 zawiera listę wszystkich komponentów dostępnych w module Tkinter. Nie jest to oczywiście lista wszystkich komponentów dostępnych w Pythonie, gdyż wiele kontrolek zdefiniowanych jest w modułach innych niż Tkinter – zainteresowanych odsyłam do dokumentacji Pythona.
Tabela 5 Komponenty graficzne modułu Tkinter
Komponent |
Opis |
Komponent |
Opis |
Button |
Przycisk poleceń |
Menu |
Pasek menu |
Canvas |
Złożona grafika |
Menubutton |
Przycisk menu |
Checkbutton |
Przycisk stanu |
Message |
Wyświetla tekst |
Entry |
Pole wprowadzania tekstu |
Radiobutton |
Przycisk opcji |
Frame, Toplevel |
Zawiera inne komponenty |
Scale |
Ustawia wartość liczbową |
Label |
Wyświetla tekst lub obraz |
Scrollbar |
Pasek przewijania |
Listbox |
Lista wyboru |
Text |
Wyświetla sformatowany tekst |
Na zakończenie rozdziału jeszcze jeden przykład, tym razem demonstrujący rysowanie dowolnych kształtów w obszarze okna. Wykorzystamy w nim komponent Canvas, jedną z metod którego jest create_line(x, y, xn, yn), rysująca linię z punktu o współrzędnych x, y do punktu o współrzędnych xn, yn.
from Tkinter import *
def czysc(zdarzenie=None):
tlo.delete('all')
def zamknij(zdarzenie=None):
czysc()
okno.quit()
okno.destroy()
# poczatek linii
def poczatek(zdarzenie):
global x,y
x=zdarzenie.x
y=zdarzenie.y
# koniec linii
def rysuj(zdarzenie):
global x,y
if x is not None:
tlo.create_line(x,y,zdarzenie.x,zdarzenie.y)
x=zdarzenie.x
y=zdarzenie.y
def koncz(zdarzenie):
global x,y
rysuj(zdarzenie)
x = None
x = None
okno = Tk()
tlo = Canvas(okno,width=400, height=400, bg="white")
tlo.pack(fill=BOTH,expand=YES)
tlo.bind("<Button-1>", poczatek)
tlo.bind("<Button-3>", czysc)
tlo.bind("<ButtonRelease-1>", koncz)
tlo.bind("<Motion>", rysuj)
okno.bind("<Any-KeyPress>", zamknij)
okno.mainloop()
Po uruchomieniu programu, rysujemy linię poruszając myszą z przytrzymanym lewym przyciskiem, kasujemy rysunek prawym przyciskiem myszy, natomiast wciśnięcie któregokolwiek klawisza kończy działanie programu.
Jeżeli powyższe zasady wydają się komuś skomplikowane, możemy dodać do programu krótką instrukcję. Będzie ona dostępna w dodatkowym okienku wyświetlanym po wybraniu funkcji z menu głównego. Na początku programu, poniżej pierwszej linii (from Tkinter import *), dopiszmy:
from tkMessageBox import showinfo
def info(zdarzenie=None):
showinfo("Instrukcja", """Rysujemy linie poruszajac mysza z przytrzymanym lewym przyciskiem,
kasujemy rysunek prawym przyciskiem myszy,
wcisniecie któregokolwiek klawisza konczy dzialanie programu""")
Moduł tkMessageBox zawiera funkcje służące do wyświetlania okienek (ang. message boxes), nie tylko czysto informacyjnych (użyte wyżej showinfo, a także showwarning i showerror), ale również wymagających podania odpowiedzi przez użytkownika (askquestion, askokcancel, askyesno i askretrycancel).
Wewnątrz programu, poniżej linii okno = Tk(), wstawmy poniższy kod:
menu = Menu(okno)
menuplik = Menu(menu)
menuplik.add_command(label="Instrukcja", command=info)
menuplik.add_separator()
menuplik.add_command(label="Koniec", command=zamknij)
menu.add_cascade(label="Plik", menu=menuplik)
okno.config(menu=menu)
Menu to widget reprezentujący główne menu aplikacji; add_cascade dodaje podmenu, add_command funkcję menu, a add_separator linię oddzielającą pozycje menu. Metoda okno.config(menu=menu) aktywuje menu w podanym oknie.
Po ponownym uruchomieniu programu powinniśmy mieć do dyspozycji menu główne z dwiema funkcjami: instrukcją oraz zakończeniem pracy programu.
Ćwiczenie. Dodaj do ostatniego przykładu możliwość zmiany koloru rysowanej linii (np. pod wybranymi klawiszami i/lub w menu głównym). Wykorzystaj opcję fill metody create_line.