Multiple charts on multiple shapes using jFreeChart

I am trying to use jFreechart to generate two digits, each of which contains 12 graphs (which are called series in jFreeChart). However, some graphs are simply missing! I know that I have a synchronization problem and tried to use the method that @trashgod user provided to me here , however I failed. I know how I use swingworker incorrectly ! I do not know how to fix this.

Each figure should contain 10 graphs that are parallel horizontal lines. As you can see from the attached image, some lines are missing. These two numbers should also be the same (which is not the case). In practice, I will have to create several graphs in several places of my applications at different times (random time interval between each figure and even graphs of individual digits) Any help would be greatly 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(); } } }); } } 
+6
source share
2 answers

I know how I use swingworker, this is wrong! I do not know how to fix this.

Before starting, I have a few tips:

  • Get rid of arrays: you have several of them, and you will see that they only mess things up, because you will need indexes and loops to work with them everywhere, and it's too easy to make a mistake. I would especially remove this:

    private XYSeries [] xySeries; //XYSeriesCollection is intended to keep a series list, so...

  • Do not extend the class from JFrame (or any Swing component) unless you add any functions. You can use a variable instead.

  • In addition, the need to fix SwingWorker should be fixed; more worrying is the presence of a new Thread that calls this SwingWorker . Get rid of him (not necessary).

  • As @trahsgod noted in this comment , XYSeriesCollection is a chart model, so the key works with it.

Having said that, about your implementation of SwingWorker , 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 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, which you can take as a starting point. Hope this will 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(); } }); } } 
+4
source

You do 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 this — the Swing timer should only start in the Swing (or EDT) event dispatch thread. Have you tried using SwingWorker? If so, what was your result?

And you seem to use the Swing timer with a delay of 0 and stop it immediately. If so, this is a little strange and suggests that you should not use a timer at all.

+1
source

Source: https://habr.com/ru/post/958438/


All Articles