Archiv für

Juli, 2006

...

Installation von GNU ClasspathX unter Win32

kein kommentar

So, da ich jetzt wieder Internet hab, hab ich damit auch Zugriff auf meinen NNTP-Server und das CVS von GNU Classpath. Damit kann ich noch einmal den NNTP-Provider aus der GNU JavaMail Implementierung in Angriff nehmen.

Also hab ich mir erstmal mit Eclipse anonymen Zugriff auf das CVS verschafft (dazu sage ich nur: Lest aktuelle Dokumentation, nicht veralteten Mist!) und die folgenden Modul als eigenständige Projekte abgerufen:

  • mail
  • activation
  • crypto
  • inetlib

Die Reihenfolge der Module ist auch gleichzeitig die Reihenfolge, in der hinterher die Projekte kompiliert und die JARs erzeugt werden müssen. An dieser Stelle sollte ich wohl darauf hinweisen, dass die Ant-Skripte nicht offiziell unterstützt werden. Man sollte sich bei Beschwerden also lieber nicht an die Entwickler wenden…
Nachdem alle roten Kreuzchen verschwunden sind, kann das activation.jar erstellt werden. Dazu reicht in Eclipse das ausführen von activation/build.xml als Ant-Skript und schon erscheint das erste JAR im Verzeichnis von activation.

Anschliessend muss crypto übersetzt werden, da die inetlib aus diesem Modul einige Dinge benötigt. Ausserdem wird eine Implementierung der JSSE benötigt. Die freie Umsetzung Jessie eignet sich für die schnelle Kompilierung. Einfach ein weiteren Eclipse-Projekt einrichten und das Ant-Skript build.xml ausführen.

Die inetlib muss ausgetrickst werden: Das Standardziel von Ant erstellt bei diesem Modul kein JAR, dazu muss in der Outline des Ant-Skriptes build.xml aus inetlib das Ziel “inetlib.jar” ausgeführt werden. Dann erst wird ein JAR erstellt, dass anschliessend für die Kompilierung von mail verwendet werden kann.

Ok, das war’s für’s erste. Morgen geht es weiter. :-)

Mouse Handling mit JFreeChart

kein kommentar

Heute habe ich ein weiteres Testprogramm geschrieben, um das Mouse Handling von JFreeChart zu begreifen. Herausgekommen ist ein Programm, das verschiedene Funktionen enthält:

  • Auswahl des nächstmöglichen Datenpunktes unter der Mausposition
  • Anzeige des verfügbaren Datenpunktes
  • Verschieben von Datenpunkten mit der Maus
  • Verändern des Suchradius, wenn nach nächstmöglichen Datenpunkten gesucht wird

Herunterladen kann man das Testprogramm als ausführbare JAR-Datei von auf http://rapidshare.de/files/26403584/EntityTester_fat.jar.html. Wer den Quelltext haben möchte, schaut in meinem Wiki nach.

Drucken mit JFreeChart v0.9.11

kein kommentar

Es ist wieder soweit: Ich habe ein Problem beim Drucken mit JFreeChart v0.9.11. Nachdem ich ja schon einmal darüber geschrieben hatte, tritt jetzt ein neues Problem auf:

Ich möchte ein stinknormales ChartPanel drucken. Der Chart ist ein ScatterPlot, den ich mit den Factory-Methoden aus ChartFactory erzeugt habe. Es werden nur 50k Datenpunkte angezeigt. Nach dem Aufruf von ChartPanel#createChartPrintJob() vergeht nun geraume Zeit, in der keinerlei Reaktion des Programms erfolgt. Ausserdem wird ein 38MB großer Druckauftrag erzeugt. Wenn ich bedenke, dass ich eigentlich vorhabe, einen Chart mit 2,5M Datenpunkten zu drucken, wird mir jetzt schon schwindelig.

Gut, also muss ich das Drucken ein wenig aufmöbeln. Der einfachste Ansatz ist das Erstellen eines eigenen PrintJobs:

PrinterJob job = PrinterJob.getPrinterJob();
PageFormat format = job.defaultPage();
format.setOrientation(PageFormat.LANDSCAPE);
format = job.pageDialog(format);
job.setPrintable(cp, format);
if (job.printDialog()) {
try {
job.print();
} catch (PrinterException e) {}
}

Dadurch wird das Verhalten auch nicht anders, also verwendet das ChartPanel genau diesen Mechanismus zum Drucken – allerdings kann ich jetzt direkt Querformat vorgeben. Hilft aber immer noch nicht. Also erstmal in einen Thread auslagern:

Thread t = new Thread(new Runnable() {
public void run() {
// allein: sehr schlecht - lange, zu großer Speicherbedarf
PrinterJob job = PrinterJob.getPrinterJob();
PageFormat format = job.defaultPage();
format.setOrientation(PageFormat.LANDSCAPE);
format = job.pageDialog(format);
job.setPrintable(cp, format);
if (job.printDialog()) {
try {
job.print();
} catch (PrinterException e) {}
}
}
});
t.start();

Danach zeigt zumindest das GUI wieder eine Reaktion, nachdem der Einstellungsdialog für die Druckoptionen geschlossen wurde. Allerdings kann man nun nicht mehr erkennen, dass überhaupt etwas passiert, denn die Erstellung des PrintJob läuft ja im Hintergrund. Also auch noch keine Lösung. Tatsächlich scheint hier wieder nur eine eigenen Implementierung eines ChartPanels zu helfen, die die print()-Methode überschreibt und das ChartPanel auf geeignete Weise neu zeichnet.

Ich bin einen etwas anderen Weg gegangen und habe mir zuerst ein Bild des ChartPanels in einer fest eingestellten Auflösung gezeichnet und anschliessend dieses Bild ausgegeben. Dazu habe ich ein paar Methoden definiert. Als erstes eine Methode, die aus dem ChartPanel ein Bild in fester Auflösung macht:



// Kopiert aus CustomChartPanel#getHighResChartImage
private BufferedImage getHighResChartImage(ChartPanel chartpanel, int resolution) {
int screenResolution = Toolkit.getDefaultToolkit().getScreenResolution();
int scaleRatio = resolution / screenResolution;

// get width and height for image files
//int width = ResourceContainer.getInstance().getWidthForFiles();//getWidth();
//int height = ResourceContainer.getInstance().getHeightForFiles();//getHeight();
// width, height for resolution
int width = chartpanel.getWidth() * scaleRatio;
int height = chartpanel.getHeight() * scaleRatio;

BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

Graphics2D g2 = image.createGraphics();

g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);

/*
g2.transform(AffineTransform.getScaleInstance(scaleRatio,
scaleRatio));
*/
chartpanel.getChart().draw(g2, new Rectangle2D.Double(0,
0, width, height), null);
g2.dispose();

return image;
}

Jetzt fehlt noch eine druckbare Variante des BufferedImage, um das Bild zu Papier zu bringen:

class PrintImage implements Printable {
private BufferedImage myImage;

public PrintImage(BufferedImage image) {
myImage = image;
}

public int print(Graphics gr, PageFormat pageFormat, int pageIndex) {
if(pageIndex!=0) return NO_SUCH_PAGE;

Graphics2D g = (Graphics2D)gr;

double x = pageFormat.getImageableX();
double y = pageFormat.getImageableY();
double w = pageFormat.getImageableWidth();
double h = pageFormat.getImageableHeight();

g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);

double sx = w / myImage.getWidth();
double sy = h / myImage.getHeight();

g.translate(x,y);
g.scale(sx, sy);

gr.drawImage(myImage, 0, 0, Color.WHITE, null);

return PAGE_EXISTS;
}
}

Anschliessend wird beim Druck folgender Aufruf verwendet:

PrintImage pi = new PrintImage(getHighResChartImage(cp, 300));

PrinterJob job = PrinterJob.getPrinterJob();
PageFormat format = job.defaultPage();
format.setOrientation(PageFormat.LANDSCAPE);
format = job.pageDialog(format);
job.setPrintable(pi, format);
if (job.printDialog()) {
try {
job.print();
} catch (PrinterException e) {}
}

Es wird ein BufferedImage mit 300dpi erstellt, dass das ChartPanel cp entsprechend seiner Bildschirmdarstellung enthält. Dieses Bild ist deutlich schneller erstellt, als der PrintJob von Original-ChartPanel. Anschliessend wird das PrintImage gedruckt, was wiederum schneller beendet ist, als ein ChartPanel zu drucken. Nachteil dieser Methode ist aber, dass die Auflösung fest mit 300dpi angegeben wurde – wenn der Drucker es schafft, ok. Ansonsten wird skaliert, was wieder zu schlechten Ergebnissen führt…

Alles noch nicht sehr ausgereift, und ich hoffe, das aktuelle JFreeChart-Versionen dieses Manko behoben haben…