Графики в реальном времени на Java
У меня есть приложение, которое обновляет переменную примерно от 5 до 50 раз в секунду, и я ищу способ нарисовать непрерывный XY график этого изменения в режиме реального времени.
Хотя JFreeChart не рекомендуется для такой высокой частоты обновления, многие пользователи все еще говорят, что это работает для них. Я попытался использовать эту демонстрацию и изменил ее для отображения случайной величины, но, похоже, она все время использует 100% загрузки процессора. Даже если я игнорирую это, я не хочу ограничиваться пользовательским классом JFreeChart для конструирования форм (хотя я не уверен, какие именно его возможности есть). Можно ли было интегрировать его с "формами" Java и выпадающими меню? (как доступно в VB) В противном случае, есть ли альтернативы, которые я мог бы рассмотреть?
РЕДАКТИРОВАТЬ: я новичок в Swing, поэтому я собрал код, чтобы проверить функциональность JFreeChart с ним (избегая использования класса ApplicationFrame JFree, так как я не уверен, как это будет работать с комбо Swing коробки и кнопки). Прямо сейчас график обновляется немедленно, а загрузка ЦП высока. Можно ли буферизовать значение с помощью новой функции Millisecond() и обновлять ее, возможно, дважды в секунду? Кроме того, я могу добавить другие компоненты к остальной части JFrame, не разрушая JFreeChart? Как бы я это сделал? 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);
}
}
}
}
}
8 ответов
Если ваша переменная обновляется так быстро, нет смысла обновлять график каждый раз.
Задумывались ли вы о буферизации изменений переменных и обновлении диаграммы в другом потоке, скажем, каждые 5 секунд? Вы должны обнаружить, что JFreeChart может хорошо обрабатывать такие частоты обновления.
Поскольку JFreeChart - это обычная настольная библиотека, вы можете очень легко интегрировать ее со стандартным приложением Swing. Или вы можете использовать его для построения графиков через веб-приложение (путем рендеринга в JPEG/PNG и т. Д. JFreeChart также может автоматически генерировать карты изображений, поэтому вы можете использовать указатели мыши и т. Д.)
Для того, чтобы ваш процессор был ниже 100% и чтобы ваш графический интерфейс оставался отзывчивым, вы должны снизить частоту обновления графика. Максимальная частота обновления около 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) {
}
}
}
}
Согласно этому сообщению в блоге:
http://jonathanwatmough.com/2008/02/prototyping-code-in-clojure/
можно реализовать отображение аудио-спектров в реальном времени, используя библиотеку KJ DSP:
http://sirk.sytes.net/software/libs/kjdss/index.htm
так что если вы можете обойтись довольно простыми графиками, это может быть альтернативой JFreeChart.
Если данные обновляются чаще, чем вы можете сгенерировать диаграмму, тогда у вас должна быть задача в отдельном потоке, который регенерирует диаграмму и запускает другую регенерацию, когда это будет сделано. Нет смысла запускать его чаще, чем это, но если это оказывается слишком большой нагрузкой на процессор, вы можете уменьшить частоту, с которой он перезапускается. Если обновления не приходят, вы не запускаете повторную генерацию. Я недавно сделал что-то подобное в своем проекте 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;
}
}
Ну, я также использую JFreechart для высоких обновлений. JFreeChart обновляет до 10-15 кадров в секунду, но использует 100% загрузки ЦП. Но если я хочу обновить его на более высокой частоте, он не будет обновлен. Если вы найдете какую-либо библиотеку, которая может быть обновлена со скоростью около 20 кадров в секунду и может быть использована для разработки приложения на Java, пожалуйста, предложите мне также. Я видел много часто задаваемых вопросов по библиотеке JFreeChart, но я не уверен, что кто-то может использовать обновления для скорости около 20 кадров в секунду.
Ответили раньше здесь. Ваша переменная изменяется до 50 раз в секунду, но в большинстве случаев вам не нужно будет обновляться каждый раз, когда вносятся изменения. Вместо этого вы можете обновлять график через регулярные промежутки времени (например, каждые 100 мс).
Вы должны попробовать графики из VisualVM (часть JDK). Введение в это: http://java.dzone.com/news/real-time-charts-java-desktop
Может быть, вы можете использовать две темы. Один для обновления переменной с приоритетом ведьмы равен 10. И второй поток, который рисует так быстро, как возможный приоритет ведьмы, равен 5.
Я должен был сделать то же самое в игре, которую я пишу.
Возможно, я не поняла ваш вопрос.