Ebben a bejegyzésben olyan program kidolgozását kezdjük el, amellyel közlekedési jelzőtáblákat – konkrétabban tiltó táblákat – jeleníthetünk meg grafikus felhasználói felületen. Azért választottuk e KRESZ táblákat, mert elég változatosak, viszont van közös vonásuk, hogy mindegyik kör alakú egy piros szegéllyel. Az egyes táblák a tábla közepén levő tartalomban térnek el, amely lehet szám, szöveg (pl. sebességkorlátozó táblák) vagy piktogram (pl. előzni tilos).
Így e táblák nem csak arra adnak lehetőséget, hogy megnézzük miként kell elindulni egy összetettebb rajzelem elkészítésénél, hanem egyéb implementációs kérdések felvetésére és megoldások bemutatására is alkalmas. Például arra, hogy a kör alakú piros szegéllyel rendelkező, belül üres „Mindkét oldalról behajtani tilos” tábla programbeli modellje nem csak önállóan használható, hanem a többi táblát is származtatni lehet belőle, mert csak a belső részt kell tartalommal kitölteni. Más szóval, ha ezt a táblát egy osztályban valósítjuk meg, akkor a többi tiltó tábla ennek specializált utódosztályaiként állítható elő például attól függően, hogy szöveg (szám) vagy piktogram szerepel a tábla közepén.
Azt is megnézzük majd, hogy hogyan rendeljünk eseményeket e táblákhoz, amelyeket például arra használhatunk, hogy növeljük vagy csökkentsük a táblák méretét. Ez utóbbi feladat, ahogy majd látni fogjuk, nem is annyira magától értetődő, szükség lesz a megvalósításhoz némi gondolkodásra.
Most ebben a bejegyzésben egyelőre csak az alapot jelentő „Mindkét oldalról behajtani tilos” táblát valósítjuk meg.
Ha Pythonban a tkinter modult használjuk a GUI felület fejlesztéséhez, akkor a rajzolást a vászon grafikus vezérlőelemen (Canvas widget) tudjuk megtenni. Mivel egy összetett rajzot kívánunk készíteni, és ezt több példányban használni, ezért egy osztályt definiálunk, amely a Canvas osztályt örökli. Az osztály neve legyen ProhibitorySign. Az osztálydefiníciót alább láthatjuk.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
import tkinter as tk from collections import namedtuple # A koordinátákkal való munka megkönnyítésére egy Point típust hozunk létre. Point = namedtuple('Point', 'x y') class ProhibitorySign(tk.Canvas): def __init__(self, master, size: int = None): super().__init__(master, bg='gray90') self.name = 'Mindkét irányból behajtani tilos' # Ha nem adjuk meg létrehozáskor a vászonpéldány oldalméretét, akkor a szülőelem méretéhez igazítjuk azt. if size is None: # A helyes méretek lekérdezéséhez előbb frissíteni kell az ablakot. master.update() m_w, m_h = master.winfo_width(), master.winfo_height() size = min(int(m_h), int(m_w)) self.config(height=size, width=size) # A körök közös középpontja, amelynek koordinátái a vászon aktuális oldalméretének felével egyezik meg. cc = Point(size / 2, size / 2) # Külső kör sugara, amely a vászon oldalméretének fele. r1 = size / 2 # A számítási pontosság korlátai miatt a kör széle nem mindig látszana, ezért egy egész kicsit csökkentjük a sugarat. r1 -= 2 # Beállítjuk a belső és a külső kör sugarának arányát, ami meghatározza, hogy milyen széles legyen a tiltó tábla piros szegélye. r2 = r1 * 0.8 # A későbbi használathoz a középpontot és sugarakat attribútumként tároljuk. self._ck, self._r1, self._r2 = cc, r1, r2 # A külső kör befoglaló négyzetének pontjai a középponttal és sugárral meghatározva. k1p1, k1p2 = Point(cc.x - r1, cc.y - r1), Point(cc.x + r1, cc.y + r1) # A külső kör létrehozása a befoglaló négyzet pontjait használva. # A külső kört piros színnel töltjük ki. A kisebb belső kör pedig fehér lesz. Így alakul ki majd a piros szegély. self._kör1 = self.create_oval(*k1p1, *k1p2, fill='red') # A belső kör befoglaló négyzetének pontjai a középponttal és sugárral meghatározva. k2p1, k2p2 = Point(cc.x - r2, cc.y - r2), Point(cc.x + r2, cc.y + r2) # A belső kör létrehozása a befoglaló négyzet pontjait használva. self._kör2 = self.create_oval(*k2p1, *k2p2, fill='white') self.bind_events_and_event_handlers() def bind_events_and_event_handlers(self):... # TESZT root = tk.Tk() root.geometry('500x500') ProhibitorySign(root, 400).pack() root.mainloop() |
Mivel a tiltó táblák kör alakúak, ezért a vászon négyzet lehet. Ennek megfelelően a ProhibitorySign osztály __init__ metódusában egyetlen méretadatot kérünk csak be. A másik paraméter (master) a szülőelemet /pl. gyökérablak (root)/ fogadja.
Bár nem lenne feltétlenük szükséges, de most biztosítjuk azt a rugalmasságot, hogy ha nem adunk meg oldalméret adatot, akkor a master méretéhez igazodik majd a vászonpéldány, amit most az osztályban a self reprezentál.
A következő lépés két eltérő sugarú koncentrikus kör rajzolása a vásznon. A külső kört piros színnel töltjük majd ki. A kisebb belső kör pedig fehér lesz. Így alakul ki majd a piros szegély.
A körök létrehozásához először meghatározzuk a körök közös középpontját, amely x, y koordinátái a négyzetalakú vászon aktuális oldalméretének felével egyeznek meg.
A következő körjellemző a sugár. A külső, azaz a nagyobb sugarú kör átmérője megegyezik a vászon oldalméretével, ezért a külső kör sugara ennek fele. A belső kör sugara kisebb kell, hogy legyen, hogy a piros szegély látszódjon. Elvileg szabadon megválaszthatjuk a külső kör sugarához viszonyítva, de azon tartományon belül kell maradni, ami olyan széles szegélyt eredményez vizuálisan, amit a KRESZ tiltó tábláin látunk. Megfelelőnek tűnik, ha a belső kör sugarát a külső 80%-ára állítjuk be.
Most már következhet a körök kirajzolása. Ezt a vászonpéldányra meghívott create_oval() metódussal tehetjük meg, amelynek első argumentumpárja a kör befoglaló négyzetének balfelső pontjának, a második argumentumpár pedig a jobb alsó pontjának x és y koordinátái kell, hogy legyenek. Ezeket kell előbb előállítani a középpont és sugár ismeretében. Ez elég egyszerű, hiszen a balfelső pont koordinátáit úgy kapjuk meg, hogy a középpont koordinátáiból levonjuk a sugár értékét, a jobb alsó ponthoz pedig a középpont koordinátáihoz hozzáadjuk a sugár értékét. A szegély piros színének kialakításához a külső kör létrehozásakor a fill paraméternek a „red” értéket adjuk, a belső kör esetében ugyanezen paraméter a fehér színt jelölő „white” karakterláncot kapja.
Ezzel lényegében kész az osztálydefiníció. A megadott tesztkódsorokkal ki is próbáljuk az alkotásunkat.
Először létrehozzuk a gyökérablakot (root), és azt egy megadott méretre állítjuk be. Ezt követően létrehozzuk a ProhibitorySign egy példányát a root és oldalméret megadásával. Utána – mivel e példány egyben egy vászon grafikus elem is – lehelyezzük a gyökérablakban a pack() metódussal. Majd pedig belépünk az eseményfigyelő állapotba.
Az eredményt a képen láthatjuk:

Az osztálydefiníció __init__ metódusának végén szerepel még egy metódushívás, ami az alatta definiált metódust hívja meg. A neve alapján nem nehéz kitalálni a célját. Ebben fogjuk definiálni azokat az egér és billentyűzet eseményeket, amelyekre szeretnénk, hogy a táblánk valamilyen módon reagálna.
Ezzel folytatjuk a következő bejegyzésben.
E bejegyzés tartalmához a Python tudásépítés lépésről lépésre című e-könyv „Grafikus felhasználói felület készítése” fejezetének alábbi alfejezeteiben találunk bővebb kifejtést: „Grafikus elemek konfigurálásának módjai”, „A grafikus elemek fajtái, létrehozásuk és konfigurálásuk” alfejezet „Vászon” alcím, továbbá az „Osztály vigyázz! – típuslétrehozás osztályokkal”, és „Öröklődés” fejezetek.