【问题标题】:Jfreechart indexoutofbounds exceptionJfreechart indexoutofbounds 异常
【发布时间】:2017-07-09 03:03:09
【问题描述】:

这是我第一次在 StackOverFlow 上发帖,所以请原谅我帖子中的任何错误。 谈到这个话题,我有一个程序应该绘制某些 X 和 Y 坐标。 我目前正在为 XYChart 使用 JFREECHART。 为了满足高速更新的要求,我有2个系列。 Series1 累积 1000 个数据点,然后添加到图表中以显示它。在此 series2 累积 1000 个数据点之后,一旦完成,它就会清除 series1(为新的 1000 个数据点做准备)并将 series2 附加到图表(现在有 1000 个数据点) )...这个循环还在继续。 问题在于 XYSeries.clear 和 XYSeries.add 函数。显然他们创建了自己的线程,在他们完成执行之前我已经产生了一个新线程,因为我的应用程序非常快。我认为这会导致 indexoutobounds 错误。我可以通过以下方式猜到这一点: http://www.jfree.org/phpBB2/viewtopic.php?p=68111 类似的错误。 解决方案似乎是: Swing Thread Safe Programming

这里的解决方案是使用 SwingUtilities.invokeLater。但我不明白如何在我的应用程序中使用它。 我的代码是:

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

    }
}

【问题讨论】:

  • 您似乎本末倒置,因为通常最好在尝试进行实时 GUI 图形之前学习面向对象和线程概念。在继续之前,您可能希望考虑备份并深入研究这些基础知识,否则我担心您可能会感到非常沮丧。
  • 即使只有一个系列,我在其中添加元素和删除元素也会给出 indexoutofbound 错误。睡眠间隔较大时不会出现该错误。我正在考虑创建 2 个线程,1 个用于数据积累,1 个用于 GUI 更新,但是一个接一个地调用 ADD 方法和 REMOVE 方法是主要问题。
  • 看了一小部分JFreeChart后,根本没有使用同步,所以你的没用...

标签: java swing thread-safety jfreechart


【解决方案1】:

Swing 在设计上不是线程安全的。您必须使用来自另一个线程的 SwingUtilities.invokeLater( ... ),而不是像 here 讨论的 Swing 事件泵线程。

tutorial about Swing 可能会有所帮助。

这是一个面向对象的建议。希望这会有所帮助。

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

【讨论】:

  • 还考虑检查的方法之一here
  • 非常感谢 Aubin 提供的代码,我会检查并研究它。 Trashgod 感谢我正在研究时间序列以查看是否可以使用它来满足我的目的的建议。
【解决方案2】:

非常感谢 AUBIN,我得到了最终的高速工作代码,没有任何错误。最终代码为:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-24
    • 1970-01-01
    • 2014-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-12
    • 2011-11-10
    相关资源
    最近更新 更多