Più grafici in più figure usando jFreeChart

Sto provando ad usare jFreechart per generare due figure ciascuna delle quali con 12 grafici (di cui si fa riferimento come serie in jFreeChart). Tuttavia alcuni dei grafici vengono semplicemente saltati! So che ho un problema di sincronizzazione qui e ho provato ad usare il metodo che l’utente @trashgod mi ha fornito qui, ma ho fallito. So che il modo in cui utilizzo lo swingworker è sbagliato ! Non so come aggiustarlo

Ogni figura dovrebbe contenere 10 grafici che sono rette orizzontali parallele. Come vedi nell’immagine allegata mancano alcune linee. Anche le due figure devono essere identiche (che non lo sono). In pratica dovrò generare più grafici in diverse posizioni delle mie applicazioni in vari momentjs (intervallo di tempo casuale tra ogni figura e anche grafici di singole figure) Qualsiasi aiuto sarà molto apprezzato

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.actionPerformsd(SwingWorker.java:842) at javax.swing.Timer.fireActionPerformsd(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) 

inserisci la descrizione dell'immagine qui

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

So che il modo in cui utilizzo lo swingworker è sbagliato! Non so come aggiustarlo

Prima di iniziare ho alcuni suggerimenti:

  • Sbarazzati degli array: ne hai diversi e vedrai che confondono solo le cose perché avrai bisogno di indici e loop ovunque per lavorare con loro ed è troppo facile commettere un errore. Lo rimuoverei soprattutto questo:

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

  • Non estendere la class da JFrame (o qualsiasi componente Swing) se non si aggiungeranno funzionalità. Puoi solo usare una variabile, invece.

  • Inoltre, l’implementazione di SwingWorker deve essere risolta, è più fastidioso avere una nuova Thread che chiama questo SwingWorker . Sbarazzati anche di questo (non è necessario).

  • Come sottolineato da @trahsgod in questo commento , XYSeriesCollection è il modello del grafico in modo che la chiave funzioni con esso.

Detto questo, sulla tua implementazione SwingWorker , dovrebbe assomigliare a questo:

 SwingWorker worker = new SwingWorker() { @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 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); } } }; 

Esempio di lavoro

Ecco un esempio di lavoro completo basato sul tuo lavoro che puoi prendere come punto di partenza. Spero sia utile 🙂

 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 actionPerformsd(ActionEvent e) { xySeriesCollection.removeAllSeries(); } }); JButton fillChart = new JButton("Fill chart") ; fillChart.addActionListener(new ActionListener() { @Override public void actionPerformsd(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 worker = new SwingWorker() { @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 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(); } }); } } 

Stai facendo la stessa cosa – chiamando Swing da un thread in background.

Qui crei un nuovo thread in multiPlot, quindi chiami il Timer Swing da quel thread – non farlo – un timer Swing dovrebbe essere avviato solo sul thread di invio dell’evento Swing (o EDT). Hai provato a usare SwingWorker? Se sì, quale è stato il tuo risultato?

E sembra che tu stia usando uno Swing Timer con un ritardo di 0 e poi lo fermi immediatamente. Se è così, è un po ‘strano, e suggerisce che non dovresti usare affatto un timer.