2017-07-09 4 views
0

Ceci est ma première publication sur StackOverFlow alors s'il vous plaît excuser les erreurs dans mon message. Venir au sujet J'ai un programme qui est censé tracer certaines coordonnées X et Y. J'utilise actuellement JFREECHART pour XYChart. Afin de répondre aux exigences de mise à jour à haute vitesse, j'ai 2 séries. Series1 accumule 1000 points de données, puis est ajouté au graphique pour l'afficher.Après cette série2 accumaltes 1000 points de données et une fois qu'il est finis efface series1 (pour le préparer pour 1000 nouveaux points de données) et attache série2 au graphique (qui a maintenant 1000 points de données) ... et ce cycle continue. Le problème est lié aux fonctions XYSeries.clear et XYSeries.add. Apparemment, ils créent leurs propres threads et avant qu'ils puissent terminer l'exécution, je génère déjà un nouveau thread depuis mon application est très rapide. Je pense que cela provoque une erreur indexoutobounds. J'ai été capable de deviner cela avec: http://www.jfree.org/phpBB2/viewtopic.php?p=68111 Erreur similaire. La solution semble être: Swing Thread Safe ProgrammingJfreechart indexoutofbounds exception

Ici, la solution consiste à utiliser SwingUtilities.invokeLater. Mais je n'arrive pas à comprendre comment l'utiliser dans mon application. Mon code est:

import java.awt.BorderLayout; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import static java.lang.Thread.sleep; 
import org.jfree.chart.axis.ValueAxis; 
import javax.swing.JButton; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import org.jfree.chart.ChartFactory; 
import org.jfree.chart.ChartPanel; 
import org.jfree.chart.JFreeChart; 
import org.jfree.chart.axis.NumberTickUnit; 
import org.jfree.chart.plot.XYPlot; 
import org.jfree.data.xy.XYSeries; 
import org.jfree.data.xy.XYSeriesCollection; 

public class dataPlotter { 

    private static int x = 0; 
    private static boolean thread_start = false; 
    private static final double PI = 3.1428571; 
    private static boolean thread_run = true; 
    private static double voltage = 0; 
    private static boolean useSeries1 = true; 

    public static void main(String[] args) { 

     //create and configure window 
     JFrame window = new JFrame(); 
     window.setTitle("Data Plotter GUI"); 
     window.setSize(600, 400); 
     window.setLayout(new BorderLayout()); 
     window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 

     //create a drop down box and connect button, then place that at the top of the window 
     JButton connectButton = new JButton("Start"); 
     JPanel topPanel = new JPanel(); 
     topPanel.add(connectButton); 
     window.add(topPanel, BorderLayout.NORTH); 

     //create the line graph 
     XYSeries series1 = new XYSeries("Voltage Characteristics"); 
     XYSeries series2 = new XYSeries("Voltage Characteristics"); 
     XYSeriesCollection dataset = new XYSeriesCollection(series1); 
     JFreeChart chart = ChartFactory.createXYLineChart("Sine Wave", "Time", "Voltage", dataset); 
     window.add(new ChartPanel(chart), BorderLayout.CENTER); 

     //set range of x axis and range of series 
     XYPlot xyPlot = chart.getXYPlot(); 
     ValueAxis domainAxis = xyPlot.getDomainAxis(); 
     ValueAxis rangeAxis = xyPlot.getRangeAxis(); 
     rangeAxis.setRange(-1.5, 1.5); 
     domainAxis.setRange(0, 1000); 

     //Declaring thread 
     Thread thread; 
     thread = new Thread() { 
      @Override 
      public void run() { 
       thread_start = true; 
       int count_y = 0; 
       thread_run = true; 
       int count = 0; 
       while (thread_run) { 
        //create "Y" datapoint i.e. voltage 
        if (count_y < 800) { 
         count_y++; 
        } else { 
         count_y = 0; 
        } 
        voltage = Math.sin(2 * PI * count_y/800); 

        //add datapoint to CHART 
        try { 
         //use series 1 to accumulate 1000 datapoints 
         if(useSeries1){ 
          synchronized (series1) { 
           if (series1.getItemCount() > 1000) { 
            domainAxis.setRange(x - 1001, x); 
           } 
           series1.add(x++, voltage); 
           sleep(1); 
          } 
          synchronized (series1) { 
           window.repaint(); 
          } 

          synchronized (series1) { 
           if (series1.getItemCount() ==1001) { 

            dataset.removeAllSeries(); 
            series2.clear(); 
            domainAxis.setRange(0, 1000); 
            dataset.addSeries(series1); 
            useSeries1 = false; 
            sleep(5); 
            x = 0; 
            window.repaint(); 
           } 
          } 
         } else{ 
          synchronized (series2) { 
           if (series2.getItemCount() > 1000) { 
            domainAxis.setRange(x - 1001, x); 
           } 
           series2.add(x++, voltage); 
           sleep(1); 
          } 
          synchronized (series2) { 
           window.repaint(); 
          } 
          synchronized (series2) { 
           if (series2.getItemCount() == 1001) { 
            dataset.removeAllSeries(); 
            series1.clear(); 
            domainAxis.setRange(0, 1000); 
            dataset.addSeries(series2); 
            useSeries1 = true; 
            sleep(5); 
            x = 0; 
            window.repaint(); 
           } 
          } 
         } 
        } catch (Exception er) { } 
       } 
       thread_start = false; 
      } 
      }; 

     //configure the connect button and use another thread to listen for data 
     connectButton.addActionListener(new ActionListener() { 
      @Override 
      @SuppressWarnings("empty-statement") 
      public void actionPerformed(ActionEvent e) { 
       if (connectButton.getText().equals("Start")) { 

        connectButton.setText("Stop"); 

        thread.start(); 
       } else { 
        //disconnect from the serial port 
        thread_run = false; 

        //while (thread_start); 
        connectButton.setText("Start"); 
        //synchronized (series) { 
        //series.clear(); 
        //} 
        //x = 0; 

       } 
      } 

     }); 

     //Display winow 
     window.setVisible(true); 

    } 
} 
+1

Vous semblez avoir le chariot avant le cheval car il est généralement préférable d'apprendre les concepts orientés objet et de threading avant d'essayer de faire des graphes GUI en temps réel. Vous voudrez peut-être envisager de reculer un peu et d'étudier intensivement ces bases avant de continuer, sinon je crains que vous soyez dans une situation de frustration grave. –

+0

Même avec une seule série où j'ajoute des éléments et supprime des éléments, cela donne une erreur d'indexoutofbound. L'erreur n'apparaît pas avec de grands intervalles de sommeil. Je pense à créer 2 threads 1 pour l'accumulation de données et 1 pour la mise à jour de l'interface graphique, mais avoir et appelant une méthode ADD et une méthode REMOVE l'une après l'autre crée le problème principal. – EgLeO

+0

Après avoir lu un petit sous-ensemble de JFreeChart, il n'utilise pas du tout synchronisé, donc le vôtre est inutile ... – Aubin

Répondre

1

Swing est pas sûre, par la conception. Vous devez utiliser SwingUtilities.invokeLater(...) à partir d'un autre fil que le fil de la pompe de l'événement Swing comme indiqué here. Le tutorial about Swing peut aider.

Voici une proposition orientée objet. J'espère que cela t'aides.

public class Plotter implements Runnable { 

    private static final String BTN_START = "Start"; 
    private static final String BTN_STOP = "Stop"; 

    private final JFrame    window  = new JFrame(); 
    private final JButton   connectButton = new JButton(BTN_START); 
    private final XYSeries   series1  = new XYSeries("Voltage Characteristics"); 
    private final XYSeries   series2  = new XYSeries("Voltage Characteristics"); 
    private final XYSeriesCollection dataset  = new XYSeriesCollection(series1); 
    private final JFreeChart   chart   = 
     ChartFactory.createXYLineChart("Sine Wave", "Time", "Voltage", dataset); 
    private final XYPlot    xyPlot  = chart.getXYPlot(); 
    private final ValueAxis   domainAxis = xyPlot.getDomainAxis(); 
    private final ValueAxis   rangeAxis  = xyPlot.getRangeAxis(); 
    private /* */ Thread    thread  = null; 
    private /* */ boolean   thread_run = true; 
    private /* */ double    voltage  = 0; 
    private /* */ boolean   useSeries1 = true; 
    private /* */ int    x    = 0; 

    public Plotter() { 
     window.setTitle("Data Plotter GUI"); 
     window.setSize(600, 400); 
     window.setLayout(new BorderLayout()); 
     window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     final JPanel topPanel = new JPanel(); 
     topPanel.add(connectButton); 
     window.add(topPanel, BorderLayout.NORTH); 
     window.add(new ChartPanel(chart), BorderLayout.CENTER); 
     rangeAxis.setRange(-1.5, 1.5); 
     domainAxis.setRange(0, 1000); 
     connectButton.addActionListener(e -> startOrStop()); 
     window.setVisible(true); 
    } 

    private void startOrStop() { 
     if (connectButton.getText().equals(BTN_START)) { 
     connectButton.setText(BTN_STOP); 
     synchronized(series1) { series1.clear(); } 
     synchronized(series2) { series2.clear(); } 
     thread = new Thread(Plotter.this); 
     thread.setName("Plotter"); 
     thread.setDaemon(true); 
     thread.start(); 
     } 
     else { 
     thread_run = false; 
     //disconnect from the serial port 
     connectButton.setText(BTN_START); 
     try { 
      thread.join(10_000L); 
     } 
     catch(final InterruptedException e){ 
      e.printStackTrace(); 
     } 
     } 
    } 

    private void updateSeries1() { 
     if (series1.getItemCount() > 1000) { 
     domainAxis.setRange(x - 1001, x); 
     } 
     series1.add(x++, voltage); 
     window.repaint(); 
     if (series1.getItemCount() ==1001) { 

     dataset.removeAllSeries(); 
     series2.clear(); 
     domainAxis.setRange(0, 1000); 
     dataset.addSeries(series1); 
     useSeries1 = false; 
     window.repaint(); 
     x = 0; 
     } 
    } 

    private void updateSeries2() { 
     if (series2.getItemCount() > 1000) { 
     domainAxis.setRange(x - 1001, x); 
     } 
     series2.add(x++, voltage); 
     window.repaint(); 
     if(series2.getItemCount() == 1001) { 
     dataset.removeAllSeries(); 
     series1.clear(); 
     domainAxis.setRange(0, 1000); 
     dataset.addSeries(series2); 
     useSeries1 = true; 
     window.repaint(); 
     x = 0; 
     } 
    } 

    @Override 
    public void run() { 
     x = 0; 
     int count_y = 0; 
     thread_run = true; 
     while(thread_run) { 
     if (count_y < 800) { 
      count_y++; 
     } 
     else { 
      count_y = 0; 
     } 
     voltage = Math.sin((2 * Math.PI * count_y)/800); 
     try { 
      // Push a new job in the Swing queue 
      if(useSeries1) { 
       SwingUtilities.invokeLater(() -> updateSeries1()); 
      } 
      else { 
       SwingUtilities.invokeLater(() -> updateSeries2()); 
      } 
     } 
     catch (final Exception er) { 
      er.printStackTrace(); 
     } 
     try { 
      Thread.sleep(2L); // Give time to Swing to execute the job 
     } 
     catch(final InterruptedException e){ 
      e.printStackTrace(); 
     } 
     } 
    } 

    public static void main(String[] args) { 
     SwingUtilities.invokeLater(() -> new Plotter()); 
    } 
} 
+1

Considérez également l'une des approches examinées [ici] (https://stackoverflow.com/q/5048852/230513). – trashgod

+1

Merci beaucoup Aubin pour le code que je vais vérifier et étudier. Trashgod merci pour la suggestion que je regarde dans les timeseries pour voir si je peux l'utiliser pour répondre à mon objectif. – EgLeO

0

Merci beaucoup à AUBIN J'ai obtenu un code de travail final et rapide sans aucune erreur. Le code final est:

import java.awt.BorderLayout; 
import org.jfree.chart.axis.ValueAxis; 
import javax.swing.JButton; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.SwingUtilities; 
import org.jfree.chart.ChartFactory; 
import org.jfree.chart.ChartPanel; 
import org.jfree.chart.JFreeChart; 
import org.jfree.chart.plot.XYPlot; 
import org.jfree.data.xy.XYSeries; 
import org.jfree.data.xy.XYSeriesCollection; 


public class Plotter implements Runnable { 

    private static final String BTN_START = "Start"; 
    private static final String BTN_STOP = "Stop"; 

    private final JFrame    window  = new JFrame(); 
    private final JButton   connectButton = new JButton(BTN_START); 
    private final XYSeries   series1  = new XYSeries("Voltage Characteristics"); 
    private final XYSeries   series2  = new XYSeries("Voltage Characteristics"); 
    private final XYSeriesCollection dataset  = new XYSeriesCollection(series2); 
    private final JFreeChart   chart   = ChartFactory.createXYLineChart("Sine Wave", "Time", "Voltage", dataset); 
    private final XYPlot    xyPlot  = chart.getXYPlot(); 
    private final ValueAxis   domainAxis = xyPlot.getDomainAxis(); 
    private final ValueAxis   rangeAxis  = xyPlot.getRangeAxis(); 
    private /* */ Thread    thread  = null; 
    private /* */ boolean   thread_run = true; 
    private /* */ double    voltage  = 0; 
    private /* */ boolean   useSeries1 = true; 
    private /* */ int    x    = 0; 

    public Plotter() { 
     window.setTitle("Data Plotter GUI"); 
     window.setSize(600, 400); 
     window.setLayout(new BorderLayout()); 
     window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     final JPanel topPanel = new JPanel(); 
     topPanel.add(connectButton); 
     window.add(topPanel, BorderLayout.NORTH); 
     window.add(new ChartPanel(chart), BorderLayout.CENTER); 
     rangeAxis.setRange(-1.5, 1.5); 
     domainAxis.setRange(0, 1000); 
     connectButton.addActionListener(e -> startOrStop()); 
     window.setVisible(true); 
    } 

    private void startOrStop() { 
     if (connectButton.getText().equals(BTN_START)) { 
     connectButton.setText(BTN_STOP); 
     synchronized(series1) { series1.clear(); } 
     synchronized(series2) { series2.clear(); } 
     thread = new Thread(Plotter.this); 
     thread.setName("Plotter"); 
     thread.setDaemon(true); 
     thread.start(); 
     } 
     else { 
     thread_run = false; 
     //disconnect from the serial port 
     connectButton.setText(BTN_START); 
     try { 
      thread.join(10_000L); 
     } 
     catch(final InterruptedException e){ 
      e.printStackTrace(); 
     } 
     } 
    } 

    private void updateSeries1() { 
     if (series1.getItemCount() > 1000) { 
     domainAxis.setRange(x - 1001, x); 
     } 
     series1.add(x++, voltage); 
     //window.repaint(); 
     if (series1.getItemCount() ==1001) { 

     dataset.removeAllSeries(); 
     series2.clear(); 
     domainAxis.setRange(0, 1000); 
     dataset.addSeries(series1); 
     useSeries1 = false; 
     window.repaint(); 
     x = 0; 
     } 
    } 

    private void updateSeries2() { 
     if (series2.getItemCount() > 1000) { 
     domainAxis.setRange(x - 1001, x); 
     } 
     series2.add(x++, voltage); 
     //window.repaint(); 
     if(series2.getItemCount() == 1001) { 
     dataset.removeAllSeries(); 
     series1.clear(); 
     domainAxis.setRange(0, 1000); 
     dataset.addSeries(series2); 
     useSeries1 = true; 
     window.repaint(); 
     x = 0; 
     } 
    } 

    @Override 
    public void run() { 
     x = 0; 
     int count_y = 0; 
     thread_run = true; 
     while(thread_run) { 
     if (count_y < 800) { 
      count_y++; 
     } 
     else { 
      count_y = 0; 
     } 
     voltage = Math.sin((2 * Math.PI * count_y)/800); 
     try { 
      // Push a new job in the Swing queue 
      if(useSeries1) { 
       SwingUtilities.invokeAndWait(() -> updateSeries1()); 
      } 
      else { 
       SwingUtilities.invokeAndWait(() -> updateSeries2()); 
      } 
     } 
     catch (final Exception er) { 
      er.printStackTrace(); 
     } 
     } 
    } 

    public static void main(String[] args) { 
     SwingUtilities.invokeLater(() -> new Plotter()); 
    } 
}