Draw2d: Unterschied zwischen den Versionen

Aus Eclipse
Wechseln zu: Navigation, Suche
(Anordnungen (Layouts))
(Anordnungen (Layouts))
Zeile 316: Zeile 316:
 
* ''AbstractLayout'' - eine abstrakte Implementierung mit vielen Hilfsfunktionen
 
* ''AbstractLayout'' - eine abstrakte Implementierung mit vielen Hilfsfunktionen
 
* ''AbstractHintLayout'' - Basisklasse für die ''Layouts'', die die bevorzugte Größe der Figuren berücksichtigen.
 
* ''AbstractHintLayout'' - Basisklasse für die ''Layouts'', die die bevorzugte Größe der Figuren berücksichtigen.
 +
* ''ToolbarLayout'' - die Kinder werden senkrecht oder waagerecht an einer Linie platziert.
 +
 +
<br>
 +
In folgenden Beispielen werden die Figuren wie folgt erzeugt:
 +
<br>
 +
<source lang=java>
 +
Label label1 = new Label("Label 1");
 +
LineBorder borderL1 = new LineBorder();
 +
label1.setBounds(new Rectangle(10, 10, 100, 30) );
 +
label1.setBorder(borderL1);
 +
contents.add(label1);
 +
 +
Label label2 = new Label("Label 2");
 +
label2.setBounds(new Rectangle(150, 150, 100, 30) );
 +
label2.setBorder(borderL1);
 +
contents.add(label2);
 +
 +
Ellipse ellipse = new Ellipse();
 +
ellipse.setBackgroundColor(ColorConstants.blue);
 +
ellipse.setBounds(new Rectangle(0, 0, 20, 40) );
 +
ellipse.setOpaque(true);
 +
contents.add(ellipse);
 +
 +
RectangleFigure rectangle = new RectangleFigure();
 +
rectangle.setBounds(new Rectangle(20, 40, 120, 30) );
 +
rectangle.setOpaque(true);
 +
rectangle.setBackgroundColor(ColorConstants.red);
 +
contents.add(rectangle);
 +
</source>
 +
<br>
 +
== ''ToolbarLayout'' ==

Version vom 12. Juni 2010, 23:07 Uhr

Einführung

Draw2d ist ein auf Diagrammenzeichnung orientiertes Vektorgrafik-Toolkit für Eclipse. Es ist als Teil von GEF entstanden, kann aber unabhängig von GEF benutzt werden. Draw2d weist nicht so viele Möglichkeiten für eine komplizierte Grafikdarstellung auf, legt aber Akzent auf die in verschiedenen Diagrammentypen vorkommende Elemente wie z.B. rechteckige Blöcke mit Text oder Verbindungslinien jeglicher Art. Es bietet weiter die Möglichkeiten für eine automatische Anordnung der Elemente auf der Zeichnung sowie das sogenannte "Hit Testing" - die Erkennung des Elementes unter dem Mauszeiger.
Draw2d benutzt Standard Widget Toolkit (SWT, org.eclipse.swt) für das Rendering der Objekte.
Als Interface zwischen draw2d und SWT dient die Klasse org.eclipse.draw2d.LightweightSystem. Sie bietet die Möglichkeit, die draw2d-Objekte auf dem SWT-Canvas zu zeichnen, ohne das für die draw2d-Objekte OS-Ressourcen beansprucht werden (deswegen heißt die Klasse LightweightSystem).
Jedes sichtbare draw2d-Objekt ist eine Figur.

Packages

  • org.eclipse.draw2d - alle wichtigsten Klassen sind hier definiet.
  • org.eclipse.draw2d.geometry - Basisklassen für geometrische Objekte wie Punkte, Vektoren, Rechtecke usw.
  • org.eclipse.draw2d.graph - Klassen für die Graphendarstellung.
  • org.eclipse.draw2d.images
  • org.eclipse.draw2d.internal
  • org.eclipse.draw2d.parts - enthält im Moment eine Thumbnail-Figur. Das ist eine Figur, die eine andere Figur verkleinert darstellen kann.
  • org.eclipse.draw2d.text - diverse Klassen für die Textdarstellung.
  • org.eclipse.draw2d.widgets

Figuren (Figures)

Eine Figur in Draw2d ist alles, was das Interface IFigure implementiert, wobei es nicht empfohlen wird, in eigenen Klassen direkt dieses Interface zu implementieren, sondern eigene Klassen von existierenden Draw2d-Figuren abzuleiten.
Jede Figur ist ein Container für weitere Figuren. Man kann aus Figuren ganze Hierarchiebäume erzeugen. In der Praxis, für die Darstellung auf einem SWT-Canvas müssen alle Figuren sich in einem Baum befinden. Das Wurzelobjekt dieses Baums, das auch eine Figur ist, wird dem LightweightSystem übergeben.
Jede Figur hat bevorzugte Größe, minimale Größe und maximale Größe.
In der Methode paint muss eine Figur sich Zeichnen. Dafür hat sie die Methoden der abstrakten Klasse org.eclipse.draw2d.Graphics zur Verfügung.

"Hello world" in draw2d

In diesem Beispiel wird die Zusammenarbeit von SWT und draw2d demonstriert. Ausserdem sieht man hier, wie die Wurzelfigur für das LightweightSystem eingestellt wird und wie ein Figurenbaum entsteht.
In diesem Beispiel werden die SWT-Klassen Display und Shell benutzt. Weit SWT ausserhalb des Geltungsbereiches dieses Artikels ist, werden diese Klassen hier nicht weiter beschrieben. Siehe dazu die SWT-Dokumentation.

import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.draw2d.*;
 
public class draw2d_test {
 
	public static void main(String[] args) {
		Display display = new Display();
		Shell shell = new Shell(display);
 
		LightweightSystem lwSystem = new LightweightSystem(shell);
 
		IFigure contents = new Figure();
		LineBorder border = new LineBorder();
		border.setWidth(5);
		contents.setBorder(border);
 
		ToolbarLayout layout = new ToolbarLayout(false);
		layout.setSpacing(10);
		contents.setLayoutManager(layout);
 
		Label label = new Label("Hello world!!!");
		contents.add(label);
 
		shell.open();
 
		lwSystem.setContents(contents);
		while(!shell.isDisposed() )
		{
			if(!display.readAndDispatch() )
			{
				display.sleep();
			}
		}
 
		display.dispose();
	}
 
}


Das Output von diesem Beispiel sieht so aus:
Dratva 1919 example1 draw2d.png

In diesem Beispiel werden zwei Figuren erstellt, contents und label. Das label wird mit add an das contents angehängt, contents wird als Wurzelfigur im lwSystem gesetzt. Damit man die Wurzelfigur contents sehen kann, wird ihr ein dicker Rahmen hinzugefügt.

Zeichnungsreihenfolge und Überlappung der Figuren

Verbindungen und Routing

Verbindungen und Anker

Eine spezielle Art der Figuren sind die Verbindungen. Verbindungen müssen das Interface Connection implementieren, das das IFigure erweitert.
Diese Verbindungsfiguren stellen Verbindungslinien dar, die zwei Figuren miteinander verbinden. Der Verlauf einer solchen Linie wird von einem anderen Objekt - einem ConnectionRouter definiert.
Für die polygonartige Verbindungslinien existiert die Klasse org.eclipse.draw2d.PolylineConnection. Das ist die einzige Connection-Implementierung in draw2d. Diese Klasse wird auch im nächsten Beispiel verwendet.

 
	Label label1 = new Label("Label 1");
	LineBorder borderL1 = new LineBorder();
	label1.setBorder(borderL1);
	contents.add(label1);
 
	Label label2 = new Label("Label 2");
	label2.setBorder(borderL1);
	contents.add(label2);
 
	PolylineConnection connection = new PolylineConnection();
	connection.setSourceAnchor(new ChopboxAnchor(label1) );
	connection.setTargetAnchor(new ChopboxAnchor(label2) );
	contents.add(connection);


Dratva 1919 example2 draw2d.png



In diesem Beispiel sieht man, dass eine Verbindung zwei so genannte Anker braucht, um sicht richtig darzustellen. Ein Anker ist eine Implementierung des Interfaces ConnectionAnchor. Diese Anker werden nicht nur in PolylineConnection verwendet, sondern in jeder Connection-Implementierung. Die Methoden setSourceAnchor und setTargetAnchor sind nähmlich in diesem Interface definiert. Diese zwei Anker legen zwei Punkte fest, die von der Linie verbunden werden. Die Eigenschaften des hier verwendeten ChopboxAnchor werden im nächsten Beispiel besser demonstriert:

	Label label1 = new Label("Label 1");
	LineBorder borderL1 = new LineBorder();
	label1.setBounds(new Rectangle(10, 10, 100, 50) );
	label1.setBorder(borderL1);
	contents.add(label1);
 
	Label label2 = new Label("Label 2");
	label2.setBounds(new Rectangle(150, 150, 100, 50) );
	label2.setBorder(borderL1);
	contents.add(label2);
 
	PolylineConnection connection = new PolylineConnection();
	connection.setSourceAnchor(new ChopboxAnchor(label1) );
	connection.setTargetAnchor(new ChopboxAnchor(label2) );
	contents.add(connection);



Der Output von diesem Program sieht so aus:

Dratva 1919 example3 draw2d.png



Hier sieht man deutlich die Eigenschaften des in diesem Beispiel gewählten ChopboxAnchor. Die Linie wird zwischen Zentren der gewählten Figuren gezeichnet, wobei die Teile der Linie, die auf den Figuren selbst liegen, werden verborgen.

Neben dem ChopboxAnchor existieren in draw2d folgende vorgefertigten Implementierungen des ConnectionAnchor:

  • ConnectionAnchorBase - abstrakte Implementierung, die einen Listener-Mechanismus für die Verschiebung des Ankers anbietet.
  • XYAnchor - eine konkrete Implementierung, die eine feste Position für den Anker definiert, unabhängig von anderen draw2d-Objekten.
  • AbstractConnectionAnchor - abstrakte Basisklasse für alle Anker, deren Position von Eigenschaften einer Figur abhängig ist.
  • ChopboxAnchor - eine Implementierung von AbstractConnectionAnchor, die sich wie im obigen Beispiel verhält.
  • EllipseAnchor - auch eine Implementierung von AbstractConnectionAnchor, die ähnlich dem ChopboxAnchor ist. Der Unterschied ist die Art, wie der unsichtbare Teil der Linie festgelegt wird. Im Fall von EllipseAnchor ist es der der Ellipse, der in das Rechteck der Figur eingebettet ist, der die Grenze der Linie festlegt.



Wenn man das vorherige Beispiel auf folgende Weise modifiziert:

	Label label1 = new Label("Label 1");
	LineBorder borderL1 = new LineBorder();
	label1.setBounds(new Rectangle(10, 10, 100, 100) );
	label1.setBorder(borderL1);
	contents.add(label1);
 
	Label label2 = new Label("Label 2");
	label2.setBounds(new Rectangle(150, 150, 100, 100) );
	label2.setBorder(borderL1);
	contents.add(label2);
 
	PolylineConnection connection = new PolylineConnection();
	connection.setSourceAnchor(new EllipseAnchor(label1) );
	connection.setTargetAnchor(new EllipseAnchor(label2) );
	contents.add(connection);



sieht man, wir der EllipseAnchor funktioniert:

Dratva 1919 example4 draw2d.png



Die roten Kreise in diesem Bild sind nicht Teil der Figuren, sondern wurden auf dem Screenshot gezeichnet, um die Eingenschaften des EllipseAnchor darzustellen.

Im folgenden Beispiel wird eine freischwebende Linie mithilfe von XYAnchor gezeichnet:

	PolylineConnection connection = new PolylineConnection();
	connection.setSourceAnchor(new XYAnchor(new org.eclipse.draw2d.geometry.Point(10, 20) ) );
	connection.setTargetAnchor(new XYAnchor(new org.eclipse.draw2d.geometry.Point(100, 40) ) );
	contents.add(connection);



Dratva 1919 example5 draw2d.png

Routing

Wie man oben gesehen hat, zeichnet die PolylineConnection eine gerade Linie zwischen den von Anker festgelegten Punkten. Oft braucht man aber die Möglichkeit, die Linien anderer Formen zu zeichnen. In diesem Fall kann man eine neue Implementierung von Connection entwickeln. Es besteht aber eine einfachere Möglichkeit - das Connection-Objekt mit einem ConnectionRouter zu parametrieren. Das macht man mit der Methode Connection.setConnectionRouter. ConnectionRouter ist ein Interface in draw2d, vom dem es mehrere fertige Implementierungen gibt:

  • AbstractRouter - abstrakte Klasse mit einigen Hilfsfunktionen.
  • AutomaticRouter - abstrakte Klasse mit "Collision Detection" - Erkennung überlappender Verbindungen. Die Auflösung des Konflikts müssen die abgeleiteten Klassen übernehmen.
  • FanRouter - eine Ableitung von AutomaticRouter mit Konfliktauflösung. Die Konflikte werden so gelöst, dass mehrere Verbindungen zwischen zwei gleichen Punkten unterschiedlich geführt werden und eine Art Ventilatorschaufel bilden.
  • BendpointConnectionRouter - führt die Linie über die in einer Liste gegebenen Punkte.
  • ShortestPathConnectionRouter - finden den kürzesten Pfad, der alle Kinder einer gegebenen Figur meidet.
  • ManhattatConnectionRouter - erzeugt eine rechteckige Verbindung mit nur senkrechten und waagerechten Linien.


Hier ein Beispiel mit dem ManhattanConnectionRouter:

	Label label1 = new Label("Label 1");
	LineBorder borderL1 = new LineBorder();
	label1.setBounds(new Rectangle(10, 10, 100, 30) );
	label1.setBorder(borderL1);
	contents.add(label1);
 
	Label label2 = new Label("Label 2");
	label2.setBounds(new Rectangle(150, 150, 100, 30) );
	label2.setBorder(borderL1);
	contents.add(label2);
 
	ManhattanConnectionRouter router = new ManhattanConnectionRouter(); 
 
	PolylineConnection connection = new PolylineConnection();
	connection.setConnectionRouter(router);
	connection.setSourceAnchor(new ChopboxAnchor(label1) );
	connection.setTargetAnchor(new ChopboxAnchor(label2) );
	contents.add(connection);


Und der Output:
Dratva 1919 example6 draw2d.png

Decorations

Das letzte Beispiel wird jetzt modifiziert:

	Label label1 = new Label("Label 1");
	LineBorder borderL1 = new LineBorder();
	label1.setBounds(new Rectangle(10, 10, 100, 30) );
	label1.setBorder(borderL1);
	contents.add(label1);
 
	Label label2 = new Label("Label 2");
	label2.setBounds(new Rectangle(150, 150, 100, 30) );
	label2.setBorder(borderL1);
	contents.add(label2);
 
	ManhattanConnectionRouter router = new ManhattanConnectionRouter(); 
 
	PolylineConnection connection = new PolylineConnection();
	connection.setConnectionRouter(router);
	connection.setSourceAnchor(new ChopboxAnchor(label1) );
	connection.setTargetAnchor(new ChopboxAnchor(label2) );
 
	PolygonDecoration decoration = new PolygonDecoration();
	connection.setTargetDecoration(decoration);
 
	contents.add(connection);


Der Output von diesem Programm sieht so aus:
Dratva 1919 example7 draw2d.png

Wie man sieht, wurde hier ein Pfeil an einem Ende der Linie gezeichnet. Das wurde mit dem Aufruf von connection.setTargetDecoration erreicht. Es gibt eine entsprechende Methode für den Anfang der Linie, setSourceDecoration. Für das Decoration selbst wurde hier PolygonDecoration verwendet. In draw2d gibt es noch eine Decoration-Implementierung, PolylineDecoration. Und man darf natürlich eigene Decorations implementieren. Sie müssen das Interface RotatableDecoration implementieren.
Die Klassen PolygonDecoration und PolylineDecoration zeichnen standardmäßig einen Pfeil. Sie besitzen aber die Methode setTemplate, mit der man das Aussehen von diesem Decoration ändern kann. Das wird im nächsten Beispiel demonstriert. Hier wird auch das ManhattanRouting weggenommen, um das Rotieren des Templates deutlicher zu zeigen.


	Label label1 = new Label("Label 1");
	LineBorder borderL1 = new LineBorder();
	label1.setBounds(new Rectangle(10, 10, 100, 30) );
	label1.setBorder(borderL1);
	contents.add(label1);
 
	Label label2 = new Label("Label 2");
	label2.setBounds(new Rectangle(150, 150, 100, 30) );
	label2.setBorder(borderL1);
	contents.add(label2);
 
	//ManhattanConnectionRouter router = new ManhattanConnectionRouter(); 
 
	PolylineConnection connection = new PolylineConnection();
	//connection.setConnectionRouter(router);
	connection.setSourceAnchor(new ChopboxAnchor(label1) );
	connection.setTargetAnchor(new ChopboxAnchor(label2) );
 
	PointList template = new PointList();
	template.addPoint(0, 0);
	template.addPoint(-4, 2);
	template.addPoint(-2, 0);
	template.addPoint(-4, -2);
 
	PolygonDecoration decoration = new PolygonDecoration();
	decoration.setTemplate(template);
 
	connection.setTargetDecoration(decoration);
 
	contents.add(connection);


Dratva 1919 example8 draw2d.png
Man sieht hier deutlich, wie der Muster definiert wird, und wie die automatische Rotation funktioniert. Bei der Definition des Templates muss man sich vorstellen, dass die Verbindungslinie waagerecht ist und der Anfang des Koordinatensystems am Endpunkt der Linie sich befindet. Die negative X-Richtung ist dann die Richtung zum Zentrum der Linie. Beim Zeichnen wird der Muster um den Winkel rotiert, den die Verbindungslinie (oder ihr letzter Abschnitt im Fall einer Polylinie) zu der X-Achse bildet.

Anordnungen (Layouts)

Die Layouts legen fest, wie die Parent-Figur ihre Kinder platziert. Ein Layout ist eine Implementierung des Interfaces LayoutManager. Es gibt mehrere Standardimplementierungen in draw2d:

  • AbstractLayout - eine abstrakte Implementierung mit vielen Hilfsfunktionen
  • AbstractHintLayout - Basisklasse für die Layouts, die die bevorzugte Größe der Figuren berücksichtigen.
  • ToolbarLayout - die Kinder werden senkrecht oder waagerecht an einer Linie platziert.


In folgenden Beispielen werden die Figuren wie folgt erzeugt:

	Label label1 = new Label("Label 1");
	LineBorder borderL1 = new LineBorder();
	label1.setBounds(new Rectangle(10, 10, 100, 30) );
	label1.setBorder(borderL1);
	contents.add(label1);
 
	Label label2 = new Label("Label 2");
	label2.setBounds(new Rectangle(150, 150, 100, 30) );
	label2.setBorder(borderL1);
	contents.add(label2);
 
	Ellipse ellipse = new Ellipse();
	ellipse.setBackgroundColor(ColorConstants.blue);
	ellipse.setBounds(new Rectangle(0, 0, 20, 40) );
	ellipse.setOpaque(true);
	contents.add(ellipse);
 
	RectangleFigure rectangle = new RectangleFigure();
	rectangle.setBounds(new Rectangle(20, 40, 120, 30) );
	rectangle.setOpaque(true);
	rectangle.setBackgroundColor(ColorConstants.red);
	contents.add(rectangle);


ToolbarLayout