JavaFXで別スレッドからGUIを操作する方法

JavaFXで作成したスレッド上でGUIを操作しようとするとエラーが発生します。
このエラーを回避する方法としてPlatform.runLaterを使用する方法を紹介します。

別スレッドでGUIを操作するとエラーが発生する

メインスレッドとは別のスレッドでGUIを操作する処理を行うと以下のようなエラーが発生します。

Exception in thread "Thread-5" java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-5

Platform.runLater を使用する方法

Pralform.runLater関数を使用することで、別スレッドからGUI等を操作する処理を行ってもエラーが出ず正常に処理することができ、JavaFXのメインスレッド上で処理することができます。

Platform.runLater( () ->
{
  // GUI操作処理
});

サンプルプログラム

import javafx.application.Application;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.FlowPane;
import javafx.scene.paint.Color;


public class Main extends Application {
	@Override
	public void start(Stage primaryStage) {
		try {
			FlowPane root = new FlowPane();

			Label label = new Label("テキスト変更前");
			root.getChildren().add(label);

			// ラベルの文字を3秒後に変更するタスクを作成
			Task<Boolean> task   = new Task<Boolean>()
			{
				@Override
				protected Boolean call() throws Exception
				{
					Thread.sleep(3000);		// 重い処理の代わり

					// Platform.runLater を使用するとメインスレッド上で処理されます。
					Platform.runLater( () ->
					{
						label.setText( "テキスト変更後" ) ;
					});

					// ↓ Platform.runLaterを使わずラベルを変更しようとするとエラーになる
					// label.setText( "テキスト変更後" );
					return true;
				}
			};

			Button btn = new Button("ボタン");

			// ボタンクリックイベントを設定
			btn.setOnMouseClicked(ev ->
			{
				// タスクを実行
				Thread t = new Thread( task );
				t.setDaemon( true );
				t.start();
			});

			root.getChildren().add(btn);

			// シーンを作成
			Scene scene = new Scene(root, 800, 400, Color.BLACK);
			scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());

			// ステージにシーンを登録し、ウィンドウを表示する。
			primaryStage.setScene(scene);
			primaryStage.setTitle("JavaFX Thread からGUI操作する");
			primaryStage.show();
		} catch(Exception e) {
			e.printStackTrace();
		}
	}

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

}


参考:
https://docs.oracle.com/javase/jp/8/javafx/interoperability-tutorial/concurrency.htm#JFXIP546
https://docs.oracle.com/javase/jp/8/javafx/api/javafx/application/Platform.html

コメント

タイトルとURLをコピーしました