Okná a toolbary programu DynaSim
![]() |
Trenčianska Univerzita Alexandra Dubčeka v Trenčíne
Fakulta Mechatroniky |
![]() |
Okná a toolbary programu DynaSim Diplomový projekt |
Autor: | Bc. Michal Janíček |
Pedagogický vedúci: |
Ing. Juraj Ďuďák |
Študijný odbor: | Mechatronika
|
Akademický rok |
2009/2010
|
1. | Platforma NetBeans |
2. | Vizuálna časť programu DynaSim |
3. | Dátová časť simulačných komponentov |
4. | Okná a toolbary programu DynaSim |
5. | Funkčná časť programu DynaSim
|
Obsah
Okná a toolbary programu DynaSim
Paleta
Paleta slúži ako knižnica komponentov, ktoré sa z nej dajú preniesť na editačnú plochu DynaSimu. Je aj súčasťou NetBeans IDE, kde napríklad Matisse GUI Builder ukladá AWT a Swing komponenty na paletu, a užívateľ ich potom môže pomocou drag&drop premiestniť na NetBeans editor. Aby sme mohli paletu v našom module používať, je potrebné pridať závislosť na Common Palette. Obsah palety je spravovaný PaletteController-om. PaletteController je prístupný DynaSimTopComponentu cez Lookup. Opäť si treba uvedomiť, že sa jedná o 2 okná, kde editačná plocha a paleta majú svoj vlastný TopComponent. PaletteController sa vytvára pomocou triedy PaletteFactory. Ako paleta môže vypadať je znázornené na nasledujúcom obrázku.
Pre tvorbu palety je možné zvoliť jeden z dvoch prístupov. Prvý spôsob je vytvoriť pre každý komponent .xml súbor, v ktorom je zapísané meno a popis komponentu. Takto vytvorené .xml súbory je potom potrebné zaregistrovať v layer.xml súbore DynaSim modulu. Druhý spôsob, ktorý som si ja pre tvorbu palety zvolil, je vytvoriť hierarchiu uzlov a z nej následne vygenerovať paletu. Je potrebné si uvedomiť, že takto vytvorená hierarchia uzlov sa skladá z troch vrstiev. Prvá vrstva predstavuje root uzol, ktorý predáme metóde createPallete() na vytvorenie jej komponentov. Druhá vrstva pozostáva z detských uzlov root uzla a definuje kategórie palety. Posledná vrstva definuje jednotlivé komponenty v kategóriách. Triedy pre tieto komponenty v programe DynaSim nesú meno Shape (tvar), preto aj na obr.12 sú označené ako Shape.
Všetky triedy pre vytvorenie palety a jej hierarchie uzlov sa nachádzajú v balíčku sk.tnuni.dynasim.palette. Trieda Category slúži len ako kontajner, ktorý v sebe drží dáta. Pri kategóriách nám postačuje, že poznáme ich meno, preto táto trieda obsahuje len getter a setter na nastavenie mena danej kategórie. Trieda CategoryChildren je už zaujímavejšia. Jej deklarácia je nasledovná:
public class CategoryChildren extends Children.Keys
Trieda Children vytvára a manažuje detské uzly a správa sa ako ich kontajner. Tu je táto trieda použitá ako manažér kategórií, ktoré sa zobrazujú na palete. Kategórie palety slúžia na rozdelenie jednotlivých komponentov do skupín (kategórií). Akým spôsobom budú komponenty rozdelené, už zostáva na užívateľovi. Children.Keys obsahuje metódu na tvorbu uzlov createNodes(). Na triedu CategoryChildren sa môžeme pozerať ako na root uzol, lebo v sebe jedna jej inštancia bude obsahovať všetky existujúce kategórie.
Trieda CategoryNode je potomkom triedy AbstractNode a jej konštruktor dostane ako parameter inštanciu triedy Category, ktorá je následne predaná triede ShapeChildren, čím sa dostávame do ďalšej vrstvy hierarchie.
Trieda Shape je trieda, ktorá je ekvivalent triedy Category, ale na rozdiel od Category v sebe drží dáta pre jednotlivé komponenty a to je number – poradové číslo, v súčasnej verzii nepoužívané, category – meno kategórie do ktorej tento komponent patrí, title - meno komponentu získané z XML súboru, image – cesta k súboru s ikonou, ktorá sa zobrazí na palete vedľa mena komponentu, name – meno XML súboru bez prípony, description – tooltip pre daný komponent.
Trieda ShapeChildren je ekvivalnetom CategoryChildren, na rozdiel od CategoryChildren v sebe drží všetky uzly typu Shape danej kategórie. Nakoniec trieda ShapeNode je zodpovedná za zobrazovanie jednotlivých komponentov v palete. Keďže samotné uzly komponentov už nemajú ďalšie detské uzly, supertriede predáme prázdny kontajner detských uzlov Children.LEAF. Cez metódy setDisplayName a setIconWithExtention a ďalšie podobné metódy nastavíme komponentu ako sa má v palete zobrazovať, t.j aké má mať na palete komponent meno, akú bude mať ikonu, tooltip atď. Tieto parametre sú uložené v triede Shape. Tie sa do nej dostanú za pomoci triedy BrowseFolders.
BrowseFolders je jednoduchá trieda, ktorá prechádza priečinkami na zvolenej ceste. V rámci projektu DynaSim sa nachádza priečinok s názvom library. Ak sa pozrieme na obr.12, library sa nachádza na úrovni root. Library má svoje podpriečinky, tých môže byť ľubovoľné množstvo a môžu mať ľubovoľné pomenovanie. Tieto podpriečinky predstavujú jednotlivé kategórie a platí rovnica: meno podpriečinku = meno kategórie. Nakoniec sa prehľadá obsah týchto podpriečinkov. Ak obsahujú SVG súbory, začne sa hľadať či existuje XML súbor s rovnakým názvom. Ak áno, začne sa s ich parsovaním a z ich root elementov sa získa hodnota atribútu name. Tým sa získa meno komponentu. Ďalej sa hľadá, či existuje PNG alebo GIF súbor s rovnakým menom ako parsovaný XML súbor. V prípade že existuje, použije sa ako ikona komponentu v palete. Uloží sa aj meno súboru bez prípon a z XML súboru sa získa popis pre tooltip. Trieda BrowsFolders toto všetko v konštruktore vykoná pri jej vytvorení na zadanej ceste. Tá musí byť presne určená a ukazovať na miesto s adresárom library. Na nájdenie tohto priečinku sa použije FolderSearcher, vysvetlený v kap. 3.1. Tieto získané hodnoty sa zapíšu do dvojrozmerného poľa typu String menom itemList a cez ten sa potom pristupuje k jednotlivým položkám pri nastavovaní členských premenných inštancie Shape. Prvý rozmer poľa itemList určuje poradové číslo SVG súboru z ktorého je daný komponent. Druhý rozmer má uložené hodnoty v takomto poradí: 0 – poradové čislo, 1 – názov kategórie, 2 – názov SVG súboru bez prípony, 3 – cesta k ikone komponentu, 4 – meno komponentu z XML, 5 – tooltip z XML súboru. BrowseFolders obsahuje aj statickú premennú folderList. Je to jednorozmerné pole Stringov a obsahuje zoznam adresárov v library.
Doteraz bola vysvetlená len hierarchia uzlov palety a spôsob akým získavajú potrebné dáta. Teraz treba vysvetliť, ako sa vytvorí paleta samotná za pomoci týchto uzlov. Na tvorbu palety sa používa trieda PaletteController. Na tieto účely som si vytvoril triedu PaletteSupport, ktorá má na starosti manažovanie všetkých vlastností palety ako sú tvorba inštancie PaletteController, akcie paletz, či drag&drop. Trieda obsahuje metódu na vytvorenie palety createPallete. Jej funckia je nasledovná.
public static PaletteController createPalette() { AbstractNode paletteRoot = new AbstractNode(new CategoryChildren()); paletteRoot.setName("Palette Root"); return PaletteFactory.createPalette( paletteRoot, new MyActions(), null, new MyDnDHandler() ); }
Ako možno z kódu vidieť, najskôr sa vytvorí AbstractNode menom paletteRoot z nášho CategoryChildren, čo je trieda ktorá v sebe drží všetky kategórie palety. Tá si následne vytvorí jednotlivé CategoryNode a tie následne volajú ShapeChildren. Inými slovami vytvorením paletteRoot sa nám automaticky vytvorí celá hierarchia uzlov potrebná na tvorbu palety. Funkcia potom vráti PaletteController, ktorý sa vytvorí cez PaletteFactory. Aby sme však pochopili, aká paleta sa vytvorí treba preskúmať premenné createPalette.
public static PaletteController createPalette(Node node, PaletteActions pa, PaletteFilter pf, DragAndDropHandler dadh) throws IOException
Node predstavuje root uzol, pa sú akcie palety. Pre súčasné potreby palety nie sú potrebné žiadne špeciálne akcie a tak vygenerované metódy v triede MyActions() vracajú null. Filter pf má hodnotu null, pretože nechceme použiť žiadne filtrovanie komponentov palety. Nakoniec sa vytvorí nový DragAndDropHandler, ktorého detská trieda je zahniezdená priamo v PaletteSupport triede. Tá bude bližsie rozobraná v nasledujúcej podkapitole.
Aby sme vytvorili paletu musíme vytvoriť inštanciu triedy PaletteController v DynaSimTopComponent cez PaletteSupport.createPalette(). To však nestačí na zobrazenie, pretože sa nachádzame v inom TopComponente. Paleta má vlastný TopComponent a aby medzi nimi vznikla určitá forma komunikácie a dokázali si medzi sebou predávať hodnoty, ju nutné vložiť kontrolér palety do Lookupu. Pridanie PaletteController s menom controler do Lookupu Topcomponentu je nasledovné:
associateLookup(new AbstractLookup(content)); content.add(controller);
InstanceContent content sa používa na zobrazovanie Properties okna a samotný kontrolér palety sa do neho pridáva ako položka. Podobne ako content aj controler sa prenáša medzi triedami ako premenná v ich konštruktore, z DynaSimTopComponent-u do DynaSimScene, z tej ďalej do Component_Widget. Tu sa musí v metóde notifyStateChanged opäť controller pridať do content, pretože po vytvorení nového Properties okna sa paleta zruší. Jej pridaním do content sa opäť vytvorí.
Implementácia Drag&Drop funkcionality
Drag&Drop z palety zabezpečuje trieda MyDnDHandler, ktorá je zahniezdená v PaletteSupport aj jej inštancia je vytvorená v konštruktore PaletteController-a. Táto trieda je detskou triedou triedy DragAndDropHandler. Preťažená je len 1 funkcia a to customize. Tá má na práci prenos dát určitého typu medzi paletou a TopComponentom, ktorý Drop z palety akceptuje. Keďže po dropnutí komponentu z palety na plochu editora chceme, aby sa na danom mieste vytvoril nový grafický komponent typu Component_Widget. Na jeho tvorbu ale potrebujeme najskôr inštanciu typu MyNode a z tej už dokážeme vytvoriť ďalej celú grafickú aj dátovú časť komponentu. Konštruktor pre MyNode má 4 premenné a na ich prenos z palety do inštancie DynaSimScene sa potrebujeme zamerať. Štvrtá premenná je lokácia bodu na ktorý sme položili komponent z palety na plochu editora takže nám stačí prenos troch premnných a to je: meno komponentu, kategória do ktorej patrí a meno SVG súboru bez prípony pre vykreslenie komponentu. Akým spôsobom sa teda tieto 3 hodnoty premenných prenesú z palety do DynaSimScene? Najskôr sa v metóde customize nájde uzol palety z lookup-u. Z neho cez rôzne metódy ako getDisplayName a pod. uložíme do poľa typu String meno, kategótiu a meno súboru. Potom do exTransferable typu ExTransferable, čo je premenná metódy customize uložíme pomocou metódy put toto pole Stringov. Tým sme si zabezpečili prenos týchto hodnôt z palety na iné miesto. Problém je, že miesto, na ktoré chceme tieto hodnoty preniesť musí byť na ne pripravené.
Miesto kam chceme tieto hodnoty preniesť a na ktorom chceme vytvoriť komponenty je inštancia typu DynaSimScene. Ako už bolo viac krát spomínané, DynaSimScene je tiež widget a tým pádom mu je možné pridať rôzne akcie ako iným widgetom. Preto môžeme do jeho konštruktora napísať:
getActions().addAction(ActionFactory.createAcceptAction(new AcceptProvider())
Tento riadok kódu nám zabezpečí, že DynaSimScene bude akceptovať veci dropnuté na jeho plochu, ale AcceptProvider má 2 metódy, ktoré sa automaticky vygenerujú a ich kód treba doplniť. Metóda isAcceptable vždy vracia ConnectorState.ACCEPT. To nám zabezpečí, že akýkoľvek komponent z palety dropnutý na editačnú plochu je akceptovaný editorom. Druhá metóda je oveľa dôležitejšia a jej deklarácia vypadá nasledovne:
public void accept(Widget widget, Point point, Transferable transferable)
Z premennej point získame bod na ktorý chceme premiestniť komponent a v transferable je uložené pole Stringov, ktoré sme poslali z MyDnDHandler cez exTransferable premennú. K poľu Stringov sa dostaneme z transferable nasledovným spôsobom: String name[] = getNameFromTransferable(transferable);
Tým pádom máme k dispozícii všetky premenné na vytvorenie inštancie typu MyNode. V tejto metóde accept sa vykonajú všetky úkony na tvorbu komponentu na DynaSimScene. Vytvorí sa nový widget cez addNode a cez addPin sa mu pridajú vo for-slučke všetky piny. Nakoniec kontrolér palety cez metódu clearSelection() odznačí označenú položku na palete.