2016-08-20 1 views
1

3層(gui-> services-> dao)のデスクトップアプリケーション(JavaFX + Spring 4)があります。 私のサービス層(fx依存関係なし)で実現した私のメソッドの中には、長時間の計算があります。私はguiでの進捗状況を報告したいと思います。Spring Listenerメソッドからアクティブ化されたJavaFXプログレスバーコンポーネント

しかし、問題があります。私のサービス層はguiに直接アクセスできず、IO操作のログを作り、それを要約するだけです。 GUI層は、中間操作の進捗状況を知らない。

一般的な進捗コンポーネントを作成しようとしています。リスナーメソッドがidのGUIレイヤーに配置されたSpring Beanとして実現できます。私のサービスはApplicationEventを公開することができ、guiの進捗コンポーネントを介して受け取ることができます。イベントを取得したら、進行状況バーをアクティブにして次のイベントを報告する必要があります。

私はProgressTaskを起動し、progressPropertyを私のGUIプログレスバーコントロールでバインドしています。

私のProgressTaskの呼び出し方法は実際のカウンタがその最大ステップ数より少ない間に実行されます。

は私のコンポーネントがあります:

@Component 
@Controller 
public class ProgressBarComponent { 

    @FXML 
    private ProgressBar progressBar; 



    private ProgressTask actTask; 

    @FXML 
    public void initialize(){ 
    } 

    private void initProgressBar(int aMax){ 
     actTask=new ProgressTask(); 
     actTask.initState(aMax); 
     progressBar.progressProperty().bind(actTask.progressProperty()); 
    new Thread(actTask).start(); 
    } 

    @EventListener 
    public void handleProgressEvent(ProgressEvent event) { 
     if(event.getGetActStep()==0){ 
      initProgressBar(event.getMaxStep()); 
     } 
     actTask.changeState(event.getGetActStep()); 
    } 


    class ProgressTask extends Task<Object>{ 
     private int max=100; 
     private int act=0; 
     private volatile boolean cancelled; 

     public void initState(int aMax){ 
      this.max=aMax; 
     } 

     public void changeState(int newVal){ 
      act=newVal; 
      updateProgress(act, max); 
      updateMessage("progress: " + act); 

      try { 
       Thread.sleep(1000); 
      } catch (InterruptedException e) { 
       throw new IllegalArgumentException(e); 
      } 
     } 

     @Override 
     protected Object call() throws Exception { 
      while(act<max){ 
      } 
      return null; 
     } 

    } 

} 

しかし、それは動作しません。 プログレスバーコントロールがアクティブになっていますが、一度に100%の値があります。

プログレスバーFXコントロールの例はたくさんありますが、それらのすべてはメインロジックがcall()メソッドProgressTaskの中で計算されると仮定しています。私の場合、それは不可能です。

ProgressBarコンポーネントはリスナーメソッドからアクティブにできますか?はいの場合、どのコードをProgressTask.call()メソッドに含める必要がありますか?

+0

私はこれが動作するようになっている方法を理解するために春のイベントについて十分に知らないかもしれないが、 'ProgressEvent'とは何か、そしてどのようにあなたのサービスがこれを呼び出しています? 'Task'の全目的はバックグラウンド作業を行うことです。それが他の場所で行われている場合、' Task'実装のポイントが何であるかは本当に明確ではありません。しかし、最低限、「max」と「act」は1つのスレッドで書かれ、別のスレッドから読み込まれるため、「volatile」でなければなりません。 'call()'メソッドのbusy-waitループは良い考えではありません。 –

+0

おそらく実際の作業は、FXアプリケーションスレッドではなく、バックグラウンドスレッドで実行されています... –

答えて

2

Taskのポイントは、バックグラウンドスレッドで実行する必要がある機能を定義することです。したがって、この機能が既に他の場所に実装されている場合、タスクは必要ありません。サービスは(FXのアプリケーションスレッド上すなわちない)バックグラウンドスレッドで呼び出されていると仮定すると、

、あなただけ行うことができる必要があります:

ProgressViewer:

@Component 
@Controller 
public class ProgressBarComponent { 

    private int max ; 

    @FXML 
    private ProgressBar progressBar; 

    @FXML 
    public void initialize(){ 
    } 

    private void initProgressBar(int aMax){ 
     this.max = aMax ; 
    } 

    @EventListener 
    public void handleProgressEvent(ProgressEvent event) { 
     if(event.getGetActStep()==0){ 
      Platform.runLater(() -> initProgressBar(event.getMaxStep())); 
     } 
     Platform.runLater(() -> 
      progressBar.setProgress(1.0 * event.getGetActStep()/max)); 
    } 
} 

ここで完全SSCCEです。 Javaの:

package springevent; 

import org.springframework.context.event.EventListener; 

import javafx.application.Platform; 
import javafx.geometry.Insets; 
import javafx.scene.Scene; 
import javafx.scene.control.ProgressBar; 
import javafx.scene.layout.StackPane; 
import javafx.stage.Stage; 

public class ProgressViewer { 

    private Stage stage = new Stage(); 
    private Scene scene ; 
    private ProgressBar progressBar ; 

    public ProgressViewer() { 
     progressBar = new ProgressBar(); 
     StackPane root = new StackPane(progressBar); 
     root.setPadding(new Insets(20)); 
     scene = new Scene(root); 
     stage.setScene(scene); 
    } 

    @EventListener 
    public void progressUpdate(ProgressEvent event) { 
     if (event.getCurrent() < event.getMax()) { 
      Platform.runLater(stage::show); 
     } else { 
      Platform.runLater(stage::hide); 
     } 
     final double progress = 1.0 * event.getCurrent()/event.getMax() ; 
     Platform.runLater(() -> progressBar.setProgress(progress)); 
    } 
} 

Service.java:

package springevent; 

import javax.inject.Inject; 

import org.springframework.context.ApplicationContext; 

public class Service { 

    @Inject 
    private ApplicationContext context ; 

    public void runService() { 
     context.publishEvent(new ProgressEvent(0, 100)); 
     try { 
      for (int i = 1 ; i <= 100; i++) { 
       Thread.sleep(200); 
       context.publishEvent(new ProgressEvent(i, 100)); 
      } 
     } catch (InterruptedException e) { 
      Thread.currentThread().interrupt(); 
     } 
    } 
} 

ProgressEvent.java

package springevent; 

public class ProgressEvent { 

    private final int current ; 
    private final int max ; 

    public ProgressEvent(int current, int max) { 
     this.current = current ; 
     this.max = max ; 
    } 

    public int getCurrent() { 
     return current ; 
    } 

    public int getMax() { 
     return max ; 
    } 
} 

MainController.java

package springevent; 

import java.util.concurrent.Executor; 

import javax.inject.Inject; 

import org.springframework.context.event.EventListener; 

import javafx.fxml.FXML; 
import javafx.scene.control.Button; 

public class MainController { 

    @Inject 
    private Executor exec ; 

    @Inject 
    private Service service ; 

    @FXML 
    private Button startButton ; 

    public void startProcess() { 
     exec.execute(service::runService); 
    } 

    @EventListener 
    public void progressUpdate(ProgressEvent event) { 
     startButton.setDisable(event.getCurrent() < event.getMax()); 
    } 

} 

Main.fxml

<?xml version="1.0" encoding="UTF-8"?> 

<?import javafx.scene.layout.StackPane?> 
<?import javafx.scene.control.Button?> 
<?import javafx.geometry.Insets?> 

<StackPane xmlns:fx="http://javafx.com/fxml/1" fx:controller="springevent.MainController"> 
    <padding> 
     <Insets top="20" bottom="20" left="20" right="20"/> 
    </padding> 
    <Button fx:id="startButton" text="Start process" onAction="#startProcess" /> 
</StackPane> 

メイン。Javaの

package springevent; 

import java.io.IOException; 

import org.springframework.context.annotation.AnnotationConfigApplicationContext; 
import org.springframework.context.support.AbstractApplicationContext; 

import javafx.application.Application; 
import javafx.fxml.FXMLLoader; 
import javafx.scene.Scene; 
import javafx.stage.Stage; 

public class Main extends Application { 

    private AbstractApplicationContext context ; 

    @Override 
    public void start(Stage primaryStage) throws IOException { 
     context = new AnnotationConfigApplicationContext(AppConfig.class); 
     FXMLLoader loader = new FXMLLoader(getClass().getResource("Main.fxml")); 
     loader.setControllerFactory(context::getBean); 
     Scene scene = new Scene(loader.load()); 
     primaryStage.setScene(scene); 
     primaryStage.show(); 
    } 

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

とAppConfig.java:

package springevent; 

import java.util.concurrent.Executor; 
import java.util.concurrent.Executors; 

import org.springframework.context.annotation.Bean; 
import org.springframework.context.annotation.Configuration; 

@Configuration 
public class AppConfig { 

    @Bean 
    public Service service() { 
     return new Service(); 
    } 

    @Bean 
    public MainController mainController() { 
     return new MainController(); 
    } 

    @Bean 
    public ProgressViewer progressViewer() { 
     return new ProgressViewer(); 
    } 

    @Bean 
    public Executor exec() { 
     return Executors.newCachedThreadPool(runnable -> { 
      Thread t = new Thread(runnable); 
      t.setDaemon(true); 
      return t ; 
     }); 
    } 
} 
+0

ありがとう、あなたのソリューションは動作します:) – user2427814

関連する問題