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

Podobne dokumenty