Ha grafikus felhasználói felületet készítünk, akkor felmerülhet az a kérdés, illetve feladat, hogy egy grafikus elem (widget) helyét valamihez képest meghatározzuk. A hely azonosítása általában x, y koordinátákkal történik. Ha viszont a grafikus elem táblázatos elrendezésben lett lehelyezve (a tkinter grid() metódussal), akkor a pontkoordintákkal való jellemzés helyett vagy mellett igény lehet arra, hogy meghatározzuk, hogy táblázat/rács milyen sor- és oszlopindexszel jellemzett cellájában helyezkedik el az adott grafikus elem.
A pontkoordinátákkal való helyazonosítást, attól függően, hogy mi a referencia pont, több módon is megtehetjük. Ezt foglalja össze a következő ábra, ahol a vektorok ábrázolása mellett azt is megadtuk, hogy milyen metódussal lehet a koordinátákat megkapni, ha egy egéresemény (pl. a bal gomb lenyomása) hatására egy eseménykezelőben az eseményobjektum rendelkezésre áll. Itt két esetet is feltüntettünk. A kettő abban tér el, hogy míg a bal oldali elrendezésben csak keretek (Frame) vannak és az azokon belüli pontokat akarjuk meghatározni, a jobb oldalon láthatónál a kereten belüli grafikus elem, jelen esetben címke (Label), pontjára vagyunk kíváncsiak.

Az ábra a grafikus objektumokat táblázatos elrendezésben mutatja. Kérdés az, hogy a pontkoordináták alapján hogyan kaphatjuk meg a befoglaló cella sor- és oszlopindexét. Ehhez a grid_location() metódus szolgál, amit arra az elemre kell meghívni, amelyben a rácsos elrendezés van és ehhez képest kell az adott, az egérkurzor által mutatott, pontot azonosítani. Ez a bal oldali esetben egyszerűbb, mert a pontot tartalmazó keret szülőjében, ami a főablak (gyökér elem), van a táblázatos elrendezés. Ennek alapján a grid_location() metódusnak azon vektor koordinátáit kell megadni, amely két vektor összege: az első a gyökér elemtől az adott keret elem bal felső sarkáig mutat, a másik a keret bal felső sarkától a pontig. Az ábra jelöléseivel ez a fekete és piros vektorok összege.
Kicsit összetettebb a helyzet a jobb oldali esetben, ha a pont a címkén van (az egérkattintás esemény a címkén történik). Ugyanis a címke szülője a keret és nem a gyökér, ami a táblázatot menedzseli. Ezért, ha az egérkattintás esemény mind a keretekhez, mind a címkékhez hozzá van rendelve, akkor az eseménykezelőben vizsgálni kell, hogy melyiken történt az esemény. Ha a címkén, akkor annak szülőjével kell a további műveleteket végezni.
Mindezt az alább látható példakódban láthatjuk leprogramozva. 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 |
import tkinter as tk from itertools import product root = tk.Tk() # Segédfüggvény. def px_to_mm(pixels): """A pixelben megadott képernyőtávolságot milliméterben adja vissza.""" return round(pixels / root.winfo_fpixels('1m')) def on_click(e): """Az e egéresemény alapján kiírja az eseménnyel érintett grafikus elem nevét és szülőjének nevét, valamint azt, hogy az eseménnyel érintett grafikus elem a táblázatos elerendezés mely sor- és oszlopindexszel jellemzett cellájában van. Továbbá kiírja egérkurzor által mutatott pont különböző viszonytási pontoktól mért koordinátáit. """ w = e.widget # Az eseménnyel érintett widget. parent = e.widget.master # Az eseménnyel érintett widget szülője. print('\nAz eseménnyel érintett grafikus elem: {} és szülője: {}'.format(w.winfo_name(), parent.winfo_name())) print('Az eseménnyel érintett grafikus elem a sor={1} és oszlop={0} indexű cellában van.\n'.format(*get_grid_coords(e))) print('Az egérkurzor által mutatott pont koordinátái') s1 = ' az eseménnyel érintett widget bal felső sarkától számítva' print('{:60}: x={:>4} px, y={:>4} px; x={:>3} mm, y={:>3} mm'.format(s1, e.x, e.y, px_to_mm(e.x), px_to_mm(e.y))) s2 = ' a képernyő bal felső sarkától számítva' xp, yp = w.winfo_pointerx(), w.winfo_pointery() print('{:60}: x={:>4} px, y={:>4} px; x={:>3} mm, y={:>3} mm'.format(s2, xp, yp, px_to_mm(xp), px_to_mm(yp))) print('Az eseménnyel érintett widget bal felső sarokpontjának koordinátái') s3 = ' a szülője bal felső sarkától számítva' _x, _y = w.winfo_x(), w.winfo_y() print('{:60}: x={:>4} px, y={:>4} px; x={:>3} mm, y={:>3} mm'.format(s3, _x, _y, px_to_mm(_x), px_to_mm(_y))) s4 = ' a képernyő bal felső sarkától számítva' x0, y0 = w.winfo_rootx(), w.winfo_rooty() print('{:60}: x={:>4} px, y={:>4} px; x={:>3} mm, y={:>3} mm'.format(s4, x0, y0, px_to_mm(x0), px_to_mm(y0))) def get_grid_coords(e): """Az e egéreseménnyel érintett grafikus elem és az egérmutató által kijelölt pont koordinátái alapján meghatározza, hogy a grafikus elem a táblázat milyen sor- és oszlopindexszel jellemzett cellájában helyezkedik el, és e koordinátákat adja vissza oszlop- és sorindex sorrendben. """ # Ha az eseménnyel érintett elem nem Frame, akkor annak a szülőjét vesszük. w = e.widget if type(e.widget) is tk.Frame else e.widget.master # A keret bal felső sarokpontjának koordinátái a szülője bal felső sarkától számítva. _x, _y = w.winfo_x(), w.winfo_y() # Ehhez, hozzá kell adni az egérmutató által kijelölt pont kereten belüli koordinátáit, hogy a keret szülője, # jelen esetben a root, pontosan be tudja azonosítani, hogy mely rács cellába mutat az egér. # Az eredményül kapott pont koordinátákat a keret, mint cellatartalom szülőjére meghívott grid_location() metódusnak adjuk, ami # a cella oszlop- és sorindexét adja vissza. return w.master.grid_location(_x + e.x, _y + e.y) # A főablakba lehelyezünk 6 négyzet alakú keretet táblázatos elrendezésben 3 oszlopban és 2 sorban. for ri, ci in product(range(2), range(3)): frm = tk.Frame(root, bg='light yellow', width=75, height=75, bd=0, relief=tk.SOLID, name=f'frame{ri, ci}') frm.grid(row=ri, column=ci, padx=10, pady=10) # A keretek között hagyunk térközt, hogy jól láthatóan elkülönüljenek. frm.bind('<Button 1>', on_click) # Bal egérgomb lenyomásre érzékennyé tesszük a kereteket. frm.propagate(False) # A keretek méretét fixen tartjuk (ne a kerettartalomhoz illeszkedjen) # Minden keretbe egy címke grafikus elemet helyezünk, amelyeken kiírjuk az aktuális sor- és oszlopindexeket. lbl = tk.Label(frm, text=str((ri, ci)), bg='cyan', font=('Arial', 14, 'bold'), height=2, name=f'label{ri, ci}') lbl.pack(expand=True) lbl.bind('<Button 1>', on_click) # Bal egérgomb lenyomásre érzékennyé tesszük a címkéket. root.mainloop() |
E programot futtatva a következő ábra mutat két példát arra, amikor a bal egérgomb egy címke, illetve amikor egy keret felületén került lenyomásra.

Megjegyzés: a táblázatcella azonosítását a grid_info() metódussal is meg lehet tenni, de most pontkoordináták alapján akartunk dolgozni, ezért használtuk a grid_location() metódust.
A grafikus felhasználói felület létrehozásával, és többek között a táblázatos elrendezéssel is, 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.