I am trying to use jFreechart to generate two figures each of which with 12 graphs (being referred as series in jFreeChart ). However some of the graphs get simply skipped! I know I have synchronization issue here and tried to used the method the user @trashgod provided me here however I failed. I know the way I use swingworker is wrong! I dont know how to fix it
Each figure should contain 10 graphs which are parallel horizontal straight lines. As you see in the attached image some of the lines are missing. The two figures have to be identical too ( which are not). In practice I will have to generate multiple graphs in several locations of my applications at various times(random time interval between each figure and even graphs of individual figures) Any help will be very much appreciated
Exception in thread "AWT-EventQueue-0" java.lang.IllegalArgumentException: This dataset already contains a series with the key Plot 11
at org.jfree.data.xy.XYSeriesCollection.addSeries(XYSeriesCollection.java:154)
at swing.FastChart2$MySwingWorker.process(FastChart2.java:192)
at javax.swing.SwingWorker$3.run(SwingWorker.java:414)
at sun.swing.AccumulativeRunnable.run(AccumulativeRunnable.java:112)
at javax.swing.SwingWorker$DoSubmitAccumulativeRunnable.run(SwingWorker.java:832)
at sun.swing.AccumulativeRunnable.run(AccumulativeRunnable.java:112)
at javax.swing.SwingWorker$DoSubmitAccumulativeRunnable.actionPerformed(SwingWorker.java:842)
at javax.swing.Timer.fireActionPerformed(Timer.java:312)
at javax.swing.Timer$DoPostEvent.run(Timer.java:244)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:251)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:733)
at java.awt.EventQueue.access$200(EventQueue.java:103)
at java.awt.EventQueue$3.run(EventQueue.java:694)
at java.awt.EventQueue$3.run(EventQueue.java:692)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:703)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:242)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:161)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:150)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:146)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:138)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:91)
package swing;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.Ellipse2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.IOException;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
import org.jfree.chart.*;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.util.ShapeUtilities;
public class FastChart2 extends JFrame {
private XYSeries [] xySeries ;
private XYPlot xyPlot;
private XYSeriesCollection xySeriesCollection;
private String title;
private static int instanceNum=0;
private int figNum=0;
private ChartPanel chartPanel;
public FastChart2(String s) {
super(s);
figNum = instanceNum;
instanceNum++;
init(s);
}
private void init(String s){
title = s;
xySeries = new XYSeries[12];
for (int i = 0; i < xySeries.length; i++) {
xySeries[i] = new XYSeries("Plot "+i);
}
xySeriesCollection = new XYSeriesCollection();
JFreeChart chart = ChartFactory.createScatterPlot(
title, "X", "Y", xySeriesCollection,
PlotOrientation.VERTICAL, true, true, false);
xyPlot = chart.getXYPlot();
xyPlot.setDomainCrosshairVisible(true);
xyPlot.setRangeCrosshairVisible(true);
chartPanel = createChartPanel(chart);
add(chartPanel, BorderLayout.CENTER);
JPanel control = new JPanel();
add(control, BorderLayout.SOUTH);
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
pack();
setLocationRelativeTo(null);
setVisible(true);
}
private ChartPanel createChartPanel(JFreeChart chart) {
XYItemRenderer renderer = xyPlot.getRenderer();
renderer.setSeriesPaint(0, Color.magenta);
renderer.setSeriesPaint(1, Color.green);
renderer.setSeriesPaint(2, Color.blue);
renderer.setSeriesPaint(4, Color.black);
renderer.setSeriesPaint(3, Color.yellow);
Shape cross = ShapeUtilities.createDiagonalCross(3, 0);
Shape plus = ShapeUtilities.createRegularCross(4,0);
for (int i = 0; i <=3; i++) {
renderer.setSeriesShape(0+i, new Rectangle(-1, -1, 2, 2));
renderer.setSeriesShape(4+i, new Ellipse2D.Float(-2F, -2F, 5F, 5F));
renderer.setSeriesShape(8+i, cross);
}
NumberAxis domain = (NumberAxis) xyPlot.getDomainAxis();
domain.setRange(0,1000);
NumberAxis range = (NumberAxis) xyPlot.getRangeAxis();
range.setRange(0,1200);
return new ChartPanel(chart);
}
public void multiPlot(){
Thread thread = null;
thread = new Thread (){
public void run() {
final double [] x = new double[1000];
final double [] y = new double[1000];
try{
for (int k = 0; k < 12; k++) {
for (int i = 0; i < y.length; i++) {
x[i] = i;
y[i] = k*100;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
plot2d(k % 12, x, y," Fig:"+figNum+" Seri:"+k);
}
} catch (Exception e){
System.out.println();
}
}
};
thread.start();
}
public synchronized void plot2d( final int iSeriesN, final double [] dX, final double [] dY, final String sT){
if (dY.length != dX.length){
throw new IllegalArgumentException("Error! inputs x and y have to be of same size.");
}
MySwingWorker mySwingWorker = new MySwingWorker( iSeriesN, dX, dY, sT);
mySwingWorker
.addPropertyChangeListener(new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent pcEvt) {
if (pcEvt.getNewValue() == SwingWorker.StateValue.DONE) {
System.out.println("done");
}
if ("progress".equals(pcEvt.getPropertyName())) {
System.out.println("progress");
}
}
});
mySwingWorker.execute();
}
private class MySwingWorker extends SwingWorker<Void, Double> {
private double [] dX ;
private double [] dY ;
private String title;
private int iSeriesN;
private MySwingWorker(int iSeriesN, double [] ix, double[] iy, String st){
dX = ix.clone();
dY = iy.clone();
title= st;
this.iSeriesN = iSeriesN;
xySeriesCollection.removeAllSeries();
System.out.println("xySeriesCollection.removeAllSeries();");
}
@Override
public Void doInBackground() throws IOException {
// chartPanel.getChart().removeChangeListener((ChartChangeListener) chartPanel);
xySeries[iSeriesN].clear();
for (int i = 0; i < dX.length; i++) {
xySeries[iSeriesN].add(dX[i], dY[i]);
}
for (int i = 0; i < xySeries.length; i++) {
setProgress(i * (100 / xySeries.length));
publish(Double.valueOf(i));
try {
Thread.sleep(10);
} catch (InterruptedException e) {
} // simulate latency
}
return null;
}
@Override
protected void process(List<Double> chunks) {
for (double d : chunks) {
xySeriesCollection.addSeries(xySeries[(int) d]);
}
}
@Override
protected void done() {
try {
// chartPanel.getChart().addChangeListener((ChartChangeListener) chartPanel);
xySeries[iSeriesN].setKey(title);
} catch (Exception ignore) {
}
}
}
public XYSeries addXY(final int iSeriesN, final double [] dX, final double [] dY){
XYSeries series = new XYSeries("Plot ");
for (int i = 0; i < dX.length; i++) {
series.add(dX[i], dY[i]);
}
return series;
}
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
FastChart2 [] demo = new FastChart2[2];
for (int i = 0; i < demo.length; i++) {
demo[i] = new FastChart2("Figure "+i);
demo[i].multiPlot();
}
}
});
}
}
I know the way I use swingworker is wrong! I dont know how to fix it
Before start I have some tips:
Get rid of the arrays: you have several of them and you'll see they only mess the things up because you'll need indexes and loops everywhere to work with them and it's too easy make a mistake. I'd especially remove this one:
private XYSeries [] xySeries; //XYSeriesCollection is intended to keep a series list, so...
Don't make your class extend from JFrame
(or any Swing component) if you don't will add any functionality. You just can use a variable instead.
Besides the SwingWorker
implementation needs to be fixed, it's more disturbing having a new Thread
that calls this SwingWorker
. Get rid of it too (it's no needed).
As @trahsgod pointed out in this comment, XYSeriesCollection
is the chart's model so the key is working with it.
Having said this, about your SwingWorker
implementation, it should look like this:
SwingWorker<Void, XYSeries> worker = new SwingWorker<Void, XYSeries>() {
@Override
protected Void doInBackground() throws Exception {
/*
* This part is extracted from your multiPlot() method
* I've just reduced the scale factor and get rid of double arrays
*/
int numberOfElements = 100; // this is the number of elementes in X axis
for(int y = 0; y < 12; y++) { // we want 12 series
XYSeries series = new XYSeries("Plot " + y);
for (int x = 0; x < numberOfElements; x++) {
series.add(x, y*10); //add x,y point
}
publish(series);
Thread.sleep(100);// just for animation purpose
}
return null;
}
@Override
protected void process(List<XYSeries> chunks) {
for(XYSeries series : chunks){
/*
* Add the series to the "model" here.
* It will notify the "view" data has been changed and this last one will be updated
* It's important make this call here to ensure the "view" is updated in the EDT.
*/
xySeriesCollection.addSeries(series);
}
}
};
Here is a complete working example based on your work that you can take as start point. Hope it be helpful :)
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import org.jfree.chart.*;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.util.ShapeUtilities;
public class FreeChartDemo {
XYSeriesCollection xySeriesCollection;
String title;
public FreeChartDemo(String title){
this.title = title;
}
public void initGUI(){
JButton clearChart = new JButton("Clear chart");
clearChart.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
xySeriesCollection.removeAllSeries();
}
});
JButton fillChart = new JButton("Fill chart") ;
fillChart.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
xySeriesCollection.removeAllSeries();
fillChart();
}
});
JPanel controlPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
controlPanel.add(clearChart);
controlPanel.add(fillChart);
JPanel content = new JPanel(new BorderLayout(5, 5));
content.add(getFreeChartPanel(), BorderLayout.CENTER); //add the ChartPanel here
content.add(controlPanel, BorderLayout.SOUTH);
JFrame frame = new JFrame("JFreeChart demo");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(content);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private JPanel getFreeChartPanel(){
xySeriesCollection = new XYSeriesCollection();
JFreeChart chart = ChartFactory.createScatterPlot(title, "X axis", "Y axis", xySeriesCollection,
PlotOrientation.VERTICAL, true, true, false);
XYPlot plot = chart.getXYPlot();
plot.setDomainCrosshairVisible(true);
plot.setRangeCrosshairVisible(true);
XYItemRenderer renderer = plot.getRenderer();
renderer.setSeriesPaint(0, Color.magenta);
renderer.setSeriesPaint(1, Color.green);
renderer.setSeriesPaint(2, Color.blue);
renderer.setSeriesPaint(4, Color.black);
renderer.setSeriesPaint(3, Color.yellow);
Shape cross = ShapeUtilities.createDiagonalCross(3, 0);
for (int i = 0; i <= 3; i++) {
renderer.setSeriesShape(0+i, new Rectangle(-1, -1, 2, 2));
renderer.setSeriesShape(4+i, new Ellipse2D.Float(-2F, -2F, 5F, 5F));
renderer.setSeriesShape(8+i, cross);
}
NumberAxis domain = (NumberAxis) plot.getDomainAxis();
domain.setRange(0,100);
NumberAxis range = (NumberAxis) plot.getRangeAxis();
range.setRange(0,120);
return new ChartPanel(chart);
}
private void fillChart() {
SwingWorker<Void, XYSeries> worker = new SwingWorker<Void, XYSeries>() {
@Override
protected Void doInBackground() throws Exception {
int numberOfElements = 1000;
for(int y = 0; y < 12; y++) {
XYSeries series = new XYSeries("Plot " + y);
for (int x = 0; x < numberOfElements; x++) {
series.add(x, y*10); //add x,y point
}
publish(series);
Thread.sleep(100);// just for animation purpose
}
return null;
}
@Override
protected void process(List<XYSeries> chunks) {
for(XYSeries series : chunks){
xySeriesCollection.addSeries(series);
}
}
};
worker.execute();
}
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new FreeChartDemo("JFreeChart #1").initGUI();
new FreeChartDemo("JFreeChart #2").initGUI();
}
});
}
}
You're doing the same thing -- calling Swing from a background thread.
Here you create a new thread in multiPlot, and then call the Swing Timer from that thread -- don't do that -- a Swing Timer should only be started on the Swing event dispatch thread (or EDT). Have you tried using a SwingWorker instead? If so, what has been your result?
And you appear to be using a Swing Timer with a delay of 0 and then stopping it immediately. If so, that's a bit odd, and suggests that you shouldn't be using a timer at all.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With