使用 JFreeChart 更改系列时出现随机错误 [英] Random errors when changing series using JFreeChart

查看:31
本文介绍了使用 JFreeChart 更改系列时出现随机错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在制作一个显示后台计算结果的 GUI.但在此之前,我想测试更改数据集.这是我的代码:

 DefaultXYDataset dataset = new DefaultXYDataset();@覆盖公共无效运行(){//TODO 自动生成的方法存根for (int i = 0; i <句点; i++) {系列[0][i] = (双) i;系列[1][i] = 0;}dataset.addSeries("Series0", series);for (int it = 0; it <10; it++) {系列[1][random.nextInt(periods)] = random.nextInt(100)/2;double[][] d = new double[2][periods];for (int i = 0; i <句点; i++) {d[0][i] = 系列[0][i];d[1][i] = 系列 [1][i];}dataset.removeSeries("Series0");dataset.addSeries("Series0", series);//                     尝试 {//Thread.sleep(100);//} catch (java.lang.InterruptedException ex) {//}}

如您所见,我想更改图形上的点(每次它完成一些复杂的计算"时)——这个更改发生在我在另一个类中调用的线程中.我的问题是这整个概念不起作用.它抛出系列索引超出范围"-IllegalArgumentException、索引超出范围"-某些库内部数组列表等.我没有使用 DynamicTimeSeriesCollection 因为我需要 X 轴是我的内部迭代次数而不是时间期间,并且在并非每隔一段时间完成某些计算"时也会更新.你能告诉我我做错了什么吗?或者有没有更好的方法来更新/刷新图表?

解决方案

您的代码段不正确同步;您应该从

import java.awt.BorderLayout;导入 java.awt.Dimension;导入 java.awt.EventQueue;导入 java.beans.PropertyChangeEvent;导入 java.text.DecimalFormat;导入 java.util.List;导入 javax.swing.JFrame;导入 javax.swing.JLabel;导入 javax.swing.JProgressBar;导入 javax.swing.SwingWorker;导入 org.jfree.chart.ChartFactory;导入 org.jfree.chart.ChartPanel;导入 org.jfree.chart.JFreeChart;导入 org.jfree.chart.axis.NumberAxis;导入 org.jfree.chart.plot.PlotOrientation;导入 org.jfree.chart.plot.XYPlot;导入 org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;导入 org.jfree.data.xy.XY 数据集;导入 org.jfree.data.xy.XYSeries;导入 org.jfree.data.xy.XYSeriesCollection;/*** @see https://stackoverflow.com/a/13205322/230513* @see https://stackoverflow.com/questions/4637215*/公共最终类 ChartWorker {私有静态最终字符串S =0.000000000000000";私人最终 JProgressBar progressBar = new JProgressBar();私有最终 JLabel 标签 = new JLabel(S, JLabel.CENTER);私人最终 XYSeries 系列 = 新 XYSeries(结果");私人最终 XYDataset 数据集 = 新 XYSeriesCollection(series);私有无效创建(){JFrame f = new JFrame("√2");f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);f.add(progressBar, BorderLayout.NORTH);JFreeChart 图表 = ChartFactory.createXYLineChart(牛顿法"、X"、Y"、数据集、PlotOrientation.VERTICAL,假,真,假);XYPlot plot = (XYPlot) chart.getPlot();plot.getRangeAxis().setRange(1.4, 1.51);plot.getDomainAxis().setStandardTickUnits(NumberAxis.createIntegerTickUnits());XYLineAndShapeRenderer 渲染器= (XYLineAndShapeRenderer) plot.getRenderer();renderer.setSeriesShapesVisible(0, true);f.add(新图表面板(图表){@覆盖公共维度 getPreferredSize() {返回新维度(640, 480);}}, BorderLayout.CENTER);f.add(label, BorderLayout.SOUTH);f.pack();f.setLocationRelativeTo(null);f.setVisible(true);运行计算();}私有无效runCalc(){progressBar.setIndeterminate(true);TwoWorker 任务 = new TwoWorker();task.addPropertyChangeListener((PropertyChangeEvent e) -> {if ("progress".equals(e.getPropertyName())) {progressBar.setIndeterminate(false);progressBar.setValue((Integer) e.getNewValue());}});任务.执行();}私有类 TwoWorker 扩展了 SwingWorker;{私有静态最终 int N = 5;私人最终十进制格式 df = 新十进制格式(S);双 x = 1;私人国际n;@覆盖受保护的双 doInBackground() 抛出异常 {for (int i = 1; i <= N; i++) {x = x - (((x * x - 2)/(2 * x)));setProgress(i * (100/N));发布(x);线程睡眠(1000);//模拟延迟}返回 x;}@覆盖protected void process(List chunks) {for (double d : 块) {label.setText(df.format(d));系列.add(++n, d);}}}公共静态无效主(字符串 [] args){EventQueue.invokeLater(new ChartWorker()::create);}}

I'm making a GUI that display result of background calculations. But before that, I wanted to test changing the dataset. Here is my code:

 DefaultXYDataset dataset = new DefaultXYDataset();
@Override
        public void run() {
                // TODO Auto-generated method stub
                for (int i = 0; i < periods; i++) {
                        series[0][i] = (double) i;
                        series[1][i] = 0;
                }
                dataset.addSeries("Series0", series);
                for (int it = 0; it < 10; it++) {
                        series[1][random.nextInt(periods)] =  random.nextInt(100) / 2;
                        double[][] d = new double[2][periods];
                        for (int i = 0; i < periods; i++) {
                                d[0][i] = series[0][i];
                                d[1][i] = series[1][i];
                        }
                        dataset.removeSeries("Series0");
                        dataset.addSeries("Series0", series);
//                      try {
//                              Thread.sleep(100);
//                      } catch (java.lang.InterruptedException ex) {
//                      }
                }

As you can see, I want to change points on the graph (every time it finishes 'some complicated computations') - this change is in the thread invoked by me in another class. My problem is that this whole concept is not working. It throws 'Series index out of bounds'-IllegalArgumentException, 'index out of bounds' - of some library inner arraylist etc.. I'm not using DynamicTimeSeriesCollection because I need the X axis to be the number of my inner iterations not the time period, and also update when 'some computations' are finished not every some time period. Can you tell me what I'm doing wrong? Or is there a better way to update/refresh the graph?

解决方案

Your snippet is incorrectly synchronized; you should update your dataset from the process() method of a SwingWorker, as shown here. Because your domain is "the number of my inner iterations", don't use a DateAxis; instead, use a NumberAxis, as shown in ChartFactory.createXYLineChart().

Addendum: This variation on the example plots the worker's progress on a line chart. Note that createXYLineChart() uses NumberAxis for both domain and range. Given a series in the line chart's dataset, note also how the implementation of process() can safely update the dataset as new data arrives; the listening chart will update itself in response.

private XYSeries series = new XYSeries("Result");
…
@Override
protected void process(List<Double> chunks) {
    for (double d : chunks) {
        label.setText(df.format(d));
        series.add(++n, d);
    }
}

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.beans.PropertyChangeEvent;
import java.text.DecimalFormat;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JProgressBar;
import javax.swing.SwingWorker;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;

/**
 * @see https://stackoverflow.com/a/13205322/230513
 * @see https://stackoverflow.com/questions/4637215
 */
public final class ChartWorker {

    private static final String S = "0.000000000000000";
    private final JProgressBar progressBar = new JProgressBar();
    private final JLabel label = new JLabel(S, JLabel.CENTER);
    private final XYSeries series = new XYSeries("Result");
    private final XYDataset dataset = new XYSeriesCollection(series);

    private void create() {
        JFrame f = new JFrame("√2");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(progressBar, BorderLayout.NORTH);
        JFreeChart chart = ChartFactory.createXYLineChart(
            "Newton's Method", "X", "Y", dataset,
            PlotOrientation.VERTICAL, false, true, false);
        XYPlot plot = (XYPlot) chart.getPlot();
        plot.getRangeAxis().setRange(1.4, 1.51);
        plot.getDomainAxis().setStandardTickUnits(
            NumberAxis.createIntegerTickUnits());
        XYLineAndShapeRenderer renderer
            = (XYLineAndShapeRenderer) plot.getRenderer();
        renderer.setSeriesShapesVisible(0, true);
        f.add(new ChartPanel(chart) {
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(640, 480);
            }
        }, BorderLayout.CENTER);
        f.add(label, BorderLayout.SOUTH);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
        runCalc();
    }

    private void runCalc() {
        progressBar.setIndeterminate(true);
        TwoWorker task = new TwoWorker();
        task.addPropertyChangeListener((PropertyChangeEvent e) -> {
            if ("progress".equals(e.getPropertyName())) {
                progressBar.setIndeterminate(false);
                progressBar.setValue((Integer) e.getNewValue());
            }
        });
        task.execute();
    }

    private class TwoWorker extends SwingWorker<Double, Double> {

        private static final int N = 5;
        private final DecimalFormat df = new DecimalFormat(S);
        double x = 1;
        private int n;

        @Override
        protected Double doInBackground() throws Exception {
            for (int i = 1; i <= N; i++) {
                x = x - (((x * x - 2) / (2 * x)));
                setProgress(i * (100 / N));
                publish(x);
                Thread.sleep(1000); // simulate latency
            }
            return x;
        }

        @Override
        protected void process(List<Double> chunks) {
            for (double d : chunks) {
                label.setText(df.format(d));
                series.add(++n, d);
            }
        }
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new ChartWorker()::create);
    }
}

这篇关于使用 JFreeChart 更改系列时出现随机错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆