如何图表实时JavaFX的流媒体使用AreaChart数据2 - 并发,动画,图表(How to

2019-07-30 05:54发布

规定─建立一个动画AreaChart与实时流数据。 也许绘制300个数据点每1秒。

详细信息 - 所以我需要从医疗设备读取实时数据流,病人的呼吸模式,并在JavaFX中使用AreaChart波形的方式显示。 我是新来的JavaFX,所以我建立了一个小的POC,怎么看并发和JavaFX的动画作品。

这个概念的作品,我很高兴与基本测试,尽可能实现的功能。 但是我不是很满意,我从下面的代码获取的性能。

在下面的工作代码,创建一个单独的线程来模拟数据从医疗装置获取。 线程只是产生一个随机数,并将其添加到的ConcurrentLinkedQueue。

JavaFX应用程序线程翻出这个数据出来,从队列中,通过时间轴,并把它添加到AreaChart系列。

排序这给了我我需要的动画和数据在运行时被添加。 您可以复制粘贴此代码和测试it..It应该工作。

但表现并不令人印象深刻 - CPU进入56%的使用率 - 我有一个英特尔Core 2 Duo @ 2.53 GHz和4GB内存在我的笔记本电脑。 我的显卡是移动式英特尔4系列与最新的驱动程序表达。

我如何才能提高实时数据的这部动画或绘图,以获得更好的性能?

注:我愿意在动画妥协,如果它的瓶颈。 我愿意像这里显示的实施http://smoothiecharts.org/ ,其波形只是预建的和由右至左刚流。

  import java.util.concurrent.ConcurrentLinkedQueue;
  import java.util.concurrent.ExecutorService;
  import java.util.concurrent.Executors;
  import java.util.logging.Level;
  import java.util.logging.Logger;
  import javafx.animation.Animation;
  import javafx.animation.KeyFrame;
  import javafx.animation.SequentialTransition;
  import javafx.animation.Timeline;
  import javafx.application.Application;
  import javafx.event.ActionEvent;
  import javafx.event.EventHandler;
  import javafx.scene.Group;
  import javafx.scene.Scene;
  import javafx.scene.chart.AreaChart;
  import javafx.scene.chart.NumberAxis;
  import javafx.scene.chart.XYChart.Series;
  import javafx.stage.Stage;
  import javafx.util.Duration;

  /**
   * A chart that fills in the area between a line of data points and the axes.
   * Good for comparing accumulated totals over time.
   *
   * @see javafx.scene.chart.Chart
   * @see javafx.scene.chart.Axis
   * @see javafx.scene.chart.NumberAxis
   * @related charts/line/LineChart
   * @related charts/scatter/ScatterChart
   */
  public class AreaChartSample extends Application {
      private Series series;
      private int xSeriesData=0;
      private ConcurrentLinkedQueue<Number> dataQ = new ConcurrentLinkedQueue<Number>();
      private ExecutorService executor;
      private AddToQueue addToQueue;
      private Timeline timeline2;
      private SequentialTransition animation;

      private void init(Stage primaryStage) {
          Group root = new Group();
          primaryStage.setScene(new Scene(root));

          NumberAxis xAxis = new NumberAxis();
          xAxis.setAutoRanging(true);

          NumberAxis yAxis = new NumberAxis();
          yAxis.setAutoRanging(true);

          //-- Chart
          final AreaChart<Number,Number> sc = new AreaChart<Number,Number>(xAxis,yAxis);
          sc.setId("liveAreaChart");
          sc.setTitle("Animated Area Chart");

          //-- Chart Series
          series=new AreaChart.Series<Number,Number>();
          series.setName("Area Chart Series");
          series.getData().add(new AreaChart.Data<Number, Number>(5d, 5d));
          sc.getData().add(series);


          root.getChildren().add(sc);



      }

      @Override public void start(Stage primaryStage) throws Exception {
          init(primaryStage);
          primaryStage.show();

          //-- Prepare Executor Services
          executor = Executors.newCachedThreadPool();
          addToQueue=new AddToQueue();
          executor.execute(addToQueue);


          //-- Prepare Timeline
          prepareTimeline();


      }

      public static void main(String[] args) { launch(args); }

      private class AddToQueue extends Thread {

          public void run(){

          try {
              Thread.currentThread().setName(Thread.currentThread().getId()+"-DataAdder");
              //-- Add Random numbers to Q
              dataQ.add(Math.random());
              Thread.sleep(50);

              executor.execute(addToQueue);

          } catch (InterruptedException ex) {
              Logger.getLogger(AreaChartSample.class.getName()).log(Level.SEVERE, null, ex);
          }

          }
      }

      //-- Timeline gets called in the JavaFX Main thread
      private void prepareTimeline(){
          //-- Second slower timeline
          timeline2 = new Timeline();
          //-- This timeline is indefinite.  
          timeline2.setCycleCount(Animation.INDEFINITE);

          timeline2.getKeyFrames().add(
                  new KeyFrame(Duration.millis(100), new EventHandler<ActionEvent>() {
                      @Override public void handle(ActionEvent actionEvent) {
                          addDataToSeries();

                      }
                  })
          );

          //-- Set Animation- Timeline is created now.
          animation = new SequentialTransition();
          animation.getChildren().addAll(timeline2);
          animation.play();        

      }

      private void addDataToSeries(){

          for(int i=0;i<20;i++){ //-- add 20 numbers to the plot
              if(dataQ.isEmpty()==false)   {
                  series.getData().add(new AreaChart.Data(xSeriesData++,dataQ.remove()));

                  //-- Get rid of a bunch from the chart
                  if (series.getData().size() > 1000) {
                      series.getData().remove(0,999);
                  }

              }
              else{
                  return;
              }
          }
      }


  }

Answer 1:

作为jewelsea在他/她的评论中指出:

这个问题是交叉张贴(以及回答得很好)上的Oracle JavaFX的论坛主题 。

总之,该解决方案包括在:

  • 车削动画关闭,因为它被设计用于较慢的变化的数据,使得其在到达动画;
  • 改变TimelineAnimationTimer如期望更新图表的每一帧,以便保持与输入数据同步和尽可能顺利移动;
  • 固定螺纹为OP并不需要使用执行人时延长线程。 更改执行服务的创建。


Answer 2:

一个大的性能自动下降可能来自于你的数据收集。 没有理由要使用ExecutorService和时间可持续增加新的Threads通过它在执行顺序获得重复数据增加。 你可以解决单个线程读取/接收数据,并将其添加到队列中,通过调用启动addToQueue.start() 对于正常的工作,你想有一个循环不间断的线程中,在每个迭代结束的延迟运行。



文章来源: How to Chart real time streaming data using AreaChart in JAVAFX 2 - Concurrency, Animation, Charting