Knjiga - mrežno i paralelno programiranje
| Sjedište: | CARNET - Arhiva 2021 Loomen |
| E-kolegij: | Python2 |
| Knjiga: | Knjiga - mrežno i paralelno programiranje |
| Otisnuo/la: | Gost (anonimni korisnik) |
| Datum: | četvrtak, 23. travnja 2026., 13:34 |
Sadržaj
- 1. Mrežno programiranje 3
- 2. Višedretveno programiranje
- 3. Moduli i paralelno programiranje
- 4. Objekti kao parametri metoda
- 5. Modul tkinter
- 6. Tkinter - okviri i gumbi
- 7. Parametri klase Button i metode grid()
- 8. Dogovor o pisanju programa s grafičkim korisničkim sučeljem
- 9. Okviri za unos teksta i naljepnice
- 10. Događaji
- 11. Modul multithreading
- 12. Projektni zadatak
1. Mrežno programiranje 3
Zadatak
Na klijentskom računalu upisuje se broj, a poslužitelj šalje poruku je li zbroj znamenaka poslanog broja prost.
Treba dopuniti poslužiteljsku aplikaciju i provjeriti radi li te predati na linku mrezno3
U naredbenom retku ili drugom računalu pokrenite poslužiteljsku aplikaciju, a standardno iz Pythona pokrenite klijentsku aplikaciju.
##poslužitelj
from socket import *
class Posluzitelj:
def __init__(self, host, port):
self.s = socket()
self.h = host
self.p = port
self.s.bind((self.h, self.p))
self.s.listen(5)
print('Čekam klijenta...')
c, a = self.s.accept()
print('Spojio se',a)
self.komuniciraj(c)
c.close()
def prost(self,x):
for i xxxxxxxxxx
if xxxxxxxxxx
return False
return True
def suma(self,x):
xxxxx
while x > 0:
xxxxxxxxxx
xxxxxxxxxx
return s
def komuniciraj(self,c):
while True:
x = int(c.recv(1024).decode('utf-8'))
if x < 2:
c.send('Kraj'.encode('utf-8'))
return
rezultat = str(self.prost(self.suma(x)))
c.send(rezultat.encode('utf-8'))
Posluzitelj('DESKTOP-59PTT3J',10000)
##klijent
from socket import *
class Klijent:
def __init__(self, host, port):
self.s = socket()
self.h = host
self.p = port
self.s.connect((self.h,self.p))
while True:
x = input('Upiši broj: ')
self.s.send(x.encode('utf-8'))
rezultat = self.s.recv(1024).decode('utf-8')
if rezultat == 'True':
print('Zbroj znamenaka broja {} je prost'.format(x))
elif rezultat=='False':
print('Zbroj znamenaka broja {} nije prost'.format(x))
else:
print('Kraj...')
break
Klijent('DESKTOP-59PTT3J',10000)
Podsjetnik:
Unutar naredbenog retka možete saznati:
IPv4 adresu:
C:\Users\Korisnik>ipconfig
.............................................
IPv4 Address. . . . . . . . . . . : 192.168.1.3
Ime računala:
C:\Users\Korisnik>hostname
DESKTOP-59PTT3J
2. Višedretveno programiranje
Istovremeno odvijanje zadaća unutar jednog programa – višedretveno programiranje
U prethodnim smo primjerima imali jedan poslužitelj na kojega se mogao spojiti jedan ili više klijenata te su oni putem dogovorenog protokola komunicirali. U takvoj komunikaciji bili smo izuzetno ograničeni. Primjerice, čak i kada se na poslužitelj moglo spojiti više klijenata oni su, nakon što su poslali jednu poruku poslužitelju, mogli samo čekati poruke od poslužitelja. Takvi primjeri su u praksi vrlo rijetki. U nastavku će biti riječi o jednom realnijem problemu. Ideja problema je:
Kreirajmo mrežnu aplikaciju koja će se sastojati od poslužitelja na kojeg će se moći spojiti više klijenata. Nakon što se spoje, klijenti će moći poslužitelju slati upite koji će se odnositi na to je li broj koji je klijent poslao poslužitelju prost ili složen. Poslužitelj treba omogućiti bilo kojem klijentu da u bilo koje vrijeme može poslati upit. Isto tako u bilo kojem trenutku na računalo se može spojiti novi klijent, itd.
Aktivnosti na strani klijenta nakon što se spoji na poslužitelj:
- čeka da korisnik upiše broj
- pošalje broj poslužitelju
- čeka da poslužitelj vrati poruku
- ispiše poruku na ekranu
Nakon što se uspostavi komunikacija između klijenta i poslužitelja na klijentskoj strani će se izvoditi jedna petlja koja će izvoditi gore opisane radnje.
Slika: Petlja koja se izvodi na strani klijenta nakon uspostave komunikacije s poslužiteljem
Na strani poslužitelja je situacija nešto kompleksnija jer poslužitelje treba istovremeno:
- čekati i prihvaćati nove klijente
- za svakog klijenta na socketu čekati poruku (broj), za dobiveni broj provjeriti je li prost te pripadnom klijentu vratiti odgovarajući poruku.
Ne postoji pravilo po kojem klijenti šalju zahtjeve poslužitelju. Ako je u nekom trenutku na poslužitelj spojeno 3 klijenta (klijent 1, klijent 2, klijent 3) tada oni primjerice zahtjeve mogu slati na sljedeći način:
· klijent 1: 7
· Klijent 1: 9
· klijent 2: 3
· klijent 1: 5
· klijent 2: 19
· klijent 2: 22
· klijent 3: 14
· …
Može se dogoditi da jedan klijent pošalje nekoliko zahtjeva za redom, neki drugi klijent može slati zahtjeve vrlo rijetko itd.
Dakle, nećemo moći imati jednu petlju unutar koje ćemo redom zaprimati zahtjeve od prvog klijenta, zatim drugog, potom trećeg itd. Dakle „istovremeno“ ćemo morati čekati poruke na socketima svih klijenata što znači da bi se za svakog klijenta na poslužitelju trebala neovisno jedna od druge odvijati petlja sljedećeg oblika.
Slika: Petlja koja bi se na poslužitelju istovremeno trebala odvijati za sve klijente
(za svakog klijenta po jedna ovakva petlja)
Istovremeno sa svim tim petljama poslužitelj treba još i izvoditi još jednu petlju unutar koje će prihvaćati nove klijente.
Problem: u našim dosadašnjim mrežnim programima petlje su bile na neki način beskonačne (izvode se dok je klijet spojen) i ne mogu slijediti jedna nakon druge (prva nikada nije gotova…). Iz istog razloga one ne mogu biti niti jedna unutar druge.
U ovom i sličnim primjerima koristit ćemo tzv. dretve (engl. Thread). Na dretvu možemo gledati kao na program koji je u mogućnosti pokrenuti se unutar nekog „glavnog“ programa te će se u tom slučaju izvoditi paralelno s glavnim programom.
Dakle, na strani poslužitelja imat ćemo:
- glavnu dretvu – čeka novog klijenta te ga prihvaća
- za svakog klijenta po jedna dretva – čeka poruku (broj) od klijenta; provjerava je li broj prost; vraća pripadnom klijentu odgovarajuću poruku.
Lako se uočava da će broj dretvi na poslužitelju biti ukupno n + 1, pri čemu je n broj klijenata koji su u nekom trenutku spojeni na poslužitelj.
Rad s dretvama – modul threading
Za rad s dretvama u Pythonu na raspolaganju nam je modul threading. Između ostaloga ovaj modul sadrži klasu Thread, čiji konstruktor dolazi u obliku:
Thread(target = ime_funkcije, args = (arg1, arg2,…))
Objekt tipa Thread predstavlja jednu dretvu. Dretva će u tom slučaju izvoditi neku funkciju (ime_funkcije), čije ćemo ime navesti kao parametar konstruktora klase Thread. U tom slučaju se navodi samo ime funkcije, bez parametara i zagrada. Ukoliko je funkcija definirana na način da ima nekih dodatnih parametara, oni se navode kao dodatni parametar konstruktora (args).
Nakon što smo kreirali objekt tipa Thread, nad njim se mogu izvoditi različite metode. Neke od češće korištenih metoda su:
-
start() – započinje izvođenje dretve unutar programa
-
join() – osigurava da program neće završiti sve dok pripadna dretva ne bude izvedena do kraja
3. Moduli i paralelno programiranje
Dragi moji učenici.
Današnji je zadatak kreirati aplikaciju unutar koja će se pokretati dvije dretve. Prva dretva neka ispisuje parne brojeve do 10 te nakon svakog ispisanog broja neka čeka jednu sekundu. Druga dretva neka ispisuje neparne brojeve do 10 s odgodom od dvije sekunde.
Da biste naučili kako, pogledajte prezentaciju.
Program predajte na 1. 4. Moduli i paralelno programiranje
Programi.
4. Objekti kao parametri metoda
Dragi moji učenici,
danas ćete naučiti nešto o specijalnim metodama __str__() i __repr__() u Pythonu.
Definirati klasu Osoba koja ima dva svojstva ime i visinu, a zatim kreiraj 2 objekta s ulaznim vrijednostima („Ana”, 165 i „Ivo”, 178) te metodu visina() koja će ispisati ime osobe koja je viša. *
Primjer za metodu __str__()
|
class Osoba: def __init__(self,ime): self.ime=ime
objekt = Osoba('Ana') print(objekt) |
class Osoba: def __init__(self, ime): self.ime=ime def __str__(self): return f'{self.ime}'
objekt = Osoba('Ana') print(objekt) |
|
<__main__.Osoba object at 0x0306A598> |
Ana |
Koga zanima može istraživati i ostale specijalne metode:
|
Naziv metode |
Opis |
|
__add__(self, b) |
zbrajanje ( + ) |
|
__sub__(self, b) |
oduzimanje ( - ) |
|
__mul__(self, b) |
množenje ( * ) |
|
__floordiv__(self, b) |
cjelobrojno dijeljenje ( // ) |
|
__truediv__(self, b) |
dijeljenje ( / ) |
|
__mod__(self, b) |
ostatak cjelobrojnog dijeljenja ( % ) |
|
__pow__(self, n) |
potenciranje ( ** ) |
|
__iadd__(self, t) |
+= |
|
__isub__(self, t) |
-= |
|
__imul__(self, t) |
*= |
|
__itruediv__(self, t) |
/= |
|
__ifloordiv__(self, t) |
//= |
|
__mod__(self, t) |
%= |
|
__lt__(self, b) |
manje ( < ) |
|
__le__(self, b) |
manje ili jednako ( <= ) |
|
__gt__(self, b) |
veće ( > ) |
|
__ge__(self, b) |
veće ili jednako ( >= ) |
|
__eq__(self, b) |
jednako ( == ) |
|
__ne__(self, b) |
različito ( != ) |
|
__and__(self, b) |
logički I ( and ) |
|
__or__(self, b) |
logički ILI ( or ) |
5. Modul tkinter
Programi s grafičkim korisničkim sučeljima
Programi koje smo do sada radili unosili su podatke iz komandne linije. Današnji programi za komercijalnu upotrebu imaju grafička korisnička sučelja. U Pythonu za kreiranje takvih programa služi modul tkinter.
Osnovni element svakog programa s grafičkim korisničkim sučeljem je prozor. Na njega možemo dodavati druge elemente: okviri za unos teksta, različite vrste gumba, izbornici itd.
Za svaki od navedenih elemenata, pa i za sam prozor, kreirana je posebna klasa unutar modula tkinter, a dodavanje pojedinog elementa na sučelje programa svodi se na kreiranje instance pripadne klase, postavljanje parametara te smještanje objekta na sučelje.
Osnovni prozor programa – klasa Tk
Klasa koja će kreirati osnovni prozor programa je Tk(). Kreiranjem instance ove klase na ekranu će se pojaviti prozor kao na slici.
>>> from tkinter import *
>>> t = Tk()
>>> mainloop()

Nakon kreiranja instance klase Tk() pozvali smo funkciju mainloop() koja kao i kod kornjačine grafike služi da da grafički prozor ostane aktivan sve dok ga ne zatvorimo klikom na gumb Close (
).
Naslov, veličinu ali i još niz drugih parametara prozora moguće je programski promijeniti:
- title(naslov) - definiranje naslova prozora
- config(p_1 = v_1, p_2 = v_2,...) - definiranje postavki prozora. Parametri mogu biti:
-
- background, bg - boja pozadine prozora
- borderwidth, bd - debljina ruba prozora
- cursor - oblik kursora dok je nad prozorom, neki od oblika kursora su: arrow, man, mouse, pencil, plus, clock, cross, sizing, draft_large, draft_small, exchange, hand1, hand2, heart, umbrella,...
- height- visina
- padx, pady - udaljenost sadržaja od rubova prozora
- width - širina prozora
Boju možemo zadati engleskim nazivom ili RGB kodom, a naziv boje pišemo unutar navodnika.
ZADATAK 1
Kreirajmo prozor u čijoj će naslovnoj traci pisati: Moj prvi program s grafičkim sučeljem.
Dimenzije prozora su 400 x 300 a boja pozadine prozora je plava.
Rješenje:
from tkinter import *
t = Tk()
t.config(width = 800, height = 600, bg = 'blue')
t.title('Moj prvi program s grafičkim sučeljem')
mainloop()
Nakon što pokrenemo program dobit ćemo sljedeći prozor:

Isto tako moguće je odrediti je li veličina prozora fiksna ili promjenjiva. To ćemo definirati metodom:
- resizable(width, height) - određivanje mogućnosti da se prozoru mijenja veličina po širini odnosno visini ili je veličina prozora fiksna i nepromjenjiva. Ukoliko želimo da se veličina po nekoj komponenti može mijenjati postavit ćemo vrijednost pripadnog parametra na True, a inače ćemo postaviti vrijednost parametra na False.

Na prvoj slici klikom na ikonicu za povećavanje prozora, on će se povećati preko cijelog ekrana.
Na drugoj se može povećati samo po visini, na trećoj samo po širini, a na zadnjoj slici se prozor ne može povećavati.
Još neke metode su:
- destroy() - metoda za zatvaranje prozora (može i klikom na Close)
- cget(p) - metoda za dohvaćanje vrijednosti pojedinog parametra
from tkinter import *
t = Tk()
t.config(width = 400, height = 300, bg = 'blue')
t.title('Moj prvi program s grafičkim sučeljem')
print(t.cget('width'))
print(t.cget('bg'))
mainloop()
Osim pomoću config() neki parametar možemo promijeniti na način kao u primjeru:
Današnji zadatak je riješiti kviz kojim ćete provjeriti svoje znanje o osnovnim mogućnosti modula tkinter:
https://loomen.carnet.hr/mod/quiz/view.php?id=839028
6. Tkinter - okviri i gumbi
Okviri
Na prozor se postavljaju različiti elementi sučelja.
Prije vidljivih elemenata dodat ćemo okvir (frame) koji neće biti vidljiv ali služi da na njega stavljamo ostale elemente.
Glavni prozor se može sastojati od jednog ili više okvira.
Za postavljanje okvira na prozor koristit ćemo klasu Frame.
Parametar konstruktora ove klase je objekt tipa Tk.
t = Tk()
Osim objekta tipa Tk u konstruktoru je moguće definirati i vrijednost za neki parametar.
Tri su načina na koje je moguće postavljati elemente sučelja na okvir, a zovemo ih upraviteljima geometrije prozora (eng. geometry management). Njihovi nazivi su:
- Grid – okvir se podijeli u retke i stupce te se elementi sučelja stavljaju u odgovarajuće ćelije
- Pack – za svaki element moguće je odrediti na koje mjesto okvira ćemo ga staviti (TOP, BOTTOM, LEFT, RIGHT), ukoliko više elemenata stavljamo na isto mjesto oni će se postaviti jedan pokraj drugoga
- Place – svaki element stavljamo na točno određeno mjesto (koordinate) na prozoru
Za svaki od načina postavljanja elemenata kreirana je metoda nad klasom Frame i tom metodom naglašavamo koji ćemo upravitelj koristiti. Metode su: grid(), pack() i place()
Mi ćemo se uglavnom koristiti prvim načinom smještanja elementa na sučelje – stavljat ćemo elemente u tablicu (grid), dakle koristit ćemo metodu grid().
Parametri metode grid() su:
- rows – broj redaka tablice
- columns – broj stupaca tablice
Primjer
Treba kreirati prozor veličine 800 x 600 s plavom bojom pozadine te na njemu kreirati okvir (frame) veličine 400 x 300 s crvenom bojom pozadine i gridom od 4 retka i 3 stupca.
Rješenje:
U prvom koraku kreirat ćemo prozor dimenzija prozora 400 x 300, plave boje.
from tkinter import *
t = Tk()
t.config(width=800, height=600, bg='blue')
mainloop()

Nakon plavog prozora, kreirat ćemo crveni okvir (frame) s 4 reda i 3 stupca:
from tkinter import *
t = Tk()
t.config(width=800, height=600, bg='blue')
f = Frame(t, width=400, height=300, bg='red')
f.grid(rows=4, columns=3)
mainloop()
Pokretanje ovog programa rezultirat će sljedećim prozorom (slike su u odgovarajućim omjerima):

Primijetite da se veličina i boja prozora se je prilagodila okviru.
Gumbi
- klasa Button
-
b = Button(f)
Naziv okvira na koji dodajemo gumb je prvi parametar konstruktora klase.
Najčešće korišteni parametri klase Button i metode grid() su popisani na posebnoj stranici. Pogledajte tamo ako vam zatreba neki parametar za vaš program.
Nakon što kreiramo instancu klase gumb sa svim željenim parametrima gumb je još potrebno staviti na prozor. To ćemo načiniti tako da nad gumbom pozovemo metodu grid(). U ovom slučaju metoda grid() ima nešto drugačije parametre a parametri su dani u sljedećoj tablici:
Sada konačno možemo napisati i svoj prvi program s konkretnim elementom grafičkog korisničkog sučelja.
Primjer 2
Kreirajmo grafičko korisničko sučelje koje će se sastojati od tri gumba. Dva gumba su u prvom a jedan gumb u drugom redu. Sredina gumba u drugom redu treba biti točno između dva gumba prvog reda. Na gumbima trebaju pisati redom tekstovi: Gumb A, Gumb B te Gumb C, pri čemu boje teksta trebaju biti plava, crvena i zelena te je potrebno postaviti veličinu fonta na 14 te podebljano a stil neka bude Calibri.
Rješenje:
from tkinter import *
t = Tk()
t.config()
f = Frame(t)
f.grid(rows=2, columns=2)
a = Button(f, text='Gumb A', fg='blue', font=('Calibri', 14, 'bold'))
a.grid(row = 1, column = 1)
b = Button(f, text='Gumb B', fg='green', font=('Calibri', 14, 'bold'))
b.grid(row=1, column=2)
c = Button(f, text='Gumb C', fg='red', font=('Calibri', 14, 'bold'))
c.grid(row=2, column=1, columnspan=2)
mainloop()
Rezultat:

Primjer 3
Kreirajmo grafičko korisničko sučelje s četiri gumba. Dva gumba su u prvom a dva gumb u drugom redu. Na gumbima trebaju pisati redom tekstovi: Trokut, Kvadrat, Krug i Brisanje pri čemu boje teksta biti različite. Gumbi služe za ono što piše na njima, npr. klikom na gumb Trokut, program crta trokut itd.
Treba nam paramatar command koji služi za izvođenje naredbe klikom na odgovarajući gumb.
from tkinter import *
from turtle import * def trokut():
for i in range(3):
fd(200)
lt(120)
def kvadrat():
for i in range(4):
fd(200)
lt(90)
def krug():
circle(100)
def brisanje():
reset()
prozor = Tk()
prozor.config()
f = Frame(prozor)
f.grid(rows=2, columns=2)
a = Button(f, text='Trokut ', fg='blue', font=14, command=trokut)
a.grid(row=1, column=1)
b = Button(f, text='Kvadrat ', fg='green', font=14, command=kvadrat)
b.grid(row=1, column=2)
c = Button(f, text='Krug ', fg='brown', font=14, command=krug)
c.grid(row=2, column=1)
d = Button(f, text='Brisanje', fg='red', font=14, command=brisanje)
d.grid(row=2, column=2)
mainloop()
Pokretanjem programa dobit ćemo prozor kao na slici:

Klikom na gumbe možemo dobiti različite rezultate:

Vaš je zadatak prepraviti zadnji primjer, tako da program radi bilo što drugo. Broj gumba i funkcija nije zadan. Može biti i samo jedan gumb koji nešto pokreće. Poigrajte se i stvorite svoju kreaciju .
7. Parametri klase Button i metode grid()
Najčešće korišteni parametri klase Button
|
Naziv parametra |
Opis |
|||||||||
|
text |
Tekst koji se nalazi na gumbu |
|||||||||
|
width |
širina gumba (broj slova koji će stati na gumb) |
|||||||||
|
height |
visina gumba (broj redaka slova koji mogu stati na gumb) |
|||||||||
|
bg, background |
pozadinska boja |
|||||||||
|
fg, foreground |
boja teksta |
|||||||||
|
font |
font kojim će biti ispisan sadržan gumba. Font se zadaje trojkom, pri čemu su elementi trojke:
|
|||||||||
|
anchor |
pozicija teksta na gumbu. Pozicija se definira geografskim oznakama N – gore, S – dolje, E – desno, W – lijevo, CENTER - sredina, a moguće su i kombinacije kao primjerice NE (gore desno)
|
|||||||||
|
bd, borderwidth |
debljina ruba |
|||||||||
|
command |
naredba koja se izvodi klikom na gumb |
|||||||||
|
textvariable |
naziv varijable pomoću koje se kontrolira tekst koji piše na gumbu |
|||||||||
|
cursor |
oblik pokazivača miša dok je nad gumbom |
|||||||||
|
image |
slika na gumbu |
|||||||||
|
justify |
kako će biti poravnat tekst, ukoliko je napisan kroz više redaka: LEFT, CENTER, RIGHT |
|||||||||
|
padx |
udaljenost teksta od lijevog/desnog ruba gumba |
|||||||||
|
pady |
udaljenost teksta od gornjeg/donjeg ruba gumba |
|||||||||
|
relief |
način prikaza gumba: |
|||||||||
|
state |
je li gumb aktivan – NORMAL ili je neaktivan – DISABLED |
|||||||||
|
disabledforeground |
boja teksta dok je gumb neaktivan |
|||||||||
|
highlightbackground |
pozadinska boja dok pokazivač miša nije iznad gumba |
|||||||||
|
highlightcolor |
boja teksta dok pokazivač miša nije iznad miša |
|||||||||
|
activebackground |
pozadinska boja gumba u trenutku dok se miš nalazi na gumbu |
|||||||||
|
activeforeground |
boja teksta na gumbu dok se miš nalazi iznad gumba |
Nakon što kreiramo instancu klase gumb sa svim željenim parametrima gumb je još potrebno staviti na prozor. To ćemo načiniti tako da nad gumbom pozovemo metodu grid(). U ovom slučaju metoda grid() ima nešto drugačije parametre a parametri su dani u sljedećoj tablici:
Parametri metode grid() definirane nad gumbom
|
Naziv parametra |
Opis |
|
row |
red unutar tablice u kojem će se nalaziti gumb (numeriranje započinje od 1) |
|
column |
stupac unutar tablice u kojem će se nalaziti gumb (numeriranje započinje od 1) |
|
rowspan |
broj redaka kroz koje se proteže ćelija u kojoj će se nalaziti gumb |
|
columnspan |
broj stupaca kroz koje se proteže ćelija u kojoj će se nalaziti gumb |
|
padx |
udaljenost ruba gumba od linije tablice – lijevo i desno |
|
pady |
udaljenost ruba gumba od linije tablice – gore i dolje |
|
sticky |
položaj gumba unutar ćelije tablice, vrijednosti su iste kao za parametar anchor (vidi tablicu 3.2) |
8. Dogovor o pisanju programa s grafičkim korisničkim sučeljem
Nije obavezan dio, ali pogledajte ako imate vremena, ako ne, odmah pređite na lekciju 9.
Danas je u programiranju sveopće prihvaćena paradigma objektno usmjerenog programiranja. Takav pristup koristit ćemo i mi u svojim programima. Program ćemo kreirati kao klasu, na kraju ćemo kreirati instancu klase, tj. objekt i time pokrenuti program.
Budući da ćemo se u svakom programu koristiti klasom Frame, kreirat ćemo klase koje će ju nasljeđivati. U ovom trenutku implementacija će se sastojati samo od konstruktora. Unutar njega ćemo definirati: naslov prozora, parametre vezane uz sam prozor, povezati prozor s okvirom, definirati geometriju prozora te pozvati metodu koja će kreirati ostale elemente sučelja. Sve ostale elemente sučelja definirat ćemo u posebnoj metodi KreirajSucelje() koja će u prozor dodati sve ostale elemente sučelja. Svaki element sučelja postavit ćemo kao parametar klase unutar koje se nalazi program. Ilustrirajmo to na prethodnom primjeru:
Primjer 2 iz 7. Tkinter - okviri i gumbi
from tkinter import *
t = Tk()
t.config()
f = Frame(t)
f.grid(rows=2, columns=2)
a = Button(f, text='Gumb A', fg='blue', font=('Calibri', 14, 'bold'))
a.grid(row = 1, column = 1)
b = Button(f, text='Gumb B', fg='green', font=('Calibri', 14, 'bold'))
b.grid(row=1, column=2)
c = Button(f, text='Gumb C', fg='red', font=('Calibri', 14, 'bold'))
c.grid(row=2, column=1, columnspan=2)
mainloop()
Objektno usmjereno programiranje - isti primjer napisan na drugi način:
from tkinter import *
#Klasa Program koja nasljeđuje klasu Frame
class Program(Frame):
#Konstruktor klase kao parametar ima prozor
#programa unutar kojeg će se nalaziti okvir
def __init__(self, root):
self.root = root
self.root.title('Tri gumba')
#pozivamo konstruktor klase Frame, čiji je parametar prozor programa
super().__init__(self.root)
self.grid(rows = 2, columns = 2)
self.KreirajSucelje()
return
def KreirajSucelje(self):
f = ('Calibri', 14, 'bold')
self.A = Button(self, text = 'Gumb A', fg = 'blue', font = f)
self.A.grid(row = 1, column = 1)
self.B = Button(self, text = 'Gumb B', fg = 'green', font = f)
self.B.grid(row = 1, column = 2)
self.C = Button(self, text = 'Gumb C', fg = 'red', font = f)
self.C.grid(row = 2, column = 1, columnspan = 2)
return
def main():
#Kreiramo instancu klase Program, čiji je parametar prozor glavnog programa -
#objekt tipa Tk
p = Program(Tk())
mainloop()
return
main()
9. Okviri za unos teksta i naljepnice
U nastavku ćemo se upoznati s dva nova elementa sučelja:
- okvir za unos teksta – služi za unos teksta s tipkovnice kroz grafičko sučelje – klasa Entry
- naljepnica – služi za ispis teksta na ekran – klasa Label.
Parametri kojima ćemo koristiti kod ovih elemenata sučelja ekvivalentni su parametrima za gumbe (Najčešće korišteni parametri klase Button) u 7.1. Parametri klase Button i metode grid().
Zadatak 1
Napišimo program koji će na ekranu nacrtati prozor (kao na slici) na čijoj će naslovnoj traci pisati Zbrajanje. Prozor treba imati dva okvira za tekst, jedan ispod drugoga, ispod njih treba imati gumb na kojem piše Zbroji te ispod gumba naljepnicu na kojoj piše tekst 0.

Rješenje (pogledajte u 8. Dogovor o pisanju programa s grafičkim korisničkim sučeljem da uočite sličnost):
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, padx = 5, pady = 5)
self.KreirajSucelje()
return
def KreirajSucelje(self):
#Prvi okvir za tekst
self.A = Entry(self)
self.A.grid(row = 1, column = 1)
#Drugi okvir za tekst
self.B = Entry(self)
self.B.grid(row = 2, column = 1)
#Gumb
self.Z = Button(self, text = 'Zbroji')
self.Z.grid(row = 3, column = 1)
#Naljepnica
self.R = Label(self, text = '0')
self.R.grid(row = 4, column = 1)
return
def main():
p = Program(Tk())
mainloop()
main()
Današnja zadaća je pročitati što piše u 8. Dogovor o pisanju programa s grafičkim korisničkim sučeljem i 9. Okviri za unos teksta i naljepnice te isprobati zadatak. Zadatak proučite, prekopirajte ga u Python, isprobajte i predajte na link s današnjim datumom.
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.
11. Modul multithreading
27.5.2020.
Prema Mooreovom zakonu (1965.), broj tranzistora koji se ugrađuju u procesor se udvostručuje svake godine. Problem je zagrijavanje tranzistora. Rješenje je ugradnja više procesora, odnosno jezgri u računalo. Kod više procesora svaki ima primjerice svoj cache dok kod višejezgrenih sve jezgre koriste zajednički cache.
Pokretanjem programa on se smješta u RAM, dio RAM-a alocira se i za podatke s kojima će program raditi (varijable). Program smješten u RAM ima i neka dodatna obilježja: vrijeme početka rada, vrijeme završetka,… te takav program zovemo proces.
Na istovremeno može biti pokrenuto više procesa. Procesor najčešće procese obrađuje kružnom podjelom vremena (Round Robin) koja se temelji na podjeli procesorskog vremena (Time Sharing).
Pri izvođenju procesa program izvodi instrukcijsku dretvu (thread), koja nije ista kao programski niz naredbi. Programske naredbe su one kako su zapisane u programu a instrukcijski thread su vremenski poredane naredbe, onako kako će dolaziti u procesor. Svaki proces ima jednu ili više dretvi koje se mogu izvoditi paralelno ili prividno paralelno.
Svi threadovi nekog procesa imaju zajednički memorijski prostor, za razliku od procesa gdje svaki proces ima svoj memorijski prostor.
Za rad s procesima koristit ćemo modul multithreading.
Funkcija cpu_count() vraća broj jezgri, odnosno procesa računala.
Primjer 1.
Provjerimo broj procesora (jezgri) računala.
Rješenje:
>>> from multiprocessing import *
>>> cpu_count()
8
Proces ćemo kreirati slično kao i je thread:
Process(target = ime_funkcije, args = (arg1, arg2,…))
Neke od metoda za rad s procesima su:
- join([t]) – čeka da završi proces, ako je postavljen parametar t proces će se ugasiti nakon isteka vremena t. Ovo zapravo kaže glavnom programu da na tom mjestu čeka dok svi programi ne završe te tek nakon toga da ide na sljedeći korak,
- start() – pokreće proces,
- is_alive() – vraća istinu ako je proces aktivan, inače vraća false,
- Terminate() – gasi proces,
- name – vraća ime procesa,
- pid – vraća ID procesa.
Primjer 2.
Prikažimo sve procese koji se izvode na računalu te njihove ID-eve.
Rješenje:
Task manager.

Funkcija curent_process() vraća objekt tipa Process, a koji predstavlja tekući proces.
Primjer 3:
Napišimo program koji će pokretati četiri procesa. Svaki proces prilikom svog pokretanja treba ispisati poruku: svoje ime i ID. Nadalje, proces treba zaustaviti na nekoliko (random) sekundi te ponovno ispisati ID procesa i vrijeme za koje je proces bio zaustavljen.
Rješenje (program multiprocessing1.py):
from multiprocessing import *
from time import *
from random import *
def f():
p = current_process()
print(f'ID: {p.pid} ime: {p.name}')
t = randint(1, 10)
sleep(t)
print(f'Proces {p.pid} je završio nakon {t}s')
return
if __name__ == '__main__':
procesi = [Process(target = f) for i in range(4)]
for p in procesi:
p.start()
for p in procesi:
p.join()
Program se mora pokrenutu u naredbenom retku (cmd) jer proces ne može pisati u IDLE ali može pisati u komandni prompt.

Napomene uz zadatak:
Smisleno bi bilo u istoj petlji napraviti i start() i join() međutim to ne bi bilo dobro jer join() znači da program čeka s daljnjim operacijama sve dok se ne završi proces. To znači da bi nakon što se pokrene prvi proces i pozove join() program čekao da prvi proces završi te bi tek potom pokrenuo drugi proces i čekao da se on završi,….
Primjer 4:
Napišimo program koji će prebrojiti sve proste brojeve do 1000000 te će ispisati vrijeme izvođenja programa.
Rješenje:
from time import *
def prost(N):
for i in range(2, round(N ** 0.5 + 1)):
if N % i == 0:
return False
return True
def prosti(a, b):
t = 0
for i in range(a, b):
if prost(i):
t += 1
return t
if __name__ == '__main__':
a = 2
b = 1000001
poc = time()
print(prosti(a, b))
kraj = time()
print(f'{(kraj - poc):.5f}')
Program se može pokrenuti i na uobičajeni način (Run - Run Module) . Nakon malog čekanja pojavit će se ispis:
78498
12.32405
To znači da postoji 78 498 prostih brojeva manjih od 1 000 000 i vrijeme izvođenja ovog programa na nekom računalu trajalo je 12.32405 sekundi.
Primjer 5:
Izmijenimo prethodni primjer na način da dva procesa istovremeno broje proste brojeve te ispišimo vrijeme.
Napomene uz zadatak:
- Razmislite kako bi se posao mogao ubrzati - ideja: svakom procesu damo polovicu brojeva
- Uočite da u ovom trenutku ne znamo:
- koliko prostih brojeva je našao koji proces
- je li posao dobro napravljen .
- Problem je što se rezultat neće ispisivati u IDLE-u te program treba pokrenuti kroz cmd. Razlog tomu je slično kao i kod threadova – činjenica da proces ne može pristupiti IDLE-u.
- Na početku programa dodati from multiprocessing import *
Rješenje:
from multiprocessing import *
from time import *
def prost(N):
for i in range(2, round(N**0.5 + 1)):
if N % i == 0:
return False
return True
def prosti(a, b):
t = 0
for i in range(a, b):
if prost(i):
t += 1
return t
if __name__ == '__main__':
a = 2
b = 1000001
poc = time()
p1 = Process(target = prosti, args = (a, b // 2))
p1.start()
p2 = Process(target = prosti, args = (b // 2 + 1, b))
p2.start()
p1.join()
p2.join()
kraj = time()
print(f'{(kraj - poc):.5f}')
Napomene uz zadatak:
- Važno je uočiti da se ovo rješenje nije izvodilo duplo kraće od rješenja kada imamo samo jedan proces.
- Razlozi:
- prvi i drugi proces ne rade istu količinu posla (drugom proces ima daleko više posla jer radi s većim brojevima);
- postupak pokretanja procesa zahtjeva neko vrijeme,…
Vrijeme je kraće za približno 30 % (visi o broju drugih pokrenutih aplikacija).
Zašto to vrijeme (7.79503) nije upola kraće od prvog dobivenog vremena (12.32405)?
Razlozi:
- Računalo neće uvijek istom brzinom izvoditi ove programe. Stoga ćemo gotovo za svako pokretanje programa dobivati različita vremena. Radi se o tome da osim ovih, naših procesa, računalo izvodi i još neke druge procese, a ovisno je o zahtjevnosti tih drugih procesa, računalo je dodijelilo vrijeme našim procesima.
- Intervale za provjeru prostih brojeva nismo jednoliko podijelili. Naime, znatno je teže provjeriti je li primjerice broj 789 533 prost nego je li prost broj 11.
- Računalu je potrebno neko vrijeme da kreira svaki od procesa...
Isprobajte ove programe i provjerite kako rade na vašem računalu i predajte ih na linku
27. 5. - modul multithreading
12. Projektni zadatak
Dragi moji učenici,
stigao nam je i lipanj i vjerujem da svi jedva čekate kraj. Zato vas ja neću više "zatrpavati" zadatcima.
Do 16. lipnja (dan prije zadnjeg sata) predajte po jedan svoj zadatak (a bit će mi drago da pošaljete i više svojih zadataka ako ste ih već napravili) iz bilo kojeg područja koje smo ove godine radili. To može biti bilo što vezano uz objektno programiranje, mrežno programiranje, paralelno programiranje ili modul tkinter. Ako postoji potreba (npr. program se pokreće iz cmd) napišite i kratke upute za pokretanje programa ili rad s programom.
Vaše programe ću prije zadnjeg sata objaviti na ovim stranicama na Loomenu.
Zadnja aktivnost će biti isprobati programe koje su napravili ostali učenici iz ove naše programerske grupice.
Tu aktivnost naravno neću provjeravati, ali vjerujem da ćete svi isprobati objavljene programe. Rok nije zadan, ali najbolje to napravite zadnji dan, kad je zaključivanje ocjena, ako stignete.
Ako netko želi još malo proučavati paralelno programiranje, možete pogledati dokument Paralelno programiranje iz knjige iz koje sam priredila dosadašnje lekcije: Izrada primjenskih programa u Pythonu - priručnik za 4. razred prirodoslovno-matematičkih gimnazija
Autori: Predrag Brođanac, Leo Budin, Zlatka Markučič, Smiljana Perić