How do I put something on an FX thread?



I’m new to JavaFX (trying to move from Swing), and am trying to make a very basic window first. However, I keep getting the following runtime exception:

Exception in thread "main" java.lang.IllegalStateException: Not on FX application thread; currentThread = main
    at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:210)
    at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:393)
    at javafx.scene.Scene.<init>(Scene.java:374)
    at javafx.scene.Scene.<init>(Scene.java:232)
    at bht.tools.util.upd.TestDialog.initGUI(TestDialog.java:39)
    at bht.tools.util.upd.TestDialog.<init>(TestDialog.java:24)
    at bht.tools.util.upd.TestDialog.main(TestDialog.java:52)
package bht.tools.util.upd;

import java.awt.Window;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.GridPane;
import javax.swing.JDialog;

/**
 * TestDialog is copyright Blue Husky Programming ©2014 GPLv3 <hr/>
 * 
 * @author Kyli of Blue Husky Programming
 * @version 1.0.0
 *      - 2014-09-30 (1.0.0) - Kyli created TestDialog
 * @since 2014-09-30
 */
public class TestDialog extends JDialog
{
    public TestDialog(Window owner)
    {
        super(owner);
        initGUI();
    }

    private JFXPanel holder;
    private Scene interior;
    private ProgressBar progressBar;
    private GridPane root;
    private void initGUI()
    {
        {
            holder = new JFXPanel();
            setContentPane(holder);
            root = new GridPane();
            interior = holder.getScene();
            if (interior == null)
                holder.setScene(interior = new Scene(root));
            interior.setRoot(root);
        }
        {
            progressBar = new ProgressBar();
            progressBar.setProgress(-1);
            root.add(progressBar, 0, 1);
        }
        pack();
    }

    public static void main(String[] args)
    {
        new TestDialog(null).setVisible(true);
    }
}

However, looking at the JFXPanel source code, the constructor calls its initFX() method, which initializes the FX application thread. Why, then, am I getting this error, and how do I fix it?

Answer

The JFXPanel initializes the FX Application Thread, so it is now running; however your code is not being executed on that thread. In order to execute code on the FX Application Thread, you can call Platform.runLater(...) which is roughly analogous to SwingUtilities.invokeLater(...) in Swing and AWT applications.

This is taken more or less straight from the tutorial, but for your example code you would do

package bht.tools.util.upd;

import java.awt.Window;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.GridPane;
import javax.swing.JDialog;

/**
 * TestDialog is copyright Blue Husky Programming ©2014 GPLv3 <hr/>
 * 
 * @author Kyli of Blue Husky Programming
 * @version 1.0.0
 *      - 2014-09-30 (1.0.0) - Kyli created TestDialog
 * @since 2014-09-30
 */
public class TestDialog extends JDialog
{
    public TestDialog(Window owner)
    {
        super(owner);
        initGUI();
    }

    private JFXPanel holder;
    private Scene interior;
    private ProgressBar progressBar;
    private GridPane root;
    private void initGUI()
    {
        holder = new JFXPanel();
        setContentPane(holder);
        Platform.runLater( () -> initJFXPanel(holder) );
        pack();
    }

    private void initJFXPanel(JFXPanel holder) {
        root = new GridPane();
        interior = holder.getScene();
        if (interior == null)
            holder.setScene(interior = new Scene(root));
        interior.setRoot(root);
        progressBar = new ProgressBar();
        progressBar.setProgress(-1);
        root.add(progressBar, 0, 1);
    }

    public static void main(String[] args)
    {
        new TestDialog(null).setVisible(true);
    }
}

Note that you’re breaking Swing’s threading rules too, though I think (it’s been a while now since I programmed using Swing) that Swing has been bullet-proofed to ensure code like that works. But your main method should really be

public static void main(String[] args)
{
    SwingUtilities.invokeLater(() -> new TestDialog(null).setVisible(true));
}


Source: stackoverflow