RYSOWANIE W SWT - Linux Magazine
Transkrypt
RYSOWANIE W SWT - Linux Magazine
PROGRAMOWANIE Draw2D Biblioteka Draw2D RYSOWANIE W SWT Jeśli tworzysz aplikacje oparte o SWT w środowisku Eclipse i chcesz mieć możliwość zaprezentowania użytkownikom złożonych modeli, diagramów czy wykresów w atrakcyjny sposób, zastanów się, czy nie wykorzystać możliwości biblioteki Draw2D. KAMILA FOLTA W artykule przedstawione zostaną najważniejsze cechy biblioteki Draw2D oraz zaprezentowane niektóre spośród wielu jej możliwości. Artykułowi towarzyszy prosty program napisany przy użyciu bibliotek SWT i Draw2D, ilustrujący omawiane zagadnienia. Biblioteka Draw2D – system lekkich komponentów Draw2D jest biblioteką, która została zaprojektowana głównie jako pomoc w tworzeniu widoków graficznych w środowisku Eclipse. Na jej podstawie zbudowany został złożony system edytowania modeli graficznych (Graphical Editting Framework – GEF). Draw2D jest jednak biblioteką samodzielną i jako taka może być użyta w innych aplikacjach. Draw2D jest systemem „lekkich” komponentów, co oznacza, że tworzone w jego ra- 82 NUMER 17 CZERWIEC 2005 mach obiekty graficzne (tzw. figury) nie zajmują zasobów w systemie okien, symulują jedynie zachowanie jego obiektów: mogą posiadać fokus, zaznaczenie, indywidualny wygląd wskaźnika myszy; mogą reagować na zdarzenia związane z myszą; posiadają własny układ współrzędnych i kontekst graficzny. Kolejną ich zaletą jest to, że mogą posiadać dowolny, niekoniecznie prostokątny, kształt. Struktura programu Na Listingu 1 przedstawiona została podstawowa struktura programu korzystającego z biblioteki Draw2D. System Draw2D został osadzony bezpośrednio na obiekcie klasy Shell (może to być dowolny obiekt klasy Canvas). Obiekt „panel” klasy Figure służy jako obszar, na którym będą umieszczane inne figury. Na Listingu do panelu został dodany jedynie obiekt klasy RectangleFigure. WWW.LINUX-MAGAZINE.PL Listing 1: Podstawowa postać programu korzystającego z Draw2D Display display = new Display (); Shell shell = new Shell (display); LightweightSystem lws = newU LightweightSystem (shell); Figure panel = new Figure (); lws.setContents (panel); // dodawanie figur do panelu panel.add (new RectangleFigure ()); shell.open (); while (!shell.isDisposed ()) { while (!display.readAndDispatchU ()) { display.sleep (); } } Draw2D W przykładowym programie dołączonym do artykułu powyższa struktura została nieco zmieniona: obiekty LeightweightSystem osadzone są na obiektach klasy Canvas, będących zawartością zakładek notatnika (TabFolder), dzięki czemu na każdej zakładce zaprezentowane zostały nieco inne aspekty biblioteki Draw2D. Figury Podstawowym obiektem graficznym jest obiekt klasy Figure, zwany figurą. Każda figura posiada określone położenie i rozmiar, może zostać zaznaczona lub stać się adresatem skupienia. Figury mogą wyświetlać pod- Figure implementuje również mechanizm rysowania i odświeżania widoku. Klasy pochodne: kształty i elementy interaktywne Kształty to obiekty dziedziczące po klasie Shape. Są to figury dowolnych kształtów wraz z wypełnieniem. Mogą również posiadać obramowanie, dla którego można ustalić szerokość oraz styl linii. Kształty potrafią rysować się również w trybie XOR. Przykładami kształtów zdefiniowanych w Draw2D są: elipsa, prostokąt, trójkąt, zaokrąglony prostokąt oraz linia łamana. Oprócz klasycznych kształtów biblioteka Rysunek 1: Przykład użycia podstawowych klas kształtów. PROGRAMOWANIE Zdarzenia i odświeżanie. Jak to działa? Mechanizm delegowania zdarzeń i odświeżania widoku jest najważniejszym zadaniem obiektu klasy LeightweightSystem. Obiekt ten odpowiedzialny jest za odwzorowywanie zdarzeń zachodzących na obiekcie SWT, będącym obiektem-hostem na zdarzenia zachodzące na obiektach graficznych, którymi LeightweightSystem zarządza. Składa się on z trzech głównych komponentów. Pierwszym z nich jest figura nadrzędna (root figure). Jest to obiekt klasy LeightweightSystem.RootFigure i stanowi on rodzica dla figury, która jest główną figurą aplikacji Rysunek 2: Klasa LeightweightSystem oraz klasy towarzyszące, odpowiedzialne za obsługę zdarzeń. powiedzi, posiadać oryginalny wygląd wskaźnika myszy oraz reagować na zdarzenia, między innymi pochodzące od myszy, lecz także wywołane zmianą struktury (np.: dodaniem lub usunięciem figury-dziecka do/ z figury-rodzica) oraz zmianą swojego położenia lub rozmiaru. Rozmieszczeniem figur-dzieci w figurze-rodzicu zajmuje się menedżer rozmieszczenia, dostępny z obiektu Figure. API klasy Figure pozwala na sprawdzenie, czy figura przecina pewien prostokąt oraz czy nastąpiło kliknięcie na obszarze danej figury (lub jednego z jej dzieci). Przy operacjach takich jak przesunięcie czy zmiana rozmiaru pomocny może się okazać interfejs Translatable (klasy Dimension, PointList, Point, Rectangle), mający dwie metody: void performScale (doubleU scaleFactor) void performTranslate (int dx,U int dy). daje programistom do dyspozycji zbiór figur reprezentujących elementy interaktywne: przyciski czy pola wyboru (checkbox). Z tego względu biblioteka może być bardzo cenna dla tych programistów, którzy chcą nie tylko prezentować użytkownikom statyczne dane w postaci schematów czy układów, lecz również umożliwić prostą interakcję z prezentowaną treścią. Przykład użycia figur zaprezentowano na Rysunku 1. Kontekst graficzny Każda figura ma tzw. kontekst graficzny, tj. obiekt stanowy reprezentujący zbiór właściwości graficznych figury takich jak czcionka, kolor tła, kolor pierwszoplanowy czy styl linii. Kontekst graficzny jest obiektem klasy Graphics. Jest on argumentem metody paint (), wywoływanej przez system graficzny zawsze wtedy, gdy figura wymaga odrysowania. WWW.LINUX-MAGAZINE.PL (figura główna aplikacji to ta figura, którą przekazuje się jako parametr „contents”) w wywołaniu leightweightSystem.setContentsU (contents) i do której dodawane są następnie pozostałe figury tworzące scenę (patrz Rysunek 2). Figura nadrzędna nie jest całkowicie niezależna od obiektu-hosta; dziedziczy po nim niektóre właściwości: kolor pierwszoplanowy, kolor tła oraz czcionkę. Drugim komponentem jest obiekt klasy SWTEventDispatcher, odpowiedzialny za obsługę zdarzeń. Jego zadanie polega na tłumaczeniu zdarzeń z systemu SWT nadchodzących do obiektu-hosta na zdarzenia systemu Draw2D, które są przekazywane figurze nadrzędnej. LightweightSystem już w konstruktorze dodaje słuchaczy zdarzeń SWT do obiektu Canvas. Kiedy obiekt Canvas otrzy- NUMER 17 CZERWIEC 2005 83 PROGRAMOWANIE Draw2D ma zdarzenie SWT, LightweightSystem wywołuje odpowiednią metodę rozdzielającą obiektu SWTEventDispatcher. To jednak nie wszystko. Dispatcher śledzi stan zaznaczenia figur, zna ich położenia i wie, nad którą figurą znajduje się w danym momencie wskaźnik myszy. Dzięki temu może efektywnie przekazywać zdarzenia do odpowiednich figur: tych, które znajdują się w miejscu kliknięcia, a jednocześnie zarejestrowały odpowiedni obiekt nasłuchujący. Dispatcher obsługuje również wyświetlanie podpowiedzi. Kolejnym komponentem, odpowiedzialnym za obsługę żądań, jest menedżer odświeżania, który odpowiada za rysowanie i odświeżanie figur w systemie Draw2D. Kiedy z obiektu Canvas nadchodzi żądanie odrysowania, LeightweightSystem wywołuje metodę handleUpdate () menedżera odświeżania. Menedżer przechowuje listę tych figur, których stan zmienił się od ostatniego narysowania i wymagają odświeżenia; do jego zadań należy utrzymywanie tej listy w postaci możliwie najkrótszej tak, aby operacja odświeżenia następowała możliwie najszybciej. Domyślną implementację menedżera stanowi klasa DeferredUpdateHandler, która wysyła asynchroniczne zdarzenia odrysowania figur, kolejkowane w wątku obsługującym interfejs graficzny. Ważniejsze klasy i przykłady zastosowań W dalszej części artykułu przedstawione zostaną ważniejsze klasy systemu Draw2D wraz z krótkimi przykładami ich zastosowania. Ramki Często podczas rysowania elementów graficznych zachodzi potrzeba obramowania ich w pewien specyficzny sposób, czy to w celu podkreślenia funkcji elementu, czy to dla oznaczenia stanu, w jakim element się znajdu- je, czy też w końcu jedynie po to, aby wyraźnie odróżnić dany element od tła. Właśnie do tego celu służą ramki (borders). W pakiecie Draw2D znajduje się kilka klas implementujących interfejs Border. Niektóre z nich zostały krótko scharakteryzowane w Tabeli 1. W przykładowym programie zostały utworzone obiekty klasy Label, które następnie zostały otoczone różnego typu ramkami. Obiektom klasy Label jako parametr napisowy podano nazwę klasy ramki, która otacza obiekt. Efekt działania programu przykładowego pokazany został na Rysunku 3. Rysunek 3: Ramki implementujące interfejs Border. Menedżer rozmieszczenia Menedżery rozmieszczenia to klasy, które mają na celu ustalenie rozmiaru i prawidłowe rozmieszczenie wszystkich „dzieci” danej figury. Czynią to w następujący sposób: pytają każde z „dzieci” o wymiary obszaru, który chcą zajmować, a następnie, stosując właściwy sobie algorytm, próbują ustalić rozmieszczenie elementów względem siebie i obliczyć ostateczny rozmiar figury-rodzica. Menedżery rozmieszczenia biorą pod uwagę tzw. ograniczenia, czyli dodatkowe informacje pomocne podczas rozmieszczania, jakie posiadać może każda z figur. Figury mają metody pozwalające na dostęp do obiektów ograniczeń, Tabela 1: Zestawienie klas implementujących interfejs Border CompoundBorder tworzy ramkę będącą, złożeniem dwóch ramek. FocusBorder tworzy ramkę z linii przerywanej, podobną do otaczającej elementy ramki zaznaczenia. FrameBorder tworzy ramkę podobną do TitleBarBorder, domyślnie bez brzegów sprawiających wrażenie wypukłości. Może być używana do tworzenia figur ze znajdującym się u góry tytułem. GroupBoxBorder tworzy ramkę z napisem, przypominającą element grupujący w systemie okienkowym. LineBorder otacza element linią o podanej szerokości. MarginBorder otacza element „wolną przestrzenią”, której szerokość można określić dla każdego z brzegów (górny, dolny, lewy, prawy) oddzielnie. SimpleLoweredBorder ramka, która nadaje otaczanemu elementowi charakter wklęsłego względem otoczenia. SimpleRaisedBorder ramka, która nadaje otaczanemu elementowi charakter wypukłego względem otoczenia. TitleBarBorder tworzy ramkę wraz z tytułem; przypomina pasek tytułu w okienkach systemów okienkowych. Dodaje „wypukłe” brzegi. 84 NUMER 17 CZERWIEC 2005 a zadaniem menedżera jest takie rozmieszczenie figur, aby ograniczenia te były spełnione. Obiekty ograniczeń reprezentowane są przez tak różne klasy, że nie zdefiniowano nawet wspólnego dla nich wszystkich interfejsu. Klasy te ściśle zależą od tego, z jakim menedżerem rozmieszczenia mamy do czynienia. Na przykład, klasą obiektów ograniczeń dla menedżera klasy XYLayout jest Rectangle, a menedżer DelegatingLayout spodziewa się, że figury, którymi zarządza, będą miały ograniczenia implementujące interfejs Locator. Przykład użycia menedżerów rozmieszczenia przedstawia Rysunek 4. Przykładem menedżera rozmieszczenia jest FlowLayout. Jego cechą charakterystyczną jest algorytm, w którym elementy umieszczane są w wierszach lub kolumnach, a jeśli ich liczba sprawia, że nie mieszczą się w widocznym obszarze, szereg jest „zawijany” i elementy umieszczane są w kolejnym wierszu lub kolumnie. Parametr konstruktora „isHorizontal” określa, czy elementy mają Rysunek 4: Przykład użycia menedżerów rozmieszczenia dostępnych w bibliotece Draw2D. Tabela 2: Klasy implementujące interfejs Locator ArrowLocator służy do pozycjonowania ozdobników (takich jak np. grot strzałki) na końcach połączenia BendpointLocator służy do pozycjonowania uchwytów przeznaczonych do „złamania” połączenia MidpointLocator umieszcza figurę dokładnie w połowie połączenia ConnectionEndpointLocator umieszcza figurę bliżej jednego z końców połączenia RelativeLocator pozycjonuje figurę na podstawie wartości zmiennoprzecinkowej [0..1], określającej położenie względem połączenia: lewy górny róg – 0, prawy dolny róg – 1. WWW.LINUX-MAGAZINE.PL Draw2D PROGRAMOWANIE Rutery – określanie trasy połączenia być umieszczane w wierszu (true) czy kolumnie (false). Innym menedżerem rozmieszczenia jest XYLayout, który nadaje figurom położenie i rozmiar określone w obiektach ograniczeń klasy Rectangle. Warstwy Warstwy to przezroczyste figury, które umieszczane są wewnątrz paneli (klasa LayeredPane). Dopiero na warstwach umieszczane są figury nieprzezroczyste. Warstwy używane są najczęściej w celu uzyskania efektu „wyRysunek 6: Wykorzystanie połączeń między węzłami z ruterami FanConnectionRouter i ManhattanConnectionRouter oraz kotwic: Dwie figury na płaszczyźnie można połączyć na dowolnie wiele sposobów. Stworzona więc została klasa ConnectionRouter, odpowiedzialna za wybór trasy, jaką biegnie połączenie między figurami. Istniejące implementacje wybierają trasy biegnące przez zbiór ustalonych punktów (BendpointConnectionRouter) lub trasy będące najkrótszymi ścieżkami w metryce euklidesowej (FanConnectionRouter) czy taksówkowej (ManhattanConnectionRouter). Na Rysunku 6 przedstawiony został przykład połączeń biegnących po najkrótszych ścieżkach w wymienionych metrykach. EllipseAnchor oraz LabelAnchor do połączeń między napisami i elipsą. niej inne figury mogą mieć ujemne współrzędne położenia. Inną ciekawą odmianą warstwy jest ScalableFreeformLayer, która udostępnia operację void setScale (double), pozwalającą na łatwe przeskalowanie wszystkich znajdujących się na niej figur. Przykład użycia warstw pokazano na Rysunku 5. Rysunek 5: Przykład użycia warstw. Każda figura dodawana jest do nowej warstwy. Warstwy znajdują się w skalowalnym panelu. suwania na wierzch” czy „przesuwania pod spód” widocznych figur. Szczególnie dobrze nadaje się do tego celu obiekt klasy FreeformLayer, umieszczany na panelu FreeformLayeredPane. Przedrostek „Freeform” w nazwie klasy figury oznacza, że figura „rozciąga się” we wszystkich kierunkach, a umieszczane na Tabela 3: Klasy kotwic implementujące interfejs ConnectionAnchor ChopboxAnchor kotwica położona w tym punkcie na krawędzi figury (będącej węzłem), w którym połączenie przecięłoby krawędź, gdyby poprowadzić je w kierunku środka figury LabelAnchor kotwica przeznaczona dla węzłów będących obiektami klasy Label ElipseAnchor kotwica, która położona jest w tym punkcie na krawędzi eliptycznej figury, w którym połączenie przecięłoby krawędź, gdyby poprowadzić je w kierunku środka figury; jest to wariant (lecz nie klasa pochodna) ChopboxAnchor XYAnchor kotwica położona w punkcie określonym przy pomocy współrzędnych. Połączenia, lokatory i kotwice Połączenia to obiekty, dzięki którym utworzenie grafu czy diagramu okazuje się bardzo proste. Dla każdego połączenia ustalane są dwa węzły (dwie figury): początkowy i końcowy. Zmiana położenia węzłów powoduje automatyczną zmianę położenia obiektu połączenia. Klasy implementujące interfejs Locator służą pozycjonowaniu figur względem innych figur. W szczególności klasa ConnectionLocator pozwala na zdefiniowanie położenia figur względem obiektu połączenia (klasa Connection), dzięki czemu figura połączenia może np. mieć na swoich końcach groty strzałek czy prezentować opis połączenia umieszczony np. w połowie odległości między jego końcami. Figury dołączone do połączenia (grot strzałki, opis) zachowują swoje położenie względem połączenia nawet wówczas, gdy połączenie zmienia swoje położenie na płaszczyźnie. Klasy Locator przedstawione zostały w Tabeli 2. Inny interesujący zbiór klas to tzw. kotwice, czyli obiekty definiujące punkty styku połączenia i węzła, zwane końcami połączenia. Zadaniem kotwicy jest nie tylko przechowywanie informacji o końcu połączenia, lecz również powiadamianie słuchaczy o zmianie położenia końca połączenia. Klasy kotwic przedstawiono w Tabeli 3. WWW.LINUX-MAGAZINE.PL Podsumowanie Draw2D to rozwiązanie dla tych programistów, którzy używają biblioteki SWT i chcieliby uatrakcyjnić wygląd swoich aplikacji poprzez umieszczenie w nich interesujących diagramów czy też prezentację interaktywnych schematów. Zachęcam do bliższego zapoznania się z tą biblioteką; mam nadzieję, że dołączone do artykułu dodatkowe materiały (diagramy klas i źródła przykładowego programu) pozwolą choćby pobieżnie zorientować się w jej możliwościach i zachęcą do jej praktycznego wykorzystania. ■ INFO [1] Artykuł pt. „Display a UML Diagram using Draw2D” http://www.eclipse.org/articles/Article-GEF-Draw2d/GEF-Draw2d.html [2] „Eclipse Development using the GEF and the EMF” http://www.redbooks.ibm.com /redbooks/pdfs/sg246302.pdf [3] Graphical Editing Framework, strona domowa (z listą mirrorów, z których można ściągnąć zbiór bibliotek GEF-SDK-3.0.1.zip, a wśród nich Draw2D) http://www.eclipse.org/gef/ [4] Materiały pomocnicze do artykułu: http://www.linux-magazine.pl/issue/17/ draw2d.tar.gz AUTOR Kamila Folta ukończyła studia na Politechnice Wrocławskiej w specjalności Inżynieria Oprogramowania. Od dwóch lat pracuje jako Specjalistka d.s. Rozwoju Oprogramowania w dużej firmie teleinformatycznej. W wolnych chwilach zajmuje się tłumaczeniem dokumentacji języka Python; jest gorącą zwolenniczką systemu Debian GNU/Linux oraz środowiska Gnome. NUMER 17 CZERWIEC 2005 85