Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple graphs in multiple figures using jFreeChart

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) 

enter image description here

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();

            } 

         }
      });
   }
}
like image 701
C graphics Avatar asked Nov 20 '13 02:11

C graphics


2 Answers

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);
       }
   }
};

Working example

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();
         }
      });
   }
}
like image 191
dic19 Avatar answered Oct 31 '22 05:10

dic19


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.

like image 29
Hovercraft Full Of Eels Avatar answered Oct 31 '22 05:10

Hovercraft Full Of Eels