Java 中的实时绘图

2022-09-03 15:04:09

我有一个应用程序,它每秒更新大约5到50次的变量,我正在寻找某种方法来实时绘制此变化的连续XY图。

虽然JFreeChart不建议用于如此高的更新率,但许多用户仍然说它适用于他们。我尝试使用此演示并将其修改为显示随机变量,但它似乎一直占用100%的CPU使用率。即使我忽略了这一点,我也不想被限制在JFreeChart的ui类来构造表单(尽管我不确定它的功能到底是什么)。是否有可能将其与Java的“表单”和下拉菜单集成?(在 VB 中可用)否则,我是否可以研究其他选择?

编辑:我是Swing的新手,所以我整理了一个代码,只是为了测试JFreeChart的功能(同时避免使用JFree的AppplicaticalFrame类,因为我不确定它将如何与Swing的组合框和按钮一起使用)。目前,图形正在立即更新,CPU使用率很高。是否可以用新的Millisecond()缓冲该值,并可能每秒更新两次?另外,我可以在不中断JFreeChart的情况下向JFrame的其余部分添加其他组件吗?我该怎么做?frame.getContentPane().add(new Button(“Click”)) 似乎覆盖了图表。

package graphtest;

import java.util.Random;
import javax.swing.JFrame;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.XYPlot;
import org.jfree.data.time.Millisecond;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;

public class Main {
    static TimeSeries ts = new TimeSeries("data", Millisecond.class);

    public static void main(String[] args) throws InterruptedException {
        gen myGen = new gen();
        new Thread(myGen).start();

        TimeSeriesCollection dataset = new TimeSeriesCollection(ts);
        JFreeChart chart = ChartFactory.createTimeSeriesChart(
            "GraphTest",
            "Time",
            "Value",
            dataset,
            true,
            true,
            false
        );
        final XYPlot plot = chart.getXYPlot();
        ValueAxis axis = plot.getDomainAxis();
        axis.setAutoRange(true);
        axis.setFixedAutoRange(60000.0);

        JFrame frame = new JFrame("GraphTest");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        ChartPanel label = new ChartPanel(chart);
        frame.getContentPane().add(label);
        //Suppose I add combo boxes and buttons here later

        frame.pack();
        frame.setVisible(true);
    }

    static class gen implements Runnable {
        private Random randGen = new Random();

        public void run() {
            while(true) {
                int num = randGen.nextInt(1000);
                System.out.println(num);
                ts.addOrUpdate(new Millisecond(), num);
                try {
                    Thread.sleep(20);
                } catch (InterruptedException ex) {
                    System.out.println(ex);
                }
            }
        }
    }

}

答案 1

如果您的变量更新得如此之快,那么每次更新图表都没有意义。

你有没有想过缓冲变量变化,并在不同的线程上刷新图表,比如说,每5秒一次?你应该发现JFreeChart可以很好地处理这样的更新速率。

由于JFreeChart是一个普通的桌面库,因此您可以非常轻松地将其与标准的Swing应用程序集成。或者,您可以使用它通过Web应用程序(通过渲染为JPEG / PNG等)绘制图表。JFreeChart也可以自动生成图像映射,因此您可以使用鼠标悬停等。


答案 2

为了使您的CPU远低于100%并允许您的GUI保持响应,您必须限制图表更新速率。每秒约24帧的最大更新速率对于实时图表是有意义的;无论如何,任何更快的速度都或多或少是无法区分的。如果数据的传入速度超过该速率,则只需在后台缓冲数据,并以所需的更新速率在前台更新图表即可。在下面的示例中,我将 XChart 与后台线程一起使用。数据捕获以每 5 毫秒一次的速率进行模拟,图表以每秒 24 帧的速度更新。这个概念应该适用于JFreeCharts或任何其他图表库,并且只需稍作修改。免责声明:我是XChart的首席开发人员。SwingWorker

import java.util.LinkedList;
import java.util.List;

import javax.swing.SwingWorker;

import org.knowm.xchart.QuickChart;
import org.knowm.xchart.SwingWrapper;
import org.knowm.xchart.XYChart;

/**
 * Creates a real-time chart using SwingWorker
 */
public class SwingWorkerRealTime {

  MySwingWorker mySwingWorker;
  SwingWrapper<XYChart> sw;
  XYChart chart;

  public static void main(String[] args) throws Exception {

    SwingWorkerRealTime swingWorkerRealTime = new SwingWorkerRealTime();
    swingWorkerRealTime.go();
  }

  private void go() {

    // Create Chart
    chart = QuickChart.getChart("SwingWorker XChart Real-time Demo", "Time", "Value", "randomWalk", new double[] { 0 }, new double[] { 0 });
    chart.getStyler().setLegendVisible(false);
    chart.getStyler().setXAxisTicksVisible(false);

    // Show it
    sw = new SwingWrapper<XYChart>(chart);
    sw.displayChart();

    mySwingWorker = new MySwingWorker();
    mySwingWorker.execute();
  }

  private class MySwingWorker extends SwingWorker<Boolean, double[]> {

    LinkedList<Double> fifo = new LinkedList<Double>();

    public MySwingWorker() {

      fifo.add(0.0);
    }

    @Override
    protected Boolean doInBackground() throws Exception {

      while (!isCancelled()) {

        fifo.add(fifo.get(fifo.size() - 1) + Math.random() - .5);
        if (fifo.size() > 500) {
          fifo.removeFirst();
        }

        double[] array = new double[fifo.size()];
        for (int i = 0; i < fifo.size(); i++) {
          array[i] = fifo.get(i);
        }
        publish(array);

        try {
          Thread.sleep(5);
        } catch (InterruptedException e) {
          // eat it. caught when interrupt is called
          System.out.println("MySwingWorker shut down.");
        }

      }

      return true;
    }

    @Override
    protected void process(List<double[]> chunks) {

      System.out.println("number of chunks: " + chunks.size());

      double[] mostRecentDataSet = chunks.get(chunks.size() - 1);

      chart.updateXYSeries("randomWalk", null, mostRecentDataSet, null);
      sw.repaintChart();

      long start = System.currentTimeMillis();
      long duration = System.currentTimeMillis() - start;
      try {
        Thread.sleep(40 - duration); // 40 ms ==> 25fps
        // Thread.sleep(400 - duration); // 40 ms ==> 2.5fps
      } catch (InterruptedException e) {
      }

    }
  }
}

XChart SwingWorker Realtime Java Chart


推荐