Természetesen nem az Excel táblázatkezelő programot akarjuk megírni, mert egy több évtizedes fejlesztés eredményét nehéz lenne egy-két bejegyzésben megismételni. Célunk – mint minden ezen oldalon megjelenő cikkben – pusztán csak az, hogy példafeladatokon keresztül gyakoroljuk a Python használatát, és egy feladat kapcsán a megoldási lehetőségeken való gondolkodást.
Most tehát az lesz a feladat, hogy állítsunk elő egy Excel munkalaphoz kicsit hasonlító alkalmazást, amely a következőket tudja:
- Az egyes cellákba értékeket vagy képleteket (kifejezéseket) lehet írni, aminek az eredménye megjelenik a cellában.
- A képletek mindig az egyenlőségjellel ( = ) kezdődnek, és cellahivatkozásokat is tartalmazhatnak.
- A táblázat sorait 1-től kezdődő sorszámok azonosítják, az oszlopokat pedig az angol abc nagybetűi A-Z-ig.
- A képletekben a táblázat egyes celláira sor- és oszlopazonosítókkal lehet hivatkozni „cell(A:1)” karaktersor formában, ahol A:1 az első oszlopot és első sor azonosítja.
- A cellába írt értékek megváltozása esetén az arra a cellára hivatkozó képletek értéke is megfelelően módosul.
- A képleteket a cellában történő dupla bal egérgomb kattintással meg lehet jeleníteni, hogy szerkeszthetőek legyenek a cellában. A képlet megjelenésekor a kurzor a karaktersor végére kerül.
- A képletek akkor értékelődnek ki, ha Entert nyomunk, vagy a celláról a fókusz elkerül. Ez utóbbi akkor történik, ha vagy a Tab billentyűt nyomjuk meg, vagy az egérrel egy másik cellába kattintunk.
- Ha Enter nyomunk, akkor a kurzor az alatta levő cellába kell, hogy menjen. Ha nincs ilyen cella, akkor a jobbra mellette levőbe. Ha ilyen sincs – ami azt jelenti, hogy a bal alsó sarokcellában vagyunk –, akkor a kurzor marad a cellában.
- A cella tartalmát törölni a Del (delete) gomb használatával lehet. A cellába új értéket beírni csak az előző törlése után lehet.
- A jobb egérgombbal duplán kattintva egy cellán, az adott oszlop szélessége úgy változik, hogy az oszlop cellái közül a leghosszabb szöveg is látszódjon.
A feladat megoldását három fő fázisra bontjuk. Az első a táblázat kirajzolása, a második az események meghatározása és eseménykezelők hozzárendelése. A harmadik fázis pedig az eseménykezelők átgondolása és megvalósítása lesz.
Jelen cikkben az első fázissal foglalkozunk. A táblázat megjelenítéséhez, kirajzolásához a tkinter modul eszköztárát használjuk.
A számolótáblánk természetszerűleg rácsos elrendezésű, ahol az egyes cellákba adatokat (szöveget) lehet írni. Ezért kézenfekvő, hogy a tkinter beviteli mezőjét (entry widget) használjuk a cellák írásához és a cellatartalom megjelenítéséhez. Ezeket egy megadott sor- és oszlopszám által meghatározott mennyiségben hozzuk létre, és helyezzük le rácsos elrendezésben a grid() metódussal.
Ahhoz, hogy egy beviteli mező értékét írni-olvasni tudjuk hozzá kell rendelni egy kontrollváltozót. Ez azonban nem elég, mert a kontrollváltozó csak a beviteli mező megjelenő értékét tárolja. Nekünk ezen felül az is kell, hogy az adott cellához milyen képlet tartozik, ha meg lett adva ilyen. Ezért a táblázat egy adott pozíciójában levő beviteli mezőhöz tartozó kontrollváltozót és képletet (azt leíró karakterláncot) egy szótárban tároljuk, ahol a kulcs egy kételemű tuple a sor- és oszlopindexekkel, a hozzá rendelt érték pedig szintén egy kételemű tuple, amely első eleme a kontrollváltozó, a második a képletet leíró karaktersor.
Ezen elvet követve nem kell mást tenni, mint a sor- és oszlopindexeken végighaladva feltöltjük az említett szótárt kezdeti értékekkel: üres StringVar kontrollváltozó és üres karakterlánc, mert kezdetben nincs sehol képlet. Ezt követően lehet a létrehozni az Entry osztályból a beviteli mező példányokat, amelyeknek a textvariable konfigurációs paraméteréhez a szótár megfelelő pozíciójához tartozó kontrollváltozóját rendeljük. A mezők betűtípusát és szélességét szintén a létrehozáskor állítjuk be.
Mivel az első sor és első oszlop cellái az oszlop- és sorazonosítókat tartalmazzák, így ezen beviteli mezőknek nem lesz aktív szerepük. Ezért e beviteli mezőket letiltjuk (DISABLED állapotba hozzuk), hogy tartalmuk ne legyen változtatható. Ezt, és más közös konfigurációs paramétereiket (háttérszín, szövegigazítás, betűtípus) egy szótárban határozzuk meg. Ezt követően az első sor egyes beviteli mezőiben az angol abc nagybetűi lesznek mint oszlopazonosítók. Az első oszlop beviteli mezőiben pedig a sorszámok jelennek meg 1-től kezdődő egészekként. Ahhoz, hogy a sorszámok mindig jól láthatók legyenek, az első oszlop szélességét a legnagyobb sorszám szélességét figyelembe véve automatikusan állítjuk be.
Végül, a beviteli mezőket lehelyezzük a rács sor- és oszlopindexekkel meghatározott pozíciójába.
A fenti megfontolásoknak megfelelő táblázatmegjelenítő kódsor az alábbi. A megértést a részletes kommentek segítik.
|
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 |
import tkinter as tk from itertools import product root = tk.Tk() root.title('Számolótábla') # A táblázat sor- és oszlopszámának megadása. num_of_rows, num_of_columns = 20, 6 # A táblázat egy adott pozíciójában levő beviteli mezőhöz (entry box) tartozó kontrollváltozót és # képletet (kifejezést) egy szótárban tároljuk, ahol a kulcs egy kételemű tuple a sor- és oszlopindexekkel, a hozzá # rendelt érték pedig szintén egy kételemű tuple, amely első eleme a kontrollváltozó, a második a képletet leíró karaktersor. ebx_vars: dict[tuple[int, int], list] = {} # Táblázatrács kirajzolása. for ri, ci in product(range(num_of_rows + 1), range(num_of_columns + 1)): # A sor- és oszlopindexeken végighaladva felöltjük a szótárt kezdeti értékekkel (üres StringVar és üres string) ebx_vars[(ri, ci)] = [tk.StringVar(), ''] # Létrehozzuk az egyes cellákhoz tartozó grafikus beviteli mezőket, és kontrollváltozóként a szótárban előbb # létrehozott StringVar objektumot rendeljük. Beállítjuk a mezők betűtípusát és szélességét. ebx = tk.Entry(root, textvariable=ebx_vars.get((ri, ci))[0], font=('Noto Mono', 12), width=20) # A táblázat első sora és oszlopa a sor- és oszlopazonosítokat tartalmazzák, ezért e beviteli mezőket # letiltjuk, hogy tartalmuk ne legyen változtatható. A közös konfig paramétereiket egy szótárban határozzuk meg. headers_common_params = dict(state=tk.DISABLED, disabledbackground='gray95', justify=tk.CENTER, font=('Arial', 12, 'bold')) # Az első sor egyes beviteli mezőiben az angol abc nagybetűi lesznek mint oszlopazonosítók. if ri == 0 and ci > 0: ebx_vars[(ri, ci)][0].set(chr(ord('A') + ci - 1)) ebx.config(**headers_common_params) # Az első oszlop egyes beviteli mezőiben a sorszámok jelennek meg 1-től kezdődő egészekként. # Az első oszlop szélességét a legnagyobb sorszám szélességét figyelembe véve állítjuk be. if ci == 0: ebx.config(width=len(str(num_of_rows)) + 2, **headers_common_params) if ri > 0: ebx_vars[(ri, ci)][0].set(ri) # A beviteli mezőket lehelyezzük a rács sor- és oszlopindexekkel maghatározott pozíciójába. ebx.grid(row=ri, column=ci, sticky='we') root.mainloop() |
A megjelenő táblázat így néz ki:

Itt már vittünk be adatokat a táblázat egyes celláiba, és egy helyen képletet is írtunk, ami egyben egy példát is mutat a cellahivatkozásra.
A következő bejegyzésben a fenti specifikációnak megfelelő eseményeket fogjuk definiálni.
E bejegyzéshez a Python tudásépítés lépésről lépésre című e-könyvben különösen a következő részeket érdemes átnézni: „Grafikus felhasználói felület készítése” fejezet „Kontrollváltozók” és „A grafikus elemek fajtái, létrehozásuk és konfigurálásuk” alfejezete, és ebben a „Beviteli mező” alcím.