Graficzny interfejs użytkownika

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,
gdy kontrolka jest aktywna

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.