Java 中的实时绘图

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1389285/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 11:32:51  来源:igfitidea点击:

Real-time graphing in Java

javagraphreal-timejfreechart

提问by thodinc

I have an application which updates a variable about between 5 to 50 times a second and I am looking for some way of drawing a continuous XY plot of this change in real-time.

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

Though JFreeChart is not recommended for such a high update rate, many users still say that it works for them. I've tried using thisdemo and modified it to display a random variable, but it seems to use up 100% CPU usage all the time. Even if I ignore that, I do not want to be restricted to JFreeChart's ui class for constructing forms (though I'm not sure what its capabilities are exactly). Would it be possible to integrate it with Java's "forms" and drop-down menus? (as are available in VB) Otherwise, are there any alternatives I could look into?

尽管 JFreeChart 不推荐用于如此高的更新率,但许多用户仍然表示它对他们有用。我试过使用这个演示并修改它以显示一个随机变量,但它似乎一直使用 100% 的 CPU 使用率。即使我忽略了这一点,我也不希望局限于 JFreeChart 的 ui 类来构建表单(尽管我不确定它的功能究竟是什么)。是否可以将其与 Java 的“表单”和下拉菜单集成?(在 VB 中可用)否则,有没有我可以研究的替代方案?

EDIT:I'm new to Swing, so I've put together a code just to test the functionality of JFreeChart with it (while avoiding the use of the ApplicationFrame class of JFree since I'm not sure how that will work with Swing's combo boxes and buttons). Right now, the graph is being updated immediately and CPU usage is high. Would it be possible to buffer the value with new Millisecond() and update it maybe twice a second? Also, can I add other components to the rest of the JFrame without disrupting JFreeChart? How would I do that? frame.getContentPane().add(new Button("Click")) seems to overwrite the graph.

编辑:我是 Swing 的新手,所以我整理了一个代码来测试 JFreeChart 的功能(同时避免使用 JFree 的 ApplicationFrame 类,因为我不确定这将如何与 Swing 的组合一起使用框和按钮)。现在,图表正在立即更新,CPU 使用率很高。是否可以使用 new 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);
                }
            }
        }
    }

}

采纳答案by Brian Agnew

If your variable is updating that fast, there's no point in updating a chart every time.

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

Have you thought about buffering the variable changes, and refreshing the chart on a different thread, say, every 5s ? You should find that JFreeChart can handle such update rates well.

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

Since JFreeChart is a normal desktop library, you can integrate it with a standard Swing application very easily. Or, you can use it to chart via a web application (by rendering to a JPEG/PNG etc. JFreeChart can generate image maps automatically as well, so you can use mouseovers etc.)

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

回答by kgiannakakis

Answered before here. Your variable changes up to 50 times per second, but in most cases you won't need to update every time a change is made. Instead you could update the graph at regular intervals (every 100ms for instance).

之前在这里回答过。您的变量每秒最多更改 50 次,但在大多数情况下,您不需要在每次更改时更新。相反,您可以定期更新图表(例如每 100 毫秒)。

回答by PanCrit

If the data is updating more often than you can generate the chart, then you should have a task in a separate thread that regenerates the chart, and starts another regeneration when it's done. There's little point in running it ore often than that, but if it turns out to be too much of a cpu load, you can throttle back the frequency with which it restarts. If updates don't come in, you don't trigger the re-generate. I did something like that in my Zocalo projectrecently. It does everything but the throttling back.

如果数据更新的频率超过您生成图表的频率,那么您应该在单独的线程中设置一个任务来重新生成图表,并在完成后开始另一次重新生成。经常运行它没有什么意义,但是如果结果证明 CPU 负载太大,您可以降低它重新启动的频率。如果没有更新,您不会触发重新生成。我最近在我的Zocalo 项目中做了类似的事情 。除了节流之外,它什么都做。

package net.commerce.zocalo.freechart;

// Copyright 2009 Chris Hibbert.  All rights reserved.

// This software is published under the terms of the MIT license, a copy
// of which has been included with this distribution in the LICENSE file.

import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.Map;
import java.util.HashMap;

/**  Schedule a task like generating a price history graph.  Multiple requests may come
 in sporadically.  We want to ensure that only one is being processed at a time.  If we're
 busy processing when a request comes in, we'll remember to start another when this one is
 done.  Multiple requests that come in while processing will spur a single restart. */
public class ChartScheduler {
    static private Logger log = Logger.getLogger(ChartScheduler.class);
    static private Map<String, ChartScheduler> schedulers = new HashMap<String, ChartScheduler>();
    private AtomicBoolean generating = new AtomicBoolean(false);
    private AtomicBoolean requested = new AtomicBoolean(false);
    private ExecutorService threads = Executors.newCachedThreadPool();
    private Callable<Boolean> callable;
    private int runs = 0;
    private String name;


    private ChartScheduler(String name, final Runnable worker) {
        this.name = name;
        callable = new Callable<Boolean>() {
            public Boolean call() throws Exception {
                worker.run();
                runs++;
                restartIfNeeded();
                return true;
            }
        };
    }

    public static ChartScheduler create(String name, Runnable worker) {
        ChartScheduler sched = find(name);
        if (sched == null) {
            sched = new ChartScheduler(name, worker);
            schedulers.put(name, sched);
        }
        return sched;
    }

    public static ChartScheduler find(String name) {
        return schedulers.get(name);
    }

    public boolean generateNewChart() {
        requested.set(true);
        if (generating.compareAndSet(false, true)) {
            startNewThread();
            return true;
        } else {
            return false;
        }
    }

    private Future<Boolean> startNewThread() {
        generating.set(true);
        requested.set(false);

        return threads.submit(callable);
    }

    private boolean restartIfNeeded() {
        generating.set(false);
        if (requested.get()) {
            return generateNewChart();

        } else {
            return false;
        }
    }

    public boolean isBusy() {
        return generating.get();
    }

    public int runs() {
        return runs;
    }
}

回答by Martijn Courteaux

Maybe you can use two threads. One for the updating of your variable witch priority equals to 10. And a second thread who paints so fast as posible witch priority equals to 5.

也许你可以使用两个线程。一个用于更新您的可变女巫优先级等于 10。第二个线程在可能的女巫优先级等于 5 时绘制得如此之快。

I had to do the same in a game I'm writing.

我必须在我正在编写的游戏中做同样的事情。

It's possible I didn't understand your question.

可能我没理解你的问题。

回答by Martijn Courteaux

Well I am also using JFreechart for high updates. JFreeChart updates up to 10 to 15 frame/second but using 100% CPU usage. But if I want to update it at a much higher frequency it wont be updated. If you find any any library which can be updated at abt 20 fps and can be used to develop a application in Java then please suggest me also. I have seen many library JFreeChart FAQbut I am not sure if anyone could be use for updates at about 20 fps.

好吧,我也在使用 JFreechart 进行高更新。JFreeChart 更新速度高达 10 到 15 帧/秒,但使用 100% 的 CPU 使用率。但是如果我想以更高的频率更新它,它就不会被更新。如果您发现任何可以以 20 fps 更新并且可用于在 Java 中开发应用程序的库,那么也请给我建议。我看过很多库JFreeChart 常见问题解答,但我不确定是否有人可以以大约 20 fps 的速度进行更新。

回答by tonys

According to this blog post:

根据这篇博文:

http://jonathanwatmough.com/2008/02/prototyping-code-in-clojure/

http://jonathanwatmough.com/2008/02/prototyping-code-in-clojure/

its possible to implement 'real-ish time' display of audio spectrums using the KJ DSP library:

可以使用 KJ DSP 库实现音频频谱的“实时”显示:

http://sirk.sytes.net/software/libs/kjdss/index.htm

http://sirk.sytes.net/software/libs/kjdss/index.htm

so if you can get by with fairly simple charts it might be an alternative to JFreeChart.

因此,如果您可以使用相当简单的图表,它可能是 JFreeChart 的替代品。

回答by Daumantas

You should try out charts from VisualVM (part of JDK). Intro on it: http://java.dzone.com/news/real-time-charts-java-desktop

您应该尝试 VisualVM(JDK 的一部分)中的图表。介绍:http: //java.dzone.com/news/real-time-charts-java-desktop

回答by herrtim

In order to get your CPU well below 100% and allow your GUI to remain responsive, you have to throttle back your chart updating rate. A maximum update rate of around 24 frames per second makes sense for a real-time chart; any faster is more or less indistinguishable anyway. If your data is coming in faster than that rate, you just need to buffer it in the background and update your chart in the foreground at your desired update rate. In the following example, I use XChartalong with a SwingWorkerbackground thread. The data capture is simulated at a rate of one per every 5 ms and the chart it updated at 24 frames per second. This concept should work with JFreeCharts or any other charting library as well with slight modification. Disclaimer: I'm the lead developer of XChart.

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

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

XChart SwingWorker 实时 Java 图表