[英]Real-time graphing in Java
我有一个应用程序,它每秒更新一个变量大约 5 到 50 次,我正在寻找某种方法来实时绘制这种变化的连续 XY 图。
尽管 JFreeChart 不推荐用于如此高的更新率,但许多用户仍然表示它对他们有用。 我试过使用这个演示并修改它以显示一个随机变量,但它似乎一直使用 100% 的 CPU 使用率。 即使我忽略了这一点,我也不希望局限于 JFreeChart 的 ui 类来构建表单(尽管我不确定它的功能究竟是什么)。 是否可以将其与 Java 的“表单”和下拉菜单集成? (在 VB 中可用)否则,有没有我可以研究的替代方案?
编辑:我是 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);
}
}
}
}
}
如果您的变量更新得那么快,那么每次都更新图表就没有意义了。
你有没有想过缓冲变量的变化,并在不同的线程上刷新图表,比如每 5s 一次? 您应该会发现 JFreeChart 可以很好地处理这样的更新率。
由于 JFreeChart 是一个普通的桌面库,您可以很容易地将它与标准 Swing 应用程序集成。 或者,您可以使用它通过 Web 应用程序绘制图表(通过渲染为 JPEG/PNG 等。JFreeChart 也可以自动生成图像映射,因此您可以使用鼠标悬停等)
为了让您的 CPU 远低于 100% 并让您的 GUI 保持响应,您必须降低图表更新率。 对于实时图表来说,大约每秒 24 帧的最大更新率是有意义的; 无论如何,任何更快的速度或多或少都无法区分。 如果您的数据进入速度比该速度快,您只需要在后台缓冲它并以您想要的更新速度在前台更新您的图表。 在以下示例中,我将XChart与SwingWorker
后台线程一起使用。 数据捕获以每 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) {
}
}
}
}
如果数据更新的频率超过您生成图表的频率,那么您应该在单独的线程中设置一个任务来重新生成图表,并在完成后开始另一次重新生成。 经常运行它没有什么意义,但是如果结果证明 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;
}
}
根据这篇博文:
http://jonathanwatmough.com/2008/02/prototyping-code-in-clojure/
可以使用 KJ DSP 库实现音频频谱的“实时”显示:
http://sirk.sytes.net/software/libs/kjdss/index.htm
因此,如果您可以使用相当简单的图表,它可能是 JFreeChart 的替代品。
之前在这里回答过。 您的变量每秒最多更改 50 次,但在大多数情况下,您不需要在每次更改时更新。 相反,您可以定期更新图表(例如每 100 毫秒)。
也许你可以使用两个线程。 一个用于更新您的变量女巫优先级等于 10。第二个线程在可能的女巫优先级等于 5 时绘制得如此之快。
我必须在我正在编写的游戏中做同样的事情。
可能我没理解你的问题。
好吧,我也在使用 JFreechart 进行高更新。 JFreeChart 更新速度高达 10 到 15 帧/秒,但使用 100% 的 CPU 使用率。 但是如果我想以更高的频率更新它,它就不会被更新。 如果您发现任何可以以 20 fps 更新并且可用于在 Java 中开发应用程序的库,那么也请给我建议。 我看过很多库JFreeChart 常见问题解答,但我不确定是否有人可以以大约 20 fps 的速度进行更新。
您应该尝试 VisualVM(JDK 的一部分)中的图表。 介绍: http : //java.dzone.com/news/real-time-charts-java-desktop
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.