[Sample] Dynamic jfreechart for SWT demo

A discussion forum for JFreeChart (a 2D chart library for the Java platform).
Locked
silent
Posts: 50
Joined: Wed Mar 16, 2005 2:55 pm
Location: Korea
Contact:

[Sample] Dynamic jfreechart for SWT demo

Post by silent » Fri Mar 30, 2007 4:14 am

Hi.

I saw some questions for dynamic swt jfreechart recently.
Here is demo code. Try this.

But, it have some problems. Just now, I don't know why.
If I don't use ChartComposite.forceRedraw(), chart can't redraw itself.
And error occur after a short period of time.

Need to try more things...

Code: Select all

package demo.swt;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.Timer;

import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.time.Millisecond;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.experimental.chart.swt.ChartComposite;
import org.jfree.ui.RectangleInsets;

public class SWTMemoryUsageDemo {

	private static Display display;
	private static ChartComposite chartComposite;
	
	private TimeSeries total;
	private TimeSeries free;

	class DataGenerator extends Timer implements ActionListener {
		DataGenerator(int i) {
			super(i, null);
			addActionListener(this);
		}
		
		public void actionPerformed(ActionEvent actionevent) {
			// [SILENT] for SWT UI thread issue, use Display.asyncExec()
			display.asyncExec(new Runnable() {
				public void run() {
					long l = Runtime.getRuntime().freeMemory();
					long l1 = Runtime.getRuntime().totalMemory();
					addTotalObservation(l1);
					addFreeObservation(l);
					
					// [SILENT] for chart redraw, use ChartComposite.forceRedraw();
					//			if not use this, then chart will not redraw.
					//			I don't know why.
					chartComposite.forceRedraw();
	            }
			});
		}
	}
	
	public SWTMemoryUsageDemo(int i) {
		total = new TimeSeries("Total Memory", org.jfree.data.time.Millisecond.class);
		total.setMaximumItemAge(i);
		free = new TimeSeries("Free Memory", org.jfree.data.time.Millisecond.class);
		free.setMaximumItemAge(i);
	}
	
	public JFreeChart createChart() {
		TimeSeriesCollection timeseriescollection = new TimeSeriesCollection();
		timeseriescollection.addSeries(total);
		timeseriescollection.addSeries(free);
		
		DateAxis dateaxis = new DateAxis("Time");
		NumberAxis numberaxis = new NumberAxis("Memory");
		dateaxis.setTickLabelFont(new Font("SansSerif", 0, 12));
		numberaxis.setTickLabelFont(new Font("SansSerif", 0, 12));
		dateaxis.setLabelFont(new Font("SansSerif", 0, 14));
		numberaxis.setLabelFont(new Font("SansSerif", 0, 14));
		
		XYLineAndShapeRenderer xylineandshaperenderer = new XYLineAndShapeRenderer(true, false);
		xylineandshaperenderer.setSeriesPaint(0, Color.red);
		xylineandshaperenderer.setSeriesPaint(1, Color.green);
		xylineandshaperenderer.setStroke(new BasicStroke(3F, 0, 2));
		
		XYPlot xyplot = new XYPlot(timeseriescollection, dateaxis, numberaxis, xylineandshaperenderer);
		xyplot.setBackgroundPaint(Color.lightGray);
		xyplot.setDomainGridlinePaint(Color.white);
		xyplot.setRangeGridlinePaint(Color.white);
		xyplot.setAxisOffset(new RectangleInsets(5D, 5D, 5D, 5D));
		
		dateaxis.setAutoRange(true);
		dateaxis.setLowerMargin(0.0D);
		dateaxis.setUpperMargin(0.0D);
		dateaxis.setTickLabelsVisible(true);
		numberaxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
		
		JFreeChart jfreechart = new JFreeChart("JVM Memory Usage", new Font("SansSerif", 1, 24), xyplot, true);
		jfreechart.setBackgroundPaint(Color.white);
		
		return jfreechart;
	}

	private void addTotalObservation(final double d) {
		total.addOrUpdate(new Millisecond(), d);
	}

	private void addFreeObservation(final double d) {
		free.addOrUpdate(new Millisecond(), d);
	}

	public static void main(String args[]) {
		display = new Display();
		Shell shell = new Shell(display);
		shell.setSize(600, 300);
		shell.setLayout(new FillLayout());
		shell.setText("Memory Usage Demo");

		SWTMemoryUsageDemo memoryusagedemo = new SWTMemoryUsageDemo(30000);
		JFreeChart chart = memoryusagedemo.createChart();

		chartComposite = new ChartComposite(shell, SWT.NONE, chart, true);
		chartComposite.setDisplayToolTips(true);
		chartComposite.setHorizontalAxisTrace(false);
		chartComposite.setVerticalAxisTrace(false);
		shell.open();
		
		(memoryusagedemo.new DataGenerator(100)).start();
		
		while (!shell.isDisposed()) {
			if (!display.readAndDispatch())
				display.sleep();
		}
	}
}

Code: Select all

org.eclipse.swt.SWTError: No more handles
	at org.eclipse.swt.SWT.error(SWT.java:3400)
	at org.eclipse.swt.SWT.error(SWT.java:3297)
	at org.eclipse.swt.SWT.error(SWT.java:3268)
	at org.eclipse.swt.widgets.Display.internal_new_GC(Display.java:2165)
	at org.eclipse.swt.graphics.Device.computePixels(Device.java:201)
	at org.eclipse.swt.graphics.Font.init(Font.java:210)
	at org.eclipse.swt.graphics.Font.<init>(Font.java:72)
	at org.jfree.experimental.swt.SWTUtils.toSwtFontData(SWTUtils.java:131)
	at org.jfree.experimental.swt.SWTGraphics2D.setFont(SWTGraphics2D.java:647)
	at org.jfree.chart.axis.ValueAxis.drawTickMarksAndLabels(ValueAxis.java:687)
	at org.jfree.chart.axis.NumberAxis.draw(NumberAxis.java:667)
	at org.jfree.chart.plot.XYPlot.drawAxes(XYPlot.java:3019)
	at org.jfree.chart.plot.XYPlot.draw(XYPlot.java:2531)
	at org.jfree.chart.JFreeChart.draw(JFreeChart.java:1175)
	at org.jfree.experimental.chart.swt.ChartComposite$1.paintControl(ChartComposite.java:558)
	at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:83)
	at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:66)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:928)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:952)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:937)
	at org.eclipse.swt.widgets.Widget.notifyListeners(Widget.java:709)
	at org.jfree.experimental.chart.swt.ChartComposite.forceRedraw(ChartComposite.java:1104)
	at demo.swt.SWTMemoryUsageDemo$1.run(SWTMemoryUsageDemo.java:56)
	at org.eclipse.swt.widgets.RunnableLock.run(RunnableLock.java:35)
	at org.eclipse.swt.widgets.Synchronizer.runAsyncMessages(Synchronizer.java:123)
	at org.eclipse.swt.widgets.Display.runAsyncMessages(Display.java:3325)
	at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:2971)
	at demo.swt.SWTMemoryUsageDemo.main(SWTMemoryUsageDemo.java:131)
Tokdo belongs to Korea.

Not a 'sea of japan', But a 'East Sea(Dong hae)'

beckchr
Posts: 9
Joined: Wed Feb 28, 2007 2:57 pm

Post by beckchr » Mon Apr 02, 2007 11:03 pm

On the "out of handles":
I have the same problem. I guess that the jfreechart swt classes
create SWT fonts from AWT fonts on the fly without disposing them
later. The fonts should be cached in a font registry instead.
Someone should investigate this since it's a real blocker...

Did you create a bug report on this?

silent
Posts: 50
Joined: Wed Mar 16, 2005 2:55 pm
Location: Korea
Contact:

Post by silent » Fri Apr 06, 2007 7:12 am

I've changed some methods in SWTGraphics2D.java that I thought needed dispose() call.
And I tested sample JVM Memory Usage chart again.
For testing I can't see any error.

Please, try this and test. What happend to you??

And how can I connect to Henry?

Code: Select all

    public AffineTransform getTransform() {
        Transform swtTransform = new Transform(gc.getDevice()); 
        gc.getTransform(swtTransform);
        // TODO silent
        AffineTransform awtTransform = toAwtTransform(swtTransform);
        swtTransform.dispose();
        return awtTransform;
    }

Code: Select all

    public void setBackground(Color color) {
        //TODO do we need this? gc.getBackground().dispose();
        //TODO silent
        org.eclipse.swt.graphics.Color swtColor = SWTUtils.toSwtColor(gc.getDevice(), color);
        gc.setBackground(swtColor);
        swtColor.dispose();
    }

Code: Select all

    public void setColor(Color color) {
        //TODO do we need this? gc.getForeground().dispose();
        //TODO silent
        org.eclipse.swt.graphics.Color swtColor = SWTUtils.toSwtColor( gc.getDevice(), color);
        gc.setForeground(swtColor);
        swtColor.dispose();
    }

Code: Select all

    public void setFont(Font font) {
        //TODO do we need this? gc.getFont().dispose();
        org.eclipse.swt.graphics.Font swtFont 
            = new org.eclipse.swt.graphics.Font( 
                    gc.getDevice(), 
                    SWTUtils.toSwtFontData(gc.getDevice(), font, true));
        gc.setFont( swtFont );
        //TODO silent
        swtFont.dispose();
    }
Tokdo belongs to Korea.

Not a 'sea of japan', But a 'East Sea(Dong hae)'

heprom
Posts: 91
Joined: Sat May 27, 2006 4:25 am
Location: Paris

Post by heprom » Sat Apr 07, 2007 1:38 pm

Hi,

silent, I've patched the SWTGraphics2D with some of your changes, thanks.
not in the setFont() method though cause it crashed the java VM for me.

I've also fixed the redraw problem, the redraw event were sent to the Control and not the canvas, and for some reasons, this were not triggering a redraw of the canvas.

beckchr, I'd be happy to discuss font caching with you. Consider sending an note to the developper mailing list.

Btw, sorry about the delay, I was (and still am) crazy with work these days, and not browsing the forum. I follow carefully the dev mailing list, please consider reporting bugs when you spot problems like this, so I'll get notified.

Regards,
Henry

beckchr
Posts: 9
Joined: Wed Feb 28, 2007 2:57 pm

Post by beckchr » Sat Apr 07, 2007 8:28 pm

Hi,

I've just looked at the changes you've made in SWTGraphics2D.
I think the changes should be rolled back.
(1) after we called dispose() a resource cannot be used anymore
(2) we should call dispose() only once on a resource
(3) we should call dispose only on resources that we created
I have the strong feeling that the new code may violate all of the above.

On the font problem: I haven't used jface FontRegistry yet, so this was
just an idea. However, I (dirty) fixed the font problem by introducing
a HashMap which caches swt fonts by <family>-<style>-<size>:

Code: Select all

    /** cache swt fonts by family-style-size to dispose correctly **/
    private HashMap swtFonts = new HashMap();

    public void setFont(Font font) {
    	// TODO added cbe
    	String key = font.getFamily() + "-" + font.getStyle() + "-" + font.getSize();
    	org.eclipse.swt.graphics.Font swtFont = swtFonts.get(key);
    	if (swtFont == null || swtFont.isDisposed()) {
    		swtFont = new org.eclipse.swt.graphics.Font( 
                    gc.getDevice(), 
                    SWTUtils.toSwtFontData(gc.getDevice(), font, true));
    		swtFonts.put(key, swtFont);
    	}
        gc.setFont( swtFont );
    }

    public void dispose() {
        gc.dispose();
        Iterator iterator = swtFonts.values().iterator();
        while (iterator.hasNext()) {
          ((org.eclipse.swt.graphics.Font)iterator.next()).dispose();
        }
    }
After doing this, I got no more "Out of handles" exceptions.
However, the code assumes that all the awt fonts passing through
the api methods have a family, style and size. Is this always true?

Next time in the developer forum.[/list]

heprom
Posts: 91
Joined: Sat May 27, 2006 4:25 am
Location: Paris

Post by heprom » Mon Apr 09, 2007 3:10 pm

discussion continues on the developper's list...

rspan
Posts: 15
Joined: Wed May 02, 2007 4:36 pm

Post by rspan » Wed May 09, 2007 10:17 pm

Silent,

Do you have the solution on the problem you got?

Thanks,
Han

Locked