2016-06-25 11 views
1

1つのスレッドで実行される複数のタスクがあります。それはうまく動作し、対応するtablecellプログレスバーは期待通りに更新されます。私がそれらを実行する前に、UI上のすべてのボタンを無効にしたい(これは私のenableControlsメソッドです)。最後のタスクが実行されたら、ボタンを再び有効にします。JavaFX:ExecutorService.awaitTerminationは、バインドされたプロパティを通じてUIを更新しません。

シャットダウンを追加して、awaitTerminationを追加しようとしましたが、すべてが完了したとき以外はUIが更新されません。

ExecutorService exec = Executors.newSingleThreadExecutor(r -> { 
    Thread t = new Thread(r); 
    t.setDaemon(true); 
    return t ; 
}); 

enableControls(false); 
for (Segment seg : selectedItems) { 
    Task loadingTask = new Task<Void>() { 
     @Override 
     public Void call() { 
      for (int i = 1; i <= 100; i++) { 
       updateProgress(i, 100); 
       try { 
        Thread.sleep(100); 
       } catch (InterruptedException e) { 
        e.printStackTrace(); 
       } 
      } 
      return null; 
     } 
    }; 
    seg.progressBarProperty().bind(loadingTask.progressProperty()); 
    loadingTask.setOnSucceeded(e -> seg.progressBarProperty().unbind()); 
    loadingTask.setOnFailed(e -> { 
     loadingTask.getException().printStackTrace(); 
     enableControls(true); 
    }); 
    exec.submit(loadingTask); 
} 
exec.shutdown(); 
try { 
    exec.awaitTermination(1, TimeUnit.MINUTES); 
} catch (InterruptedException e) { 
    e.printStackTrace(); 
} 
System.out.println("test"); 
enableControls(true); 

は、代替的に、私は現在のタスクが最後であるかどうかを決定すると考える(ループでこれ以上SEG)とsetOnSucceededにUIコントロールを再度有効。最小限に抑えるためにエレガントではない。

UIが(バウンド値を介して)更新されない理由 - これはawaitTerminationの制限ですか?

答えて

1

awaitTerminationはブロッキング呼び出しですが、JavaFXアプリケーションスレッドで呼び出すべきではありません。タスクの実行を管理し、他のタスクの終了を待つ新しいタスクを作成するだけです。新しいタスクへのバインディングを使用して、実行中のUIの一部を無効にすることができます。

run

import javafx.application.*; 
import javafx.concurrent.Task; 
import javafx.event.Event; 
import javafx.geometry.Insets; 
import javafx.scene.*; 
import javafx.scene.control.*; 
import javafx.scene.layout.VBox; 
import javafx.stage.Stage; 

import java.util.ArrayList; 
import java.util.List; 
import java.util.concurrent.*; 

public class Waiter extends Application { 

    private static final int N_TASKS = 3; 
    private static final int TOTAL_TASK_EXECUTION_TIME_LIMIT_SECS = 20; 

    private final VBox layout = new VBox(10); 

    @Override 
    public void start(Stage stage) throws Exception { 
     Button run = new Button("Run"); 
     run.setOnAction(this::work); 

     layout.getChildren().add(run); 
     layout.setPadding(new Insets(10)); 
     layout.setPrefSize(100, 150); 

     stage.setScene(new Scene(layout)); 
     stage.show(); 
    } 

    private void work(Event event) { 
     Node controlNode = (Node) event.getSource(); 

     ExecutorService exec = Executors.newSingleThreadExecutor(r -> { 
      Thread t = new Thread(r, "load"); 
      t.setDaemon(true); 
      return t; 
     }); 

     ExecutorService monitor = Executors.newSingleThreadExecutor(r -> { 
      Thread t = new Thread(r, "monitor"); 
      t.setDaemon(true); 
      return t; 
     }); 

     final List<LoadingTask> tasks = new ArrayList<>(); 
     for (int j = 0; j < N_TASKS; j++) { 
      LoadingTask loadingTask = new LoadingTask(); 
      ProgressBar progressBar = new ProgressBar(); 
      progressBar.progressProperty().bind(loadingTask.progressProperty()); 
      layout.getChildren().add(progressBar); 

      tasks.add(loadingTask); 
     } 

     Task<Void> taskRunner = new Task<Void>() { 
      @Override 
      protected Void call() throws Exception { 
       tasks.forEach(exec::submit); 
       exec.shutdown(); 
       boolean cleanShutdown = exec.awaitTermination(
         TOTAL_TASK_EXECUTION_TIME_LIMIT_SECS, 
         TimeUnit.SECONDS 
       ); 
       if (!cleanShutdown) { 
        exec.shutdownNow(); 
       } 

       Platform.runLater(() -> 
         layout.getChildren() 
           .removeIf(
             node -> node instanceof ProgressBar 
           ) 
       ); 

       return null; 
      } 
     }; 
     controlNode.disableProperty().bind(taskRunner.runningProperty()); 
     monitor.submit(taskRunner); 
     monitor.shutdown(); 
    } 

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

    private static class LoadingTask extends Task<Void> { 
     @Override 
     public Void call() { 
      for (int i = 1; i <= 100; i++) { 
       if (isCancelled() || Thread.interrupted()) { 
        break; 
       } 
       updateProgress(i, 100); 
       try { 
        Thread.sleep(20); 
       } catch (InterruptedException e) { 
        Thread.currentThread().interrupt(); 
       } 
      } 
      return null; 
     } 
    } 
} 

私はあなたが親ノードを無効にした場合、それは再帰的に親のすべての子を無効にします段階

上のすべてのボタンやテキストフィールドを無効にする必要があります。だからシーンルートのdisablePropertyを結合し、シーン内のすべてを無効にします。

controlNode.getScene().getRoot() 
    .disableProperty().bind(taskRunner.runningProperty()); 

親ステージ上で入力を無効にする他の方法は、所有者を設定してWINDOW_MODALまたはAPPLICATION_MODALダイアログを使用することです。たとえば、ControlsFX ProgressDialogです。

+0

完璧!私が持っていた1つの質問はrunningPropertyにバインドするという提案でした:呼び出しボタンだけを無効にする必要がある場合はうまくいきますが、私の場合はステージ上のすべてのボタンとテキストフィールドを無効にする必要があります。私はそれらをすべてグループ化し、disabledPropertyを使用する方法を見つけることができませんでした。代わりに、タスクを実行する前後に、すべてのsetDisableまたはsetEditableを切り替えるメソッドを使用しました。 –

関連する問題