如何在显示ProgressIndicator时打印节点?

时间:2014-10-31 11:31:44

标签: java multithreading javafx task javafx-8

如何在JavaFx中实现依赖于GUI组件的任务?

目标是从各种SceneGraph组件生成报告,同时向用户显示一些ProgressIndicator。

我们需要

  1. 生成某个节点的快照
  2. 将其转换为BufferedImage
  3. 将图像传递给报告生成引擎
  4. 由于除非从FxThread无法更新GUI,否则问题将变为:

    如何显示长时间运行的GUI操作的进度指示器?

    我的第一种方法是简单地将逻辑放入Task显然失败,导致执行snapshot method时出现java.lang.IllegalStateException: Not on FX application。 它包含一个Fx thread check,我认为这意味着暂时不能从场景中删除节点。

    使用Platform.runLater也不够用,因为它只能用于写入数据到GUI,但不能用于读取。 (或者可以吗?)

    在短时间内,我使用SwingWorker进行了评估,但发现与任务相比,它没有任何好处。

    即使我们忽略读/写问题一秒钟。考虑这个例子:

    package com.isp.lpt.progress;
    
    import java.util.Random;
    import java.util.concurrent.Executors;
    import java.util.stream.IntStream;
    import java.util.stream.Stream;
    
    import javafx.application.Application;
    import javafx.application.Platform;
    import javafx.beans.property.LongProperty;
    import javafx.beans.property.SimpleLongProperty;
    import javafx.concurrent.Task;
    import javafx.scene.Group;
    import javafx.scene.Scene;
    import javafx.scene.control.ProgressIndicator;
    import javafx.scene.effect.DropShadow;
    import javafx.scene.paint.Color;
    import javafx.scene.shape.Circle;
    import javafx.stage.Stage;
    
    public class GuiTaskExample extends Application
    {
      private static final Random RANDOM = new Random();
      private final static int    WIDTH  = 100;
      private final static int    HEIGHT = 100;
    
      @Override
      public void start( final Stage primaryStage )
      {
        final Group root = new Group();
        final Group circleGroup = new Group();
        final ProgressIndicator indicator = new ProgressIndicator();
        root.getChildren().add( circleGroup );
        root.getChildren().add( indicator );
        final Scene scene = new Scene( root );
    
        final GuiTaskExample that = this;
        final Task<Void> task = new Task<Void>()
        {
          @Override
          protected Void call() throws Exception
          {
            try (final IntStream intStream = RANDOM.ints( 1, HEIGHT ))
            {
              final long maxSize = HEIGHT * WIDTH;
              final Stream<Circle> circles = intStream.mapToObj( that::createCircleThing ).limit( maxSize );
    
              //local variables must be final for lambdas
              final LongProperty counter = new SimpleLongProperty( 1 );
    
              circles.forEach( circle ->
              {
                updateProgress( counter.get(), maxSize );
                counter.set( counter.get() + 1 );
                Platform.runLater( ( ) -> circleGroup.getChildren().add( circle ) );
              } );
    
              return null;
            }
          }
        };
    
        indicator.progressProperty().bind( task.progressProperty() );
    
        task.setOnSucceeded( done ->
        {
          root.getChildren().remove( indicator );
          primaryStage.sizeToScene();
        } );
    
        task.exceptionProperty().addListener( ( observable, oldValue, newValue ) -> newValue.printStackTrace() );
        Executors.newSingleThreadExecutor().execute( task );
    
        primaryStage.setScene( scene );
        primaryStage.show();
      }
    
      private Circle createCircleThing( final int radius )
      {
        final Circle circle = new Circle();
        System.out.println( radius );
        circle.setRadius( RANDOM.nextInt( radius ) );
        circle.setCenterX( circle.getRadius() + RANDOM.nextInt( WIDTH ) );
        circle.setCenterY( circle.getRadius() + RANDOM.nextInt( HEIGHT ) );
        circle.setStrokeWidth( RANDOM.nextDouble() * 4 );
        circle.setStroke( randomPaint() );
        circle.setFill( randomPaint() );
        circle.setEffect( new DropShadow( RANDOM.nextInt( 12 ), randomPaint() ) );
        return circle;
      }
    
      private Color randomPaint()
      {
        return Color.rgb( RANDOM.nextInt( 255 ), RANDOM.nextInt( 255 ), RANDOM.nextInt( 255 ),
            RANDOM.nextDouble() );
      }
    
      public static void main( final String[] args )
      {
        launch( args );
      }
    }
    

    该示例在任务中创建了大量GUI元素,然后将其绘制到屏幕上,但(至少在我的机器上)绘制到屏幕上的时间最长,我想隐藏在进度指示器后面

    所以问题变成了: 如何在JavaFx中绘制到屏幕外缓冲区?

    对于在Fx中长时间运行GUI操作和/或使用快照的特定用例的一般情况,我愿意接受建议。

1 个答案:

答案 0 :(得分:0)

对于报告部分,此answer解决了您的问题。引用的链接已中断,但您可以找到它here

基本上,他使用三个子任务,一个用于在屏幕外场景图中创建图表(或您需要的任何其他Node)。其他将每个图表的快照拍摄到JavaFX图像,第三个图像将JavaFX图像导出到文件。或者,您也可以将这些图像传递给报表引擎。