0

When i am using JavaFX, the sleep function won't work accordingly. Like in this code:

public class Controller {

@FXML private Label label;
@FXML private Button b1;

public void write() throws InterruptedException
{
    label.setText("FIRST TIME");
    for(int i=1;i<=5;i++)
    {
        System.out.println("Value "+i);
        label.setText("Value "+i);
        Thread.sleep(2000);
    }
    label.setText("LAST TIME");
}

when the Button b1 is pressed the write function is called. Now, in the console "Value + i" is being printed after 2 seconds. But that time the text of Label l1 doesn't change and finally it only changes to "LAST TIME". What is wrong in here ?

Zarif
  • 445
  • 5
  • 12
  • 3
    Because JavaFX, like most GUI frameworks, is single threaded, meaning that any operation which blocks their event thread will stop them from updating the UI until such time that they become unblocked (ie execution leaves the method) - and before you crack open a new thread, it's also not thread safe – MadProgrammer Jan 17 '19 at 05:15
  • 3
    You should by reading [https://docs.oracle.com/javase/8/javafx/interoperability-tutorial/fx_concurrency.htm](https://docs.oracle.com/javase/8/javafx/interoperability-tutorial/concurrency.htm) to get a better understanding. JavaFX is animation based framework (animation is key part of it's design) and it has a number of useful APIs to help facilitate it - maybe you should look at something like [JavaFX periodic background task](https://stackoverflow.com/questions/9966136/javafx-periodic-background-task) which is a possible duplicate to your question – MadProgrammer Jan 17 '19 at 05:17
  • I would recommend to create a new thread being the timer and delegating the update of Label object to JavaFx main thread by calling `Platform.runLater(Runnable runnable);` – Embid123 Jan 17 '19 at 06:02

2 Answers2

5

After having read the links proposed in the comments, you may want to remove the long process (delay) from the fx thread.
You can do it by invoking another thread :

public void write() {

    label.setText("FIRST TIME");

    new Thread(()->{ //use another thread so long process does not block gui
        for(int i=1;i<=6;i++)   {
            String text;
            if(i == 6 ){
                text = "LAST TIME";
            }else{
                 final int j = i;
                 text = "Value "+j;
            }

            //update gui using fx thread
            Platform.runLater(() -> label.setText(text));
            try {Thread.sleep(2000);} catch (InterruptedException ex) { ex.printStackTrace();}
        }

    }).start();
}

Or better use fx animation tools like :

private int i = 0; // a filed used for counting 

public void write() {

    label.setText("FIRST TIME");

    PauseTransition pause = new PauseTransition(Duration.seconds(2));
    pause.setOnFinished(event ->{
        label.setText("Value "+i++);
        if (i<=6) {
            pause.play();
        } else {
            label.setText("LAST TIME");
        }
    });
    pause.play();
}
c0der
  • 18,467
  • 6
  • 33
  • 65
3

I would try to create new thread used for time delay.

package sample;

import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;

public class Controller implements Runnable{


    private volatile boolean isRunning = false;
    private Thread timer;

    private int delay = 2000;

    @FXML
    private Label label;

    @FXML
    private Button button;


    private void start(){
        timer = new Thread(this, "timer");
        isRunning = true;
        timer.start();
    }

    private void stop(){
        isRunning = false;
    }

    private void interrupt(){
        isRunning = false;
        timer.interrupt();
    }

    @Override
    public void run() {
        int counter = 0;
        while (isRunning) {
            try {
                ++counter;
                String text = "MyText" + counter;
                Platform.runLater(() -> label.setText(text));

                if (counter == 5) {
                    stop();
                }


                Thread.currentThread().sleep(delay);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }
}

It is essential to update the label under Platform.runLater()- the JavaFx main thread is the only thread permitted to update JavaFx objects.

Embid123
  • 467
  • 5
  • 17
  • 1
    You could simplify a bit by `while{isRunnig && counter <=5)` and removing `private void interrupt()` which is never used – c0der Jan 17 '19 at 09:14