Java中的实时图形

时间:2009-09-07 12:50:23

标签: java graph real-time jfreechart

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

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

编辑:我是Swing的新手,所以我整理了一个代码来测试JFreeChart的功能(同时避免使用JFree的ApplicationFrame类,因为我'我不确定如何使用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);
                }
            }
        }
    }

}

8 个答案:

答案 0 :(得分:8)

如果您的变量快速更新,则每次更新图表都没有意义。

您是否考虑过缓冲变量更改,并在不同的线程上刷新图表,比如每5秒?您应该发现JFreeChart可以很好地处理这样的更新速率。

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

答案 1 :(得分:1)

如果数据的更新频率高于生成图表的次数,那么您应该在一个单独的线程中创建一个任务来重新生成图表,并在完成时启动另一个重新生成。经常运行它并没有什么意义,但如果结果是cpu负载过多,你可以减少它重新启动的频率。如果没有更新,则不会触发重新生成。我最近在Zocalo project做了类似的事。它除了节流之外还可以做任何事情。

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;
    }
}

答案 2 :(得分:1)

根据这篇博文:

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

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

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

所以如果你可以通过相当简单的图表获得它,它可能是JFreeChart的替代品。

答案 3 :(得分:0)

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

答案 4 :(得分:0)

也许你可以使用两个线程。一个用于更新变量的优先级等于10。 并且第二个线程如此快速地绘制成可能的优先级等于5。

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

我可能无法理解你的问题。

答案 5 :(得分:0)

我也在使用JFreechart进行高更新。 JFreeChart最多可以更新10到15帧/秒,但使用100%的CPU使用率。但如果我想以更高的频率更新它,它将不会更新。如果您发现任何可以以20 fps的速度更新并且可以用Java开发应用程序的库,那么请建议我。我见过很多图书馆JFreeChart FAQ,但我不确定是否有人可以使用大约20 fps的更新。

答案 6 :(得分:0)

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

答案 7 :(得分:0)

为了使您的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) {
      }

    }
  }
}

XChart SwingWorker Realtime Java Chart