Hogyan készítsünk KRESZ táblákat rajzoló programot? – 4.rész

Az előző bejegyzésekben fokozatosan fejlesztettük a közlekedési tiltó táblákat rajzoló programunkat, és ott tartunk, hogy ki tudjuk rajzolni a „Mindkét oldalról behajtani tilos” táblát, valamint ezen tábla osztályára alapozva, abból örökölve, egy utódosztályt definiáltunk, amiből elő tudunk állítani különböző sebességkorlátozó táblákat. A létrejövő táblák méretét az egérgörgő forgatásával vagy bizonyos billentyűkombinációkkal változtatni is tudjuk. Láttuk azt is, hogy a szöveges tartalmat megjelenítő sebességkorlátozó táblák méretváltoztatásának megoldása nem magától értetődő feladat, kellett némi előzetes megfontolás a kivitelezés módjára.

A mostani, és e program fejlesztésére vonatkozó utolsó bejegyzésben egy újabb osztályt definiálunk, amivel olyan közlekedési tiltó táblákat jeleníthetünk meg, és méretezhetünk át, amelyek közepén valamilyen piktogram, vagyis képi ábrázolás szerepel.

Ennek megoldását hasonló megközelítéssel fogjuk végezni, mint azt tettük előzőleg, a szöveget (számot) tartalmazó táblák esetében:

1) Definiálunk egy új, PictoProhibitorySign osztályt a ProhibitorySign osztály alosztályaként. Ezzel biztosítjuk, hogy a tábla körformája a piros szegéllyel rendelkezésre álljon, valamint, hogy a körtábla (a külső és belső körök és a befoglaló vászon) méretét változtatni tudjuk.

2) Beszerezzük a középen megjeleníteni kívánt piktogram képét. (Vagy, ha van hozzá tehetségünk, akár magunk is megrajzolhatjuk egy megfelelő képszerkesztővel)

3) Az osztály __init__ metódusában létrehozzuk a képobjektumot a tkinter PhotoImage osztályának használatával.

4) Ugyanitt, a vászon példányra (ami most a self) meghívott create_image() metódussal létrehozzuk, és középre helyezve megjelenítjük a vásznon a kép rajzelemet.

5) És végül, biztosítjuk azt, hogy a piktogram mérete a tábla aktuális méretéhez automatikusan igazodjon, ami azt jelenti, hogy egy előre meghatározott határoló négyzet oldalait nem lépheti túl. Ehhez a külön definiált _fit_image_size() metódust hívjuk meg.

6) Ugyanezt az automatikus méretkövetést biztosítjuk akkor is, ha valamilyen eseményre a tábla átméretezésének kezdeményezése történik. Ehhez felülírjuk a set_radius() metódust, amelyben hasonló lépéseket teszünk, mint a szöveges tartalom esetén tettünk ugyanezen metódusban: a) megváltoztatjuk a szülőosztály köreinek méretét. b) a képet az új középpontba helyezzük, és végül c) a képet az aktuális táblamérethez igazítjuk, meghívva a _fit_image_size() metódust.

A fentieket követhetjük nyomon a PictoProhibitorySign osztály __init__ és set_radius() metódusaiban.

A kérdés, hogy hogyan tudjuk megvalósítani a kép átméretezését.

Ennek alaplogikáját a _fit_image_size() metódus tartalmazza. Itt első lépésben lekérdezzük az előírt határoló négyzet és a kép befoglaló négyzetének aktuális koordinátáit. Ezek alapján megvizsgáljuk, hogy a kép kilóg-e az előírt határokból. Ha igen, akkor meghívunk egy segédmetódust _reduce_size_to_fit() néven, amely iteratívan addig csökkenti a kép méretét, ameddig az már teljesen a határokon belül lesz. Ha nincs túllógás, akkor sem vagyunk kész, mert lehet, hogy a kép mérete túl kicsi a tábla méretéhez képest. Ezért ilyenkor egy másik, _increase_size_up_to_boundary() nevű segédmetódust hívunk meg, amely szintén iteratívan megpróbálja a képméretet addig növelni, míg épp belefér az előírt határokba. Ezek definícióit is láthatjuk a fenti kódban. A működés megértését a kommentek segítik.

Egyetlen kérdés marad hátra, mégpedig az, hogy hogyan lehet egy képet nagyítani vagy kicsinyíteni. A tkinter modul nem rendelkezik egyetlen, erre alkalmas átméretező metódussal, hanem a nagyításhoz a képobjektumra meg kell hívni a zoom() metódust, a kicsinyítéshez pedig a subsample() metódust. Mindkettő két, x és y paramétert fogad, és egy PhotoImage példányt ad vissza ugyanazzal a képi tartalommal, mint az eredeti, amire meg lettek hívva. Míg a zoom() az X tengely irányban x, és az Y irányban y faktorral nagyít, addig a subsample() x és y mértékben kicsinyít. Ha y nincs megadva, az alapértelmezett érték megegyezik az x értékével. E metódusok korlátja, hogy az argumentumok csak egész számok lehetnek. Ezért ahhoz, hogy tört értékkel is lehessen méretet változtatni azt a „trükköt” kell megtenni, hogy a törtnek képezzük a számlálóját és nevezőjét, majd a számláló mértékvel növeljük a méretet, utána pedig a nevezővel csökkentjük. Ezt láthatjuk az ábrán az _image_resize() metódusban.

Miután a fentiek szerint minden szükséges metódus definiált, így tesztelhetjük a művünket. Ehhez a teljes program kódja az alábbi:

A teszteléshez három, azonos méretű Kresz táblát hozunk létre: egy „Mindkét oldalról behajtani tilos” táblát a ProhibitorySign osztályból, egy 90 km/hó sebességhatárt megadó táblát a SpeedLimitSign osztályból, és végül egy „Előzni tilos” táblát a most készített, új PictoProhibitorySign osztályból. A futtatás után megjelenő azonos méretű táblákat az egérgörgővel, valamint a Ctrl+ és Ctrl- billentyűkombinációkkal egyenként nagyítottuk vagy kicsinyítettük. Ezek egy-egy változatát mutatják az eredményképek:

A program tehát működik.

Mindazonáltal, a képméretezésre alkalmazott megoldásunknak több korlátja van. Az egyik, hogy a tkinter modul PhotoImage osztálya csak kevés képformátumot fogad el; gyakorlatban csak png és gif jön szóba. Ennél talán még lényegesebb, hogy a subsample() a kicsinyítést úgy végzi, hogy veszi az argumentumában megadott x, y értékeket és csak minden x-edik és y-adik képpontot használja a kicsinyített kép előállításához. Ez információvesztéssel jár, aminek következménye, hogy ha a képet sokszor oda-vissza növeljük és csökkentjük, akkor a kép torzul, sőt a képtartalom egy idő után el is tűnhet.

Ezért ilyen dinamikus képméretezésre a tkinter eszköztára nem ideális, célszerű más, képfeldolgozásra specializált csomagokat használni.

Gyakorlásképpen meg lehet próbálni e programot alkalmassá tenni arra, hogy a „Behajtani tilos” tábla (piros szín, vékony fehér szegély középen fehér téglalap) előállítására is képes legyen.

Bár e közlekedési tábla rajzoló program fejlesztése mint feladat látszólag nem néz ki túl bonyolultnak, mégis talán érzékelhető volt a négy bejegyzésben szereplő egy-egy fejlesztési fázisban, hogy még ilyen esetben is mennyi nyelvi eszközt és ismeretet kellett használni. Az egyes fázisoknál jeleztük is a főbb témákat.

Most összegezve, e feladat megoldásához az alábbi témákban érdemes elmélyedni, amelyek a Python tudásépítés lépésről lépésre című e-könyvben részletesen ki vannak fejtve az alábbi részekben: Az „Osztály vigyázz! – típuslétrehozás osztályokkal”, és „Öröklődés” fejezetek. A „Matematikai számítások támogatása” fejezet „Közönséges törtekkel való számolás” alfejezet. A „Grafikus felhasználói felület készítése” fejezet „Események és programkód összerendelése” alfejezete, valamint „A grafikus elemek fajtái, létrehozásuk és konfigurálásuk” alfejezete, és ebben a „Vászon” alcím, amin belül különösen a szöveg és kép rajzelemek létrehozás és konfigurálása.

Érdekel a Python tudásépítés lépésről lépésre az alapoktól az első asztali alkalmazásig című e-könyv.