Finests Java beans with Inkscape, SVGs and Batik
Par obi, Wednesday 27 October 2004 à 10:53 :: Work :: #42 :: rss
Several monthes ago, I spent a few days building a PFD java bean, using the nice Java 2D API. Meanwhile, I was starting to play with inkscape and SVG files. And found many common things.
So when I was asked for a FCU java bean, I started to search how to handle SVG in java and found Batik, which is an SVG toolkit made by the Apache foundation. The principle is quite simple: draw the interface with Inkscape, then group the elements according to the desired behaviour, eg right knob, left knob, auto-pilot button, etc. Then fire up the XML editor and set a unique ID for each group.
The next steps requires your favourite java editor or IDE. I used netbeans, but anything allowing you to type java code is OK. Use it to write a simple class able to load and display a SVG file, using the JSVGCanvas class provided by batik, or by derivation, or by using a class member
public class FCUCanvas extends JSVGCanvas {
...
private SVGDocument doc=null;
//handles on interactive SVG elements
private Element altBtn=null;
private Element spdBtn=null;
private Element altValue=null;
private Element spdValue=null;
private Element ap1Btn=null;
private Element ap1Light=null;
private Element spdLight=null;
private Element altLight=null;
private PropertyChangeSupport propertySupport=null;
/** Creates a new instance of FCUCanvas */
public FCUCanvas() {
setPreferredSize(new Dimension(500, 300));
propertySupport = new PropertyChangeSupport( this );
setDocumentState(JSVGComponent.ALWAYS_DYNAMIC);
try {
String parser = XMLResourceDescriptor.getXMLParserClassName();
SAXSVGDocumentFactory fact = new SAXSVGDocumentFactory(parser);
URL thisClassURL = ClassLoader.getSystemResource("fcd/fcu.svg");
doc = (SVGDocument)(fact.createDocument(thisClassURL.toString()));
setDocument(doc);
} catch (IOException ex) {
ex.printStackTrace();
}
//inint graphics when gvt tree is rendered
addGVTTreeRendererListener(new GVTTreeRendererAdapter() {
public void gvtRenderingCompleted(GVTTreeRendererEvent e){
//retreive SVG elements by their IDs
altBtn=doc.getElementById("altbtn");
spdBtn=doc.getElementById("spdbtn");
altValue=doc.getElementById("altvalue");
spdValue=doc.getElementById("spdvalue");
ap1Btn=doc.getElementById("ap1btn");
ap1Light=doc.getElementById("ap1light");
spdLight=doc.getElementById("spdlight");
altLight=doc.getElementById("altlight");
...
}
...
}
Once the SVG file is loaded, two things have to be done:
- In order to handle mouse inputs, we must attach event liteners to relevant SVG groups. This is to be added in the
gvtRenderingCompletedmethod:
Notre that we are using here the W3C DOM event model and not the JAVA2D one. The advantage is that we can attach the event listener directly to an SVG Element. The disadvantage is that this event model does not handle wheel mouse.((EventTarget)altBtn).addEventListener("mousedown", new EventListener(){ public void handleEvent(org.w3c.dom.events.Event evt) { DOMMouseEvent q = (DOMMouseEvent)evt; short button = q.getButton(); if (button == MOUSE_LEFT) incrAlt(400); if (button == MOUSE_RIGHT) incrAlt(-400); } }, false); ((EventTarget)spdBtn).addEventListener("mousedown", new EventListener(){ public void handleEvent(org.w3c.dom.events.Event evt) { DOMMouseEvent q = (DOMMouseEvent)evt; short button = q.getButton(); if (button == MOUSE_LEFT) incrSpd(40); if (button == MOUSE_RIGHT) incrSpd(-40); } }, false); ((EventTarget)ap1Btn).addEventListener("mousedown", new EventListener(){ public void handleEvent(org.w3c.dom.events.Event evt) { toggleAP1(); } }, false); - Write the methods able to change what is displayed. As inputs can come from two differents threads (from the graphical thread on mouse inputs, or from the java bean thread), we have to make
Runnableclasses to modify the display.Note that you must use theprivate void schedule(Runnable r) {getUpdateManager().getUpdateRunnableQueue().invokeLater(r);} private class SetVisible implements Runnable { private GraphicsNode _n; private boolean _b; public SetVisible(GraphicsNode n, boolean b) {_n = n; _b = b;} public void run() { _n.setVisible(_b); } } private class SetText implements Runnable { private Element _e; private String _s; public SetText(Element span, String s) {_e = span; _s = s;} public void run() { _e.getFirstChild().setNodeValue( _s); } } private class Transform implements Runnable { private GraphicsNode _n; private AffineTransform _t; public Transform(GraphicsNode node, AffineTransform transform) {_n = node; _t=transform;} public void run() { _n.setTransform(_t); } }Transformclass directly on graphics nodes, this way:private void translate(Element e, double dX, double dY){ if (e == null) return; GraphicsNode graphicsNode = bridgeContext.getGraphicsNode(e); AffineTransform t = graphicsNode.getTransform(); if (t==null) t=new AffineTransform(); t.translate(dX, dY); schedule(new Transform(graphicsNode, t)); } private void rotate(Element e, double angle, Element center){ if (e == null) return; GraphicsNode graphicsNode = bridgeContext.getGraphicsNode(e); Element c = (center == null) ? e : center; Rectangle2D centerBounds = bridgeContext.getGraphicsNode(c).getBounds(); if (centerBounds == null) return; AffineTransform t = graphicsNode.getTransform(); if (t==null) t=new AffineTransform(); t.rotate(angle, centerBounds.getCenterX(), centerBounds.getCenterY()); schedule(new Transform(graphicsNode, t)); }
OK, we are nearly finished. I remember having mentioned that we were making a java bean, so we miss the set/get methods, at your convenience. Here are a couple, the other one are rigourosly identical:
public int getSpeedCmd(){return speed;}
public void setSpeedCmd(int sp){
propertySupport.firePropertyChange("speedCmd", speed, sp);
speed = sp;
setValue(spdValue, sp);
}
public boolean getSpeedLight() {return speedLightOn;}
public void setSpeedLight(boolean b){
propertySupport.firePropertyChange("speedLight", speedLightOn, b);
speedLightOn = b;
setVisible(spdLight, b);
}

So what's next ? I have been thinking about putting all the behaviour in the SVG, using javascript and/or with behavioral selectors, eg adding a class="2 state button". This way, we could have a unique java class, working with any SVG, able to autogenerate methods using the ID and the class of the SVG elements. Making a java bean would be really easy this way ...
Commentaires
Le Friday 5 November 2004 à 09:39, par Dennis :: #
Le Friday 5 November 2004 à 10:21, par obi :: #
Ajouter un commentaire
Les commentaires pour ce billet sont fermés.