Az előző bejegyzésben megállapítottuk, hogy a tkinter modul Canvas osztályának create_oval() metódusa ellipszist (vagy speciálisan, ha a befoglaló négyszög egy négyzet, akkor egy kört) jelenít meg. Kérdés, hogy ha nem ellipszist, hanem más ovális alakzatot akarunk kirajzolni (akár pl. egy tényleges tojásformát) akkor azt hogyan tudnánk megtenni.
Ovális forma elvben végtelen sok lehet, de ahhoz, hogy geometriailag viszonylag könnyen szerkeszthető, illetve programmal kirajzolható legyen, általában jól ismert egyszerű ívelt síkalakzatokat használnak egy adott ovális létrehozásához. Mi is ezt fogjuk most tenni, és négy körív segítségével állítunk elő egy mindkét tengelyre szimmetrikus ovális formát. Ennek felépítését mutatja az ábra:

Itt két egymással érintkező kör ívei adják az ovális bal és jobb oldali ívszakaszait. A maradék felső, illetve az alsó ívszakaszokat pedig olyan körívek, amely körök középpontja az érintkező körök alsó, illetve felső közös érintőinek közepe. Ezen alakzatok (kör és körív) már kirajzoltathatók a Canvas megfelelő metódusaival.
A teljes ovális alakzatot a középpontjával, és szélességével fogjuk jellemezni. Ezeket adottnak véve, az ábrán láthatjuk azon összefüggéseket, amelyek szükségesek lesznek az ovális programbeli modellezéséhez és kirajzolásához.
Amennyiben arra is van igényünk, hogy az oválisnak ne csak a körvonala jelenjen, meg, hanem ki is lehessen tölteni valamilyen színnel – hasonlóan, mint a Canvas create_oval() metódussal létrehozott ellipszist -, akkor előzetes megfontolást igényel a segédalakzatok (jelen esetben a körök, körcikkek) kiválasztása és egymáshoz képesti elhelyezése. Ha alaposabban megnézzük az ábra két körét és két körcikkét, láthatjuk, hogy ha ezeket azonos színnel töltjük ki, akkor a teljes egyéni ovális formánk is ki lesz töltve az adott színnel.
A kitölthetőség hatással lesz a program megjelenítési feladataira is. Egyrészt a körvonal (kontúr) kirajzolásához négy körív kell, amelyeket a Canvas create_arc() metódusával lehet kirajzolni, ahol a style opció ’arc’ értékre van állítva, azaz csak egy ívet rajzol. A kitölthetőséghez viszont két kört és két körcikket kell rajzolni. Ez utóbbit szintén a Canvas create_arc() metódusával lehet kirajzolni, de most a style paraméternek a ’pieslice’ értéket kell adni.
Az egyéni ovális formát egy Oval nevű osztállyal valósítjuk meg, amelyben az előbb említett kontúr kirajzolását a draw() metódus, a kitöltést lehetővé tevő összetevők létrehozását a fill_area() nevű metódus fogja végezni, amelyet a draw() hív meg. Az osztály teljes definícióját alább láthatjuk, ahol még egy geometriai pontot modellező Point osztályt, mint segédosztályt is lehet látni. A részletes kommentek segítik a megértést.
|
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 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
from __future__ import annotations import tkinter as tk from itertools import count from math import sqrt from collections import namedtuple class Point(namedtuple('Point', 'x y')): """Egy x,y koordinátákkal megadott pontot reprezentáló osztály.""" def shift(self, dx, dy) -> Point: """Olyan új példánnyal tér vissza, amelynek koordinátái a self koordinátáinak és az argumentumként megadott vízszintes és függőleges eltolás mértékének az összege. """ return type(self)(self.x + dx, self.y + dy) class Oval: cnt = count(1) def __init__(self, canvas, center_point: tuple, shape_width, name='', **config_options): self.canvas: tk.Canvas = canvas # Az a Canvas példány, amelyen az ovális meg fog jelenni. self.inst_id: str = str(next(type(self).cnt)) # Az osztály példányának egyedi azonosítója. self.center_point = c = Point(*center_point) # Az alakzat középpontja. r = shape_width / 4 # A bal és jobb köríveket meghatározó, egymást érintő körök sugara. self.c1, self.c2 = c.shift(-r, 0), c.shift(+r, 0) # Az egymást érintő körök középpontjai. self.cb, self.ct = c.shift(0, -r), c.shift(0, +r) # Az alsó és felső ívek köreinek középpontjai. self.r = r self.name = name # Az alakzat hivatkozási neve. self.options = config_options def draw(self): r = self.r # Felső körív. rr = (sqrt(2) + 1) * r self.canvas.create_arc(self.ct.shift(-rr, -rr), self.ct.shift(+rr, +rr), start=45, extent=90, style='arc', width=2, tags=('kontúr' + self.inst_id)) # Alsó körív. self.canvas.create_arc(self.cb.shift(-rr, -rr), self.cb.shift(+rr, +rr), start=-45, extent=-90, style='arc', width=2, tags=('kontúr' + self.inst_id)) # Baloldali kör íve. self.canvas.create_arc(self.c1.shift(-r, -r), self.c1.shift(+r, +r), start=135, extent=90, style='arc', width=2, tags=('kontúr' + self.inst_id)) # Jobb oldali kör íve self.canvas.create_arc(self.c2.shift(-r, -r), self.c2.shift(+ r, +r), start=-45, extent=+90, style='arc', width=2, tags=('kontúr' + self.inst_id)) # Minden címkézett rajzelemhez a konstruktorban megadott nevet adjuk még címkeként hozzá. # Ezzel a címkével lehet majd később a Canvas metódusokban hivatkozni erre az egész alakzatra. self.canvas.addtag_withtag(self.name, 'kontúr' + self.inst_id) # Létrehozzuk a színkitöltést lehetővé tevő összetevőket, és érvényre juttatjuk a példányosításkor megadott # konfigurációs beállításokat. fill_color = color if (color := self.options.get('fill')) else '' self.fill_area(fill_color) self.canvas.itemconfig('kontúr' + self.inst_id, **self.options) def fill_area(self, color): r = self.r # Felső körív. rr = (sqrt(2) + 1) * r self.canvas.create_arc(self.ct.shift(-rr, -rr), self.ct.shift(+rr, +rr), start=45, extent=90, style='arc', tags=('arc' + self.inst_id, 'filler' + self.inst_id)) # Alsó körív. self.canvas.create_arc(self.cb.shift(-rr, -rr), self.cb.shift(+rr, +rr), start=-45, extent=-90, style='arc', tags=('arc' + self.inst_id, 'filler' + self.inst_id)) # Baloldali kör. self.canvas.create_oval(self.c1.shift(-r, -r), self.c1.shift(+r, +r), tags=('circle' + self.inst_id, 'filler' + self.inst_id)) # Jobb oldali kör self.canvas.create_oval(self.c2.shift(-r, -r), self.c2.shift(+ r, +r), tags=('circle' + self.inst_id, 'filler' + self.inst_id)) # A két körívet és a két kört az argumentumban megadott színnel töltjük ki. self.canvas.itemconfig('arc' + self.inst_id, style='pieslice', width=0, outline='', fill=color) self.canvas.itemconfig('circle' + self.inst_id, width=0, outline='', fill=color) # A kitöltő elemeket a kontúrt rajzoló elemek alá helyezzük a megjelenítési listában, hogy az alakzat körvonala teljesen látszódjon. self.canvas.tag_lower('filler' + self.inst_id, 'kontúr' + self.inst_id) # Felcímkézzük a kitöltést lehetővé tevő elemeket is a példányosításkor megadott névvel, hogy a Canvas műveletekben # ezek is együttesen legyenek kezelve a kontúr rajzelemekkel. self.canvas.addtag_withtag(self.name, 'filler' + self.inst_id) def itemconfig(self, **options): self.options.update(options) # A fill konfigurációs paraméter beállítása a kitöltést megvalósító rajzelemeket érinti, ezért ezeket konfiguráljuk. if (fill_color := self.options.get('fill')) is not None: self.canvas.itemconfig('filler' + self.inst_id, fill=fill_color) # Az alakzat egyéb megadott konfigurációjának (width, outline, dash) érvényre juttatása a körvonalat meghatározó rajzelemeken. self.canvas.itemconfig('kontúr' + self.inst_id, **options) |
Az egyéni ovális alakzat mint az Oval példánya a példányosításkor megadott névvel hivatkozva számos olyan Canvas metódussal használható, amelyeknél az összetevő rajzelemek együttesen vannak kezelve, illetve hatásukban és eredményükben nem érzékelhető, hogy az alakzat összetevőkből áll. Ilyen műveletek például az átméretezés és az áthelyezés.
Ugyanakkor vannak olyan Canvas metódusok, amelyekkel nem, vagy nem megbízhatóan működik az Oval példány, mert az előbb említett feltételek nem teljesülnek. Ide tartozik például a konfigurációt módosító itemconfig() metódus is. Ugyanis ez olyan összetevő rajzelemek jellemzőit is változtatná, amelyekét nem szeretnénk (pl. az outline opció nem kívánatos módon a kitöltést lehetővé tevő rajzelemek körvonalát is módosítja). Ezért látunk az Oval osztályban egy saját itemconfig() metódust, mert konfiguráláshoz ezt kell használni. Konfigurálni a normál állapothoz tartozó fill, width, dash és outline jellemzőket lehet. /A dashoffset, stipple és outlinestipple opciók Windows alatt nem működnek./
Az említett lehetőségek bemutatására a tesztsorok a következők:
|
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 |
# TESZT root = tk.Tk() root.title('Oválisok megjelenítése') # A vászon grafikus elem létrehozása, amelyen az ovális alakzatokat megjelenítjük. canvas = tk.Canvas(root, width=600, height=500, bg='gray97', highlightthickness=0) canvas.pack() # Kirajzoltatunk egy oválist kék körvonallal és piros kitöltőszínnel. oval1 = Oval(canvas, (150, 150), 200, name='ovális1', outline='blue', width=5, fill='red') oval1.draw() # Ugyanazon a vászon elemen ugyanabban a pozícióban kirajzoltatunk egy másik oválist zöld körvonallal és színkitöltés nélkül. oval2 = Oval(canvas, (150, 150), 200, name='ovális2', outline='green', width=5) oval2.draw() # Az egyéni ovális alakzat számos olyan Canvas metódussal használható, amelyeknél az összetevő rajzelemek # együttesen vannak kezelve, illetve hatásukban és eredményükben nem érzékelhető, hogy az alakzat összetevőkből áll. # Erre mutatnak példát a következő kódsorok. # Kirajzoltatjuk az ovális1 nevű alakzat befoglaló négyszögét. canvas.create_rectangle(bbx := canvas.bbox('ovális1'), dash='.') # Az ovális2 nevű alakzatot vízszintesen megnyújtjuk. canvas.scale('ovális2', *oval2.center_point, 1.5, 1) # Utána pedig áthelyezzük. canvas.move('ovális2', 200, 200) # Az ovális2 alakzat konfigurációját megváltoztatjuk. oval2.itemconfig(dash='-', fill='yellow', outline='black', width=3) # Egérklikkeléssel az ovális1 alakzatot az előtérbe hozás után elmozgatjuk. canvas.tag_bind('ovális1', '<Button 1>', lambda e: e.widget.tag_raise('ovális1') or e.widget.move('ovális1', 30, 30)) root.mainloop() |
A futtatás után megjelenő felületen a piros színű oválisra való kattintás utáni állapotot az alábbi ábra képernyőképe mutatja.

A grafikus felhasználói felület létrehozásával, benne a Canvas elem példákkal illusztrált részletes leírásával 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” fejezete foglalkozik.