0
Exception in thread "Thread-5" java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-5
    at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:279)
    at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:423)
    at javafx.scene.Parent$2.onProposedChange(Parent.java:367)
    at com.sun.javafx.collections.VetoableListDecorator.setAll(VetoableListDecorator.java:113)
    at com.sun.javafx.collections.VetoableListDecorator.setAll(VetoableListDecorator.java:108)
    at com.sun.javafx.scene.control.skin.LabeledSkinBase.updateChildren(LabeledSkinBase.java:575)
    at com.sun.javafx.scene.control.skin.LabeledSkinBase.handleControlPropertyChanged(LabeledSkinBase.java:204)
    at com.sun.javafx.scene.control.skin.LabelSkin.handleControlPropertyChanged(LabelSkin.java:49)
    at com.sun.javafx.scene.control.skin.BehaviorSkinBase.lambda$registerChangeListener$70(BehaviorSkinBase.java:197)
    at com.sun.javafx.scene.control.MultiplePropertyChangeListenerHandler$1.changed(MultiplePropertyChangeListenerHandler.java:55)
    at javafx.beans.value.WeakChangeListener.changed(WeakChangeListener.java:89)
    at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:182)
    at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:81)
    at javafx.beans.property.StringPropertyBase.fireValueChangedEvent(StringPropertyBase.java:103)
    at javafx.beans.property.StringPropertyBase.markInvalid(StringPropertyBase.java:110)
    at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:144)
    at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:49)
    at javafx.beans.property.StringProperty.setValue(StringProperty.java:65)
    at javafx.scene.control.Labeled.setText(Labeled.java:145)
    at edu.controller.GUIController.statusShow(GUIController.java:443)
    at edu.controller.GUIController.lambda$7(GUIController.java:214)
    at java.lang.Thread.run(Unknown Source)

I'm making a text editor and I want to add a status-bar in the footer that tells user different tips after a few seconds and I'm facing this error when I try to set text on the label but when I try to set that text on console that works fine.

new Thread(()->{
    statusBarShow();
}).start();

private void statusBarShow(){
try {
    statusLable.setText("Tip 1");
    Thread.sleep(2000);
    statusLable.setText("Tip 2");
    Thread.sleep(2000);
    statusLable.setText("Tip 3");
    Thread.sleep(2000);
    statusLable.setText("Tip 4");
    Thread.sleep(2000);
    statusLable.setText("Tip 5");
    Thread.sleep(2000);
} catch (Exception e) {
    e.printStackTrace();
}
}
UMEGS Hamza
  • 23
  • 1
  • 7

1 Answers1

3

The only thread that is allowed to modify the JavaFX GUI is the JavaFX thread. If any other thread modifies the UI, you will get an exception.

The most simple answer to this common problem is to wrap the code that changes the GUI in Platform.runLater(). Be careful not to put any sleeps in the runlater(), as they will cause the JavaFX GUI thread to sleep, which will cause your program to freeze for the duration of the sleep.

Platform.runLater() has the following javadoc:

Run the specified Runnable on the JavaFX Application Thread at some unspecified time in the future. This method, which may be called from any thread, will post the Runnable to an event queue and then return immediately to the caller. The Runnables are executed in the order they are posted. A runnable passed into the runLater method will be executed before any Runnable passed into a subsequent call to runLater. If this method is called after the JavaFX runtime has been shutdown, the call will be ignored: the Runnable will not be executed and no exception will be thrown.

NOTE: applications should avoid flooding JavaFX with too many pending Runnables. Otherwise, the application may become unresponsive. Applications are encouraged to batch up multiple operations into fewer runLater calls. Additionally, long-running operations should be done on a background thread where possible, freeing up the JavaFX Application Thread for GUI operations.

This method must not be called before the FX runtime has been initialized. For standard JavaFX applications that extend Application, and use either the Java launcher or one of the launch methods in the Application class to launch the application, the FX runtime is initialized by the launcher before the Application class is loaded. For Swing applications that use JFXPanel to display FX content, the FX runtime is initialized when the first JFXPanel instance is constructed. For SWT application that use FXCanvas to display FX content, the FX runtime is initialized when the first FXCanvas instance is constructed.

I don't think your code is structured in the best way to accomplish this task, but a very simple solution is the following:

new Thread(()->{
    statusBarShow();
}).start();

private void statusBarShow(){
    try {
        Platform.runLater(()->statusLable.setText("Tip 1"));
        Thread.sleep(2000);
        Platform.runLater(statusLable.setText("Tip 2"));
        Thread.sleep(2000);
        Platform.runLater(statusLable.setText("Tip 3"));
        Thread.sleep(2000);
        Platform.runLater(statusLable.setText("Tip 4"));
        Thread.sleep(2000);
        Platform.runLater(statusLable.setText("Tip 5"));
    } catch (Exception e) {
        e.printStackTrace();
    }
}

A better solution to your problem may be to use an AnimationTimer.

Here is a useful thread on how to accomplish that: JavaFX periodic background task

Grumblesaurus
  • 3,021
  • 3
  • 31
  • 61