Hogyan készítsünk Excel táblához hasonló számolótáblát? – 3.rész

A legutóbbi cikkben a számolótábla fejlesztésének második fázisát fejeztük be azzal, hogy végigvettük a tábla specifikáció szerinti működését lehető eseményeket, és az egyes eseményekhez hozzárendeltünk meghatározott nevű eseménykezelő függvényeket. Most áttérünk a harmadik fázisra, és ezen eseménykezelők megvalósításába kezdünk bele.

Elsőnek a cellatartalom kiértékelését végző eval_cell_content_event_handler() eseménykezelő és ehhez kapcsolódó függvények implementációjával foglalkozunk. Ez az eseménykezelő az Enter lenyomásának, vagy az adott cella fókuszból kikerülésének hatására fut le. Ennek eredményeképpen – ha az adott cellában egy kifejezés szerepel – az kiértékelődik. Ha ez sikeres, vagyis érvényes kifejezést írtunk a cellába, akkor annak tartalma az eredmény lesz. Ha azonban a képlet/kifejezés valamilyen szempontból érvénytelen, illetve hibás, akkor a cellában a hiba oka fog megjelenni.

Alább az eval_cell_content_event_handler() eseménykezelőt látjuk, feltüntetve a releváns kódkörnyezetet is.

Láthatjuk azt a szótárt, amely a táblázat beviteli mezőinek kontrollváltozóit, valamint a cellába beírt képletet (kifejezést) tárolja, valamint alul az eseményekhez való hozzárendelést. Ezek nem új dolgok, mert az előző bejegyzések ezekről szóltak. Ami új, azok a segédfüggvények, amelyek a kiértékelés egy-egy részfeladatát végzik el. Itt az áttekinthetőség kedvéért csak a függvények fejléce jelenik meg a függvény feladatának leírásával. Annyit azonban most is lehet látni, hogy maga az eseménykezelő nagyon egyszerű, és lényegében az eseménnyel érintett beviteli mezőt azonosítja, majd ezzel meghívja az eval_cell_content() függvényt. Valójában ez végzi az érdemi munkát. Az összes alatta felsorolt függvény neki dolgozik be.

A cellába írt, karakterláncként rendelkezésre álló képlet kiértékelését az eval() beépített függvénnyel végezzük. Ezért az eval_cell_content() függvény nagyon egyszerű lehetne, ha nem lenne a kifejezésben cellahivatkozás pl. cell(B:3) formában. Ez azonban ebben a formában nem értelmezhető az eval() számára, így minden olyan képlet eseténben, ahol cellahivatkozást írunk, hibajelzést kapnánk. Ezért az eval_cell_content() függvény kódja döntően arra irányul, hogy a cellahivatkozásokat 1) azonosítsa, majd pedig 2) átalakítsa egy olyan függvényhívássá, amelyet az eval() már ki tud értékelni. Az eval_cell_content() függvény definíciója a következő. A kommentek részletesek, hogy követni lehessen a logikát és működést.

Ahogy említettük, az eval_cell_content() több más függvényt is használ. Ezek definícióit láthatjuk alább.

Az első ilyen segédfüggvény a get_cell_indexes(). Ennek feladata, hogy azonosítsa, hogy a megadott beviteli mezőhöz a grid elrendezésben melyik sor- és oszlopindex tartozik. Ezekből képzett tuple lesz a visszatérési értéke, amely int értékeket a beviteli mezőre meghívott grid_info() metódus eredményeképpen kapott szótárból lehet megkapni.

A következő igénybe vett függvény a find_cellref(). Ez az első argumentumként megadott karakterláncban megkeresi az első olyan részkaraktersort, amely megfelel a pattern értékeként megadottnak, ahol a ?? helyén tetszőleges karakterek állhatnak. Jelen esetben tehát az első olyan karaktersorozattal visszatér, amely kezdete „cell(„ és a vége „)”. A find_cellref() megvalósítására két változatot is mutat az ábra. A másodikat azok kedvéért, akik a reguláris kifejezésekben jártasak. Ehhez a szabványos könyvtár re nevű beépített modulját be kell importálni. Egyébként ez az eset arra is példát mutat, hogy ha van egy egyszerű feladat (jelent esetben egy egyszerű mintakeresés), és nem ismerjük még az erre specializált modult, akkor csupán ehhez nem mindig kell megtanulni a modul használatát, mert némi gondolkodás után alapelemekből felépítve is meg lehet oldani a feladatot.

Nos, ha a find_cellref() talál megfelelő részkaraktersort, és az eleje és vége között a cellahivatkozásunk szintaxisának megfelelő formában megadott oszlop és sorazonosító van pl. B:3, akkor a converted_cellref() függvény ezt úgy alakítja át, hogy az oszlop is számmal legyen megadva. Tehát a példánál maradva a converted_cellref() visszatérési értéke „cell(2,3)” lesz. Ez már függvényhívásnak néz ki, amit az eval() tudna értelmezni. De ha cell() néven definiálnánk egy függvényt az nem lenne jó, mert akkor a mintakeresés ezt újra megtalálná. Ezért a „cell” karaktersort lecseréljük annak a függvénynek a nevére, amely a hivatkozott cella értékét fogja szolgáltatni. Ez pedig a get_cell_numvalue() függvény.

A get_cell_numvalue() függvény egy oszlop- és sorindexszel azonosított beviteli mező tartalmának megfelelő számértéket adja vissza, ha az int, float vagy complex típusú számként értelmezhető. Ha nem, akkor magát a tartalmat mint karakterláncot adja vissza. Abban, hogy egy karaktersor értelmezhető-e int, float vagy complex számként, a convertible_to_int(), convertible_to_float() és convertible_to_complex() függvények segítenek.

Mindennek eredményeképpen kialakult karakterlánc kerül kiértékelésre az eval_cell_content() függvény kivételkezelő szerkezetén belül. És ahogy korábban mondtuk, ha a kiértékelés sikeres, akkor az adott beviteli mező tartalma a kifejezés értéke lesz. Ha azonban a kiértékelés valamiért hibába ütközik, akkor a hiba típusa jelenik meg cellatartalomként. Ekkor a képletet elő lehet hívni és lehet szerkeszteni, javítani. De ez már egy másik eseményhez tartozik, amit a következő bejegyzésben tárgyalunk.

E bejegyzéshez kapcsolódóan a Python tudásépítés lépésről lépésre című e-könyvben elsősorban a karakterláncok műveleteit (különösen a szeletképzést) és metódusait érdemes átnézni a „Műveletek” és „Beépített típusok nyilvános metódusai” fejezetekben, továbbá a kivételkezelést a „Kivételes bánásmód – kivételek és kezelésük” fejezetben.

É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.