【问题标题】:Real-time graphing in JavaJava中的实时绘图
【发布时间】:2010-11-26 05:36:01
【问题描述】:

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

尽管更新率如此之高的 JFreeChart 不被推荐,但许多用户仍然表示它适合他们。我尝试使用this 演示并对其进行修改以显示随机变量,但它似乎一直使用 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);
                }
            }
        }
    }

}

【问题讨论】:

    标签: java graph real-time jfreechart


    【解决方案1】:

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

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

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

    【讨论】:

    • 这似乎是个好主意,尽管我还必须缓冲某种时间戳。但是,无论我每秒更新 2 次还是 50 次,JFreeChart 似乎都占用了 100% 的 CPU 使用率。
    • 嗯,它做了很多工作——每次都从头开始重新生成图表。所以我建议少更新一点(当然这取决于你的机器和你的额外负载......)
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      根据这篇博文:

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

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

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

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

      【讨论】:

        【解决方案4】:

        如果数据更新的频率超过了您生成图表的频率,那么您应该在一个单独的线程中执行一个任务来重新生成图表,并在完成后开始另一次重新生成。经常运行它没有什么意义,但如果结果证明 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;
            }
        }
        

        【讨论】:

          【解决方案5】:

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

          【讨论】:

          • 我已经查看了该线程中提到的其他一些库,但是没有太多可以与 Java 的下拉框和其他对象集成的库。它们似乎都作为单独的应用程序启动。我目前正在研究 JRobin,它可能有用,但使用起来有点复杂。
          【解决方案6】:

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

          【讨论】:

            【解决方案7】:

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

            【讨论】:

              【解决方案8】:

              也许您可以使用两个线程。一个用于更新您的变量女巫优先级等于 10。 并且第二个线程绘制得如此之快,以至于女巫优先级等于 5。

              在我正在编写的游戏中,我也必须这样做。

              我可能没听懂你的问题。

              【讨论】:

                猜你喜欢
                • 2019-02-13
                • 2011-03-08
                • 2015-05-26
                • 2013-04-12
                • 2017-06-04
                • 2015-11-04
                • 2013-03-22
                • 2013-05-21
                相关资源
                最近更新 更多