JFreeChart grafikon készítése

grafikon

grafikonXML formátumban megkapott adatokat grafikonon jelenítünk meg. 5 összetartozó adat/tulajdonság sorozatát dolgozzuk fel: JOB_TITLE, EMPLOYEE_COUNT, MIN_SALARY, AVG_SALARY, MAX_SALARY. Az adatforrásban egyszerű életpálya modell szerint munkakörönként meghatározott az adható minimális és maximális fizetés (ez a 3 adat közvetlenül rendelkezésre áll). Minden alkalmazottra teljesül, hogy a fizetése beletartozik ebbe a zárt intervallumba. Az adatforrás feldolgozása során COUNT és AVG aggregáló függvényekkel előállítjuk – munkakörönként csoportosítva – az alkalmazottak létszámát és átlagfizetését (ez a további 2 adat). Az Oracle HR sémából lekérdezve 19 munkakört kapunk, így az XML fába is ennyi <JOB_STAT> csomópont kerül. A megfelelő pillanatban rendelkezésre álló 5 összetartozó adat exportálható XML formátumba az alábbiak szerint:

Az elkészült grafikon így jelenik meg:

JFreeChart-grafikon

A JFreeChart típusú grafikont az alábbi forráskóddal készítettük el:

A grafikon rendelkezik vizuális komponens mögötti adatmodellel, hiszen MVC szerkezetű komponens. Ez egy CategoryDataset típusú objektum. Ennek factory metódusa három paramétert vár: a jelmagyarázatot (rowKeys – legends), az Y tengelyen megjelenő feliratokat (columnKeys – jobTitleCountEmployees) és az adatokat (data – datas). Az első 3 elemű String[]: "Maximum fizetés", "Átlagfizetés", "Minimum fizetés". A második 19 elemű szöveges tömb: "Accountant (5 fő)", "Accounting Manager (1 fő)", …, "Stock Manager (5 fő)". A harmadik 3*19-es méretű kétdimenziós double típusú tömb, a megjelenítendő értékekkel: {{9000, 7920, 4200}, {16000, 12000, 8200}, , {8500, 7280, 5500}}.

A szükséges adatok megadását követően meg kell adni a grafikon megjelenítését meghatározó adatokat. Ezt egy CategoryPlot típusú objektum teszi lehetővé, amely konstruktora négy paramétert vár. Az első az adatforrás ( cd), a második az Y tengely felirata ( "Munkakör és létszám"), a harmadik az X tengely – alapértelmezetten felül megjelenő – felirata ( "Fizetés"), a negyedik a diagramtípushoz tartozó megjelenítő funkcióra utaló interfész képességeivel rendelkező névtelen objektum. Ez a 3D oszlopdiagram fekvő és egymást részben átfedő/eltakaró oszlopokkal jelenik meg.

Végül az elkészült ChartPanel típusú objektumra helyezett JFreeChart típusú diagramot hozzá kell adni a JFrame típusú GUI tartalompaneljének egy BorderLayout elrendezésmenedzserű paneljéhez.

Az elkészült grafikon többféle szakterületen is hasznos lehet. Értelmezése során összefüggéseket fogalmazhatunk meg és következtethetünk is.

A bejegyzéshez tartozó forráskódot ILIAS e-learning tananyagban tesszük elérhetővé tanfolyamaink résztvevői számára.

A feladat adatfeldolgozó része a Java EE szoftverfejlesztő tanfolyam 9-12. óra: XML feldolgozás, a grafikont megjelenítő része a Java SE szoftverfejlesztő tanfolyam 29-36. óra: Grafikus felhasználói felület alkalmához kapcsolódik.

Kígyókocka grafikus felületen

Kígyókocka

KígyókockaA JavaFX grafikus felhasználói felületének és eseménykezelésének megvalósítása némileg eltér más Java GUI implementációk működésétől, például swing vagy Java3D. Főként animációk során hasznos használni. Megközelítése természetesen objektumorientált: a térbeli objektumok koordinátái, viselkedésük, transzformációkkal valósul meg, és azok is objektumok. A korábban elkészített konzolos kígyókocka programot valósítjuk meg most JavaFX GUI-val.

Ez egy két részből álló blog bejegyzés 2. része. A blog bejegyzés 1. része itt található.

A program működése

Kígyókocka JavaFX grafikus felületen

A program megvalósítása

A start() JavaFX életciklust indító eljárás felépíti a createGridUI() függvényt meghívva a felhasználói felületet (színpad/jelenet JavaFX-ben), beállítva a méreteket, címsort, és meghívja az eseménykezelésért felelős handleRotateButtons() eljárást.

A createGridUI() függvény a grafikus felhasználói felület elemeit paraméterezi (szerepe szerint Factory metódus). Öt elemből álló rács ( GridPane osztályú grid nevű objektum) készül el, amelyre nyilakat tartalmazó nyomógombok (pl.: Button típusú btLeft objektum) kerülnek fel a négy égtájnak megfelelően, valamint rajta középen helyezkedik el a kígyókocka 3D megjelenítését megvalósító objektum. A nyilak entitásai az Unikód karaktertáblából származnak. A kígyókocka objektumot a meghívott createSnakeCube() függvény hozza létre. A Node osztályú snakeCube nevű objektum geometriai transzformációs objektumot is hozzá kell rendelni: ez most a négyirányú forgatást megvalósítani képes névtelen Rotate osztályú objektum lesz. A forgatást 5 paraméterrel célszerű beállítani (van rá megfelelő túlterhelt konstruktor), ezek rendre: szög, X, Y, Z tengely origója és a forgatás tengelye. Az objektumok tulajdonosi hierarchiája swing-es szemmel nézve szokatlannak tűnik, de szemléletben legalább azonos a Java3D és a JavaFX megvalósítás.

A createSnakeCube() függvény előállítja a színpadra/jelenetbe kikerülő kígyókockát Node osztályú objektumként. A konstans CUBE tömb egységvektor rendszerben tartalmazza a kígyót alkotó kockák középpontjait. Az első ciklus mindezt nagyítást alkalmazva skálázza. A második ciklus koordináta és pont transzformációk alkalmazásával ( moveToMidPoint: eltolás középre, rotateAroundCenter: forgatás a középpont körül) a kiinduló állapotnak megfelelő méretben és pozícióban elhelyezi a kígyó útvonalát mutató hengerobjektumokat. A konstrukciós és transzformációs műveletek esetén alkalmazkodni kell ahhoz, hogy a JavaFX koordinátarendszerben az X jobbra, az Y lefelé, a Z pedig befelé (a nézőponttól távolodva a térben) növekszik. A matematikai hátteret részletesen most nem magyarázzuk el.

A handleRotateButtons() eljárás a forgatás 4 nyíl eseménykezelésének hozzárendelést végzi el. A nyomógomb objektumok setOnAction() hozzárendelő metódusának paramétere EventHandler funkcionális interfésszel és lambda művelettel működik. A forgatás irányát hozzárendeljük a megfelelő nyomógombhoz. Ez még csak végrendelkezés a jövőre: csak definiáljuk, hogy minek kell majd történnie, ha bekövetkezik az esemény (valamelyik nyílra/nyomógombra kattint a felhasználó).

A rotateSnake() eljárás minden nyíl feliratú nyomógombra kattintva reagál a bekövetkezett eseményre. A rotateAxis objektum a forgatás tengelye, a paraméterként átvett direction enum-tól függ, szinkronban azzal a nyomógombbal, amelyikre kattintott a felhasználó.

Ötletek a továbbfejlesztésre

  • Lehetne többféle irány is, például a négy sarokba átlós vagy mélységi irányú elforgatással.
  • Beépülhetne többféle transzformáció is, például skálázás (kicsinyítés, nagyítás), eltolás (közelítés, távolítás).
  • A kígyó útvonalát mutató hengerobjektumok kirajzolásának sorrendjén lehetne változtatni, mert a megjelenítés nem tökéletes. Jelenleg néhány helyzetben lehetetlennek, Escher lehetetlen konstrukcióihoz hasonlónak tűnhet a kígyókocka. Ha a tér mélységéből a nézőpont felé közeledve rajzolnánk ki a hengerobjektumokat, akkor a 3D látvány nem sérülne.

Tanfolyamainkon JavaFX grafikus felülettel hangsúlyosan nem foglalkozunk, de egy-egy kész forráskódot közösen megbeszélünk, és össze is hasonlítjuk a swing-es változattal. Fejlesztünk játékprogramot, de inkább konzolosan, vagy swing-es ablakban, vagy webes alkalmazásként.

A grafikus felületek felépítésének megismerése fontos lépcső az objektumorientált programozás elmélyítéséhez, gyakorlásához. A grafikus felületekhez egy másik lényeges szemléletváltás is kapcsolódik, hiszen a korábbi algoritmusvezérelt megközelítést felváltja az eseményvezérelt (eseménykezelés).

Tudatosan hangsúlyozott MVC-s projektben megoldva a feladatot, a modell rétegben tárolhatnánk többféle kígyókocka megjelenítéséhez és animációjához szükséges adatszerkezetet és transzformációs objektumokat/metódusokat is és a nézet/vezérlő rétegekben biztosíthatnánk ezek közül a kijelölést/kiválasztást menüvel, ikonokkal, eszköztárral, gyorsbillentyűkkel.

A bejegyzéshez tartozó teljes forráskódot ILIAS e-learning tananyagban tesszük elérhetővé tanfolyamaink résztvevői számára.

Tanfolyamaink orientáló moduljának 9-12. óra: Mesterséges intelligencia alkalmához kapcsolódóan a kígyókocka véletlenszerű előállítása helyett stratégiával rendelkező visszalépéses algoritmust specifikálhatunk és implementálhatunk.

Ez egy két részből álló blog bejegyzés 2. része. A blog bejegyzés 1. része itt található.