Grafički interfejs

Tkinter

Do sada je jedini način komunikacije između programa i korisnika bio putem tastature i input naredbe. Većina praktičnih programa koristi prozore (windows), dugmiće, skrolovanje, kao i mnoge druge vizuelene elemente. Ovi takozvani vidžeti (widgets) su deo onoga što se naziva Grafički korisnički interfejs (Graphical User Interface - skraćeno GUI). Ovo poglavlje se bavi GUI programiranjem korišćenjem Tkinter modula.

Vidžeti koje ćemo koristiti imaju puno opcija kojima se ovde ne možemo baviti. Odlična referenca za Tkinter je Tkinter Tutorial

Osnovni koncepti

Svaki GUI program koji ćemo pisati sadrži sledeće tri linije koda:

from tkinter import *
root = Tk()
mainloop()

Prva linija importuje sve GUI stvari iz tkinter modula. Druga linija kreira jedan prozor na ekranu, koji zovemo root ( ili bilo koje drugo ime koje mu damo). Treća linija stavlja program u jednu beskonačnu while petlju koja se naziva petlja događaja (event loop). Ova petlja radi tako što čeka na pritisak neke tipke na tastaturi, ili na klik na mišu, ili na neki drugi događaj, sve dok korisnik ne zatvori prozor.

Evo kako izgleda GUI program koji konvertuje temperaturu iz Farenhajta u Celzijuse.

from tkinter import *

        def calculate():
                temp = int(entry.get())
                temp = 9/5*temp+32
                output_label.configure(text = 'Converted: {:.1f}'.format(temp))
                entry.delete(0,END)

        root = Tk()

        message_label = Label(text='Enter a temperature',font=('Verdana', 16))
        output_label = Label(font=('Verdana', 16))
        entry = Entry(font=('Verdana', 16), width=4)
        calc_button = Button(text='Ok', font=('Verdana', 16),command=calculate)

        message_label.grid(row=0, column=0)
        entry.grid(row=0, column=1)
        calc_button.grid(row=0, column=2)
        output_label.grid(row=1, column=0, columnspan=3)

        mainloop()

Sada ćemo analizirati delove ovo programa.

Labele (Labels)

Labela je mesto na koje vaš program može da prikaže tekst na ekranu. Sledeći kod kreira labelu i smešta je na određenu poziciju na ekranu.

hello_label = Label(text='hello')
hello_label.grid(row=0, column=0)

Koristimo funkciju Label za kreiranje nove labele. Kreiranu labelu smo nazvali hello_label. Nakon kreiranja, iskoristili smo grid metodu da labelu smestimo na ekran. Metodu grid ćemo objasniti malo kasnije.

Opcije (Options)

Postoji niz opcija koje možete menjati, uključujući veličinu fonta (font size) i boju (color). Evo primera:

hello_label = Label(text='hello', font=('Verdana', 24, 'bold'), bg='blue', fg='white')

Uočite kako pri pozivu funkcije Label koristimo argumente sa ključem (keyword arguments). Evo nekoliko uobičajenih opcija za lebelu:

  • font — Osnovna struktura za opciju font je font= (naziv fonta, veličina fonta, stil). Možete izostaviti veličinu fonta i stil, ako želite. Opcije za stil su ‘bold’, ‘italic’, ‘underline’, ‘overstrike’, ‘roman’, and ‘normal’ (ova opcija se podrazumeva). Možete kombinovati više stilova kao na primer: ‘bold italic’.

  • fg and bg — Ove opcije su za foreground i background boje. Možete koristiti uobičajena imena za boje, kao na primer ‘blue’, ‘green’, itd. Za boju možete koristiti i izraze tipa : ‘#A202FF’ kojima se može specificirati bilo koja boja.

  • width — Ovime se označava kolko broj znakova (characters) koje labela može da ima. Ako ne zadate ovaj parametar, Tkinter će ga automatski postaviti na broj znakova koje inicijalno unesete u tekst labele. Ovo može da rezultira neočekivanim rezultatima, pa je bolje da unapred specificirate dužinu teksta u labeli.

  • height — Ovime se označava koliko će redova imati vaša lebela. Na taj način se kreiraju višelinijske labele. Koristite specijalni znak za novu liniji (\n) kada pište tekst labele. Na primer, text=’hi\nthere’.

Postoji još puno opcija koje možete pronaći u gore navedenoj referenci za Tkinter.

Promena label opcija

Kasnije u programu, nakon što ste kreirali labelu, možete poželeti da menjate nešto u njoj. Da to učinite, koristite metodu configure. Slede dva primera u kojima se menja labela čije je ime label:

label.configure(text='Bye')
label.configure(bg='white', fg='black')

Postavljanje teksta u labeli pomoću metode configure je slično print naredbi. Međutim, pri pozivu metode configure ne možemo da koristimo zareze odvajanje više stvari kao u print naredbi. Umesto toga moramo da korsitimo formatizaciju stringova. Evo jedne print naredbe i njenog ekvivalenta koriščenjem configure metode:

print('a =', a, 'and b =', b)
label.configure(text='a = {}, and b = {}'.format(a,b))

Metoda configure se može koristiti i za većinu drugih vidžeta, kao što ćemo videti kasnije.

Mreža (grid)

Metoda grid se koristi da se stvari postave na ekran. Ona posmatra ekran kao pravougaonik sastavljen od redova i kolona. Nekolko prvih redova i kolona su kako sledi:

(row=0, column=0)       (row=0, column=1)       (row=0, column=2)
(row=1, column=0)       (row=1, column=1)       (row=1, column=2)
(row=2, column=0)       (row=2, column=1)       (row=2, column=2)

Spajanje (Spanning) više redova i kolona

Postoje opcioni argumenti, rowspan i columnspan, koji omogućavaju da neki vidžet zauzme više od jednog reda i/ili kolone. Evo jednog primera sa više grid naredbi:

label1.grid(row=0, column=0)
label2.grid(row=0, column=1)
label3.grid(row=1, column=0, columnspan=2)
label4.grid(row=1, column=2)
label5.grid(row=2, column=2)

Razmaci između vidžeta

Da dodamo dodatne razmake između vidžeta koristimo opcione argumente padx i pady.

Važna napomena

Kad god kreirate novi vidžet, da bi ga posatvili na ekran morate koristiti grid (ili neki sličan metod, kao šro je pack, o kojem će biti reči kasnije). Ako to ne učinite vidžet neće biti prikazan na ekranu.

Input (entry) boks

Input boks je način na koji GUI prihvata tekstualni ulaz. Sledeći primer kreira jedan input boks i postavlja ga na ekran:

entry = Entry()
entry.grid(row=0, column=0)

Skoro sve opcije koje smo koristili kod labela mogu se koristiti i kod input boksa (kao i kod većine drugih vidžeta sa kojima ćemo raditi). Posebno je korisna width opcija kojom se specificira potrebna veličina boksa

Preuzimanje teksta iz boksa

Da preuzmemo tekst iz input boksa, koristimo metodu get. Ona nam vraća string. Ako vam treba numerički podatak onda možete koristiti eval funkciju (ili int i float funkcije). Ovde je dat jedan jednostavan primer preuzimanja podatka iz input boksa koji smo nazvali entry:

string_value = entry.get()
num_value = eval(entry.get())

Brisanje teksta iz boksa

Da izbrišemo tekst iz boksa koristimo sledeći način:

entry.delete(0,END)

Ubacivanje teksta u boks

Da ubacimo tekst u boks koristimo sledeće:

entry.insert(0, 'hello')

Dugmad

U sledećem primeru kreira se jedno jednostavno dugme (button):

ok_button = Button(text='Ok')

Ako želimo da dugme nešto radi, tada koristimo još jedan argumet, argument command. On se postavlja na naziv funkcije, koju nazivamo povratnom funkcijom (callback function). Kada se klikne na dugme poziva se ta fukcija. Evo primera:

from tkinter import *

def callback():
        label.configure(text='Button clicked')

root = Tk()
label = Label(text='Not clicked')
button = Button(text='Click me', command=callback)

label.grid(row=0, column=0)
button.grid(row=1, column=0)

mainloop()

Kada se program startuje labela kaže „Click me”. Kada kliknemo na dugme, povratna funkcija callback se poziva, koja menja labelu koja sada kaže „Button clicked”.

Kao što smo ranije rekli postoji još puno vidžeta koje možemo koristiti u prozorima. Sve to možete naci u pomenutoj referenci: Python Tkinter Tutorial.

Ovde ćemo se baviti samo sa još dva vidžeta koji nam omogućavaju da postavljavo slike na ekran, kao i da crtamo na ekranu.

Slike (Images)

Labele i dugmad ( kao i drugi vidžeti) mogu da prikazuju slike umesto teksta. Korišćenje slika zahteva malu pripremu. Prvo moramo da kreiramo PhotoImage objekat i da mu damo ime. Evo kako se to radi:

cheetah_image = PhotoImage(file='cheetahs.gif')

A evo i nekoliko primera postavljanja slika u vidžete:

label = Label(image=cheetah_image)
button = Button(image=cheetah_image, command=cheetah_callback())

You can use the configure method to set or change an image:

label.configure(image=cheetah_image)

Tipovi fajlova sa slikama

Jedno od ograničenja Tkinter-a je što se mogu koristiti samo GIF slike. Ako želite da koristite druge tipove fajlova, jedno od rešenja je da koristite Python Imaging Library kojom možete konvertovati druge formate slike u GIF.

Platno (Canvas)

Vidžet canvas je mesto na kojem možete da crtate linije, krugove, pravougaonike. Tu možete, takođe, da crtate tekst, postavljate slike i druge vidžete. Ovo je jedan veoma pogodan vidžet, mi ćemo ovde prikazati samo neke bazične mogućnosti.

Kreiranje platna

Sledeća linija koda kreira platno veličine 200 x 200 pikslova, sa belom pozadinom:

canvas = Canvas(width=200, height=200, bg='white')

Pravougaonici

Sledeći program crta crveni pravougaonika na platnu canvas:

canvas.create_rectangle(20,100,30,150, fill='red')

Prva četiri argumenta specificiraju koordinate pravougaonika na platnu. Gornji levi ugao platna je koordinatni početak (0,0), Gornji levi ugao pravougaonika je sa koordinatama (20,10), a donji desni ugao pravougaonika ima koordinate (30,150). Da je izostavljena opcija fill=’red’, rezultat bi bio pravougaonik sa crnim okvirom.

Ovali i linije

Crtanje ovala i linija se vrši na sličan način. Evo primera:

canvas.create_rectangle(20,100,70,180)
canvas.create_oval(20,100,70,180, fill='blue')
canvas.create_line(20,100,70,180, fill='green')

Pravougaonik je ovde da pokaže da linije i ovali rade na sličan način kao i pravougaonici. Prve dve koordinate su za levo gore a druge dve ya desno dole.

Da prikažemo krug sa poluprečnikom r i centrom u (x,z) koristimo sledeću funkciju:

def create_circle(x,y,r):
        canvas.create_oval(x-r,y-r,x+r,y+r)

Slike

Na platno možemo dodati sliku, kao u sledećem primeru:

cheetah_image = PhotoImage(file='cheetahs.gif')
canvas.create_image(50,50, image=cheetah_image)

Dve koordinate pokazuju gde je centar slike

Imenovanje, promena imena, pomeranje i brisanje

Možemo davati imena stvarima koje postavljamo na platno. Možemo, zatim, koristiti ta imana da pomeramo ili brišemo objekte sa platna. Evo jednog primera u kojem kreiramo pravougaonik, menjamo njegovu boju, pomeramo ga i na kraju brišemo:

rect = canvas.create_rectangle(0,0,20,20)
canvas.itemconfigure(rect, fill='red')
canvas.coords(rect,40,40,60,60)
canvas.delete(rect)

Metoda coords se koristi za pomeranje ili promenu veličine objekta, a metoda delete za brisanje objekta. Ako želite da obrišete sve na platnu, onda koristite naredbu:

canvas.delete(ALL)