这是我第一次在StackOverFlow上发帖,请原谅我的帖子中的任何错误。 来到这个主题我有一个程序,应该绘制某些X和Y坐标。 我目前正在使用JFREECHART for XYChart。 为了满足高速更新要求,我有2个系列。 Series1累积了1000个数据点,然后被添加到图表中以显示它。在这个系列之后,确定了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);
}
}
答案 0 :(得分:1)
根据设计,Swing不是线程安全的。您必须使用来自另一个线程的SwingUtilities.invokeLater( ... )
而不是Swing事件泵线程,如here所述。
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());
}
}
答案 1 :(得分:0)
非常感谢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());
}
}