Knjiga - mrežno i paralelno programiranje

10. Događaji

Da bi se dogodilo bilo što kad kliknemo mišem na gumb ili npr. okvir,  trebamo:

  • registrirati taj događaj
  • reći što program treba napraviti u toj situaciji

Klik na gumb je jedan standardni događaj unutar programa s grafičkim korisničkim sučeljem. Općenito, događaj je neka radnja koja se može dogoditi na nekom elementu grafičkog korisničkog sučelja.

Primjeri događaja kod gumba:

  • klik na gumb
  • dvostruki klik

Primjeri događaja kod okvira za unos teksta:

  • unos teksta u taj okvir
  • klik mišem u taj okvir

U lekciji 9. Okviri za unos teksta i naljepnice imali smo zadatak 1 u kojem je trebalo nacrtati prozor s dva okvira za tekst, gumbom Zbroji te naljepnicom na kojoj piše tekst 0.

Sada ćemo napraviti  funkciju (Click) koja će:

  • pročitati brojeve iz okvira za unos teksta (A i B)
  • zbrojiti ih
  • zapisati rezultat na naljepnicu (R)

Kada imamo spremnu ovu metodu trebamo još u konstruktor gumba dodati parametar command te mu za vrijednost postaviti funkciju Click().

Parametrom command definiramo naredbu koja se izvodi klikom na gumb.

Ako je funkcija Click() definirana kao metoda klase Program, instanciranje klase Button će imati sljedeći oblik:

 self.Z = Button(self, text = 'Zbroji', command = self.Click)

Kod poziva funkcije Click ne pišemo zagrade (poslije ćemo vidjeti što bi bilo da smo napisali zagrade).

Okviri za unos teksta te naljepnica su parametri klase Program  (pogledajte u 9. prvi i drugi okvir za tekst) te im kao takvima možemo pristupiti iz bilo koje metode klase, pa i iz metode Click().

Sadržaj nekog okvira za tekst dohvatit ćemo metodom:

 get()

koju ćemo pozvati nad pripadnim objektom. Primijetimo da su sadržaji okvira za tekst stringovi te ćemo ih, budući da sadržaje tretiramo kao brojeve, trebati funkcijom int() pretvoriti u cijele brojeve.

Dio programa koji će preuzeti sadržaje okvira za tekst (A i B) te ih kao cijele brojeve spremiti u varijable (a i b) je:

a = int(self.A.get())
b = int(self.B.get())

Kad se u varijablama a i b nalaze odgovarajuće vrijednosti treba zbrojiti sadržaje varijabli a i b:

c = a + b

Zbroj treba zapisati na naljepnicu R. Tekst koji piše na naljepnici definiramo parametrom text i inicijalno je na naljepnici R taj tekst 0. 
Podsjetnik (cijeli program je u 9. Okviri za unos teksta i naljepnice):

 #Naljepnica
        self.R = Label(self, text = '0')
        self.R.grid(row = 4, column = 1)

Vrijednost parametra text promijenit ćemo metodom config().

Zbroj (c) ćemo postaviti kao tekst naljepnice naredbom:

self.R.config(text = c)

Sada imamo gotovu funkciju Click() :

def Click(self):
        a = int(self.A.get())
        b = int(self.B.get())
        c = a + b
        self.R.config(text = c)
        return

Vratimo se sada na definiranje vrijednosti parametra command prilikom kreiranja instance klase Button. Dakle, pisali smo:

self.Z = Button(self, text = 'Zbroji', command = self.Click)

Postavlja se pitanje što bi bilo da smo iza naziva funkcije postavili zagrade, tj. da smo pozvali funkciju na standardan način:

self.Z = Button(self, text = 'Zbroji', command = self.Click())

Ukoliko to napravimo pri samom pokretanju programa pojavit će se greška:

ValueError: invalid literal for int() with base 10: ''

Naime, što se je dogodilo? Ukoliko funkciju pozovemo sa zagradama ona će se prilikom instanciranja gumba odmah i izvesti.

Prilikom izvođenja funkcije doći će do problema zbog pokušaja pretvorbe sadržaja okvira za tekst (A) u broj. Naime, dok u okvir za tekst ne unesemo broj, okvir za tekst je prazan, njegov sadržaj je prazan string. Prazan string nije broj i zbog toga dolazi do greške pri izvođenju naredbe: a = int(self.A.get()).

To bismo mogli izbjeći na način da prilikom kreiranja okvira za tekst u njega inicijalno upišemo neki broj, međutim to i dalje ne bi bilo dobro, naime, funkcija Click() se ne treba izvoditi odmah prilikom kreiranja gumba već u trenutku kada korisnik klikne na gumb. Štoviše, u slučaju kad kreiramo gumb s pozivom funkcije Click() sa zagradama ova se funkcija neće pozivati prilikom klika na gumb. Međutim, postojat će situacije kada ćemo ipak koristiti i zagrade.

Varijable za kontroliranje sadržaja elemenata sučelja

Često se za dohvaćanje i izmjenu vrijednosti (teksta) na elementima sučelja koriste posebne varijable.

Radi se o specijalnoj klasi StringVar. Nad ovom klasom definirane su dvije metode:

get() – vraća vrijednost varijable
set(v) – postavlja vrijednost varijable na vrijednost v

Ovakvu varijablu potrebno je povezati s elementom sučelja parametrom textvariable.

U našem slučaju treba je kreirati ovakve varijable za okvire za tekst i naljepnicu.

Sadržaj gumba  nećemo dohvaćati niti mijenjati pa za njega nije potrebna ovakva varijabla.

Radi lakšeg snalaženja, nazivi ovih varijabli bit će oblika: t  + [naziv elementa sučelja koji varijabla kontrolira], primjerice za okvir za unos teksta A ta će varijabla imati naziv: tA.

Konačna verzija programa s varijablama za kontroliranje sadržaja elemenata sučelja bi izgledala ovako:

from tkinter import *

class Program(Frame):
    def __init__(self, root):
        self.root = root
        self.root.title('Zbrajanje')
        super().__init__(self.root)
        self.grid(rows = 4, columns = 1)
        self.KreirajSucelje()
        return
 
    def KreirajSucelje(self):
        self.tA = StringVar()
        self.A = Entry(self, textvariable = self.tA)
        self.A.grid(row = 1, column = 1, padx = 2, pady = 2)
        self.tB = StringVar()
        self.B = Entry(self, textvariable = self.tB)
        self.B.grid(row = 2, column = 1, padx = 2, pady = 2)
        self.Z = Button(self, text = 'Zbroji', command = self.Click)
        self.Z.grid(row = 3, column = 1, padx = 2, pady = 2)
        self.tR = StringVar()
        self.tR.set(0)
        self.R = Label(self, textvariable = self.tR)
        self.R.grid(row = 4, column = 1, padx = 2, pady = 2)
        return
 
    def Click(self):
        a = int(self.tA.get())
        b = int(self.tB.get())
        c = a + b
        self.tR.set(c)
        return
 
def main():
    p = Program(Tk())
    mainloop()
 
main() 

Za dohvaćanje i izmjenu sadržaja nekih elemenata sučelja koristit ćemo ovakve varijable.

Vaš zadatak je kopirati program u Python, isprobati ga i predati na linku 20. 5. - Događaji.