0

I just want to program a very simple clock using JavaFX. There are two version of this program. In the one I post here a Label displaying the date and time is updated. In the second instead a TextField is used to display the date and time and is updated. I use a FXML file (and the relative controller) and the Eclipse template. The point is that the following program (with a Label) does not work, while the same with a TextField yes. In my opinion and understanding they should both work or do not work.

The update is done every second by a thread.

Could someone help me to understand why it does not work and fix it? Many thanks!

Here is the main class of the program.

package application;

import java.time.LocalDateTime;

import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.fxml.FXMLLoader;

public class ClockLabel extends Application {

    private boolean clockIsRunning = true;

    @Override
    public void start(Stage primaryStage) {
        try {
            HBox root = (HBox) FXMLLoader.load(getClass().getResource("view/ClockLabel.fxml"));
            Scene scene = new Scene(root, 400, 400);
            // scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
            primaryStage.setScene(scene);
            primaryStage.setResizable(false);
            primaryStage.setTitle("A very simple clock");
            primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
                @Override
                public void handle(WindowEvent event) {
                    clockIsRunning = false;
                }
            });
            primaryStage.show();

            // Starting the thread to update the clock...
            Label clockDisplayLabel = (Label) root.getChildren().get(1);
            updateClockLabel(clockDisplayLabel);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

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

    private void updateClockLabel(Label label) {

        Thread updateThread;
        updateThread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("Clock update thread starts...");
                while (clockIsRunning) {
                    LocalDateTime currentDateTime = LocalDateTime.now();
                    label.setText(printClockString(currentDateTime));
                    System.out.println("Ticking...");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("Clock update thread stops... and the program too...");
            }
        });
        updateThread.start();
    }

    /**
     * Given a LocalDateTime object, returns the string with the formatted time.
     * 
     * @param dateTime
     *            A LocalDateTime object.
     * @return A string with the formatted time.
     */
    public static String printClockString(LocalDateTime dateTime) {

        String dayOfWeek = dateTime.getDayOfWeek().toString();
        int dayOfMonth = dateTime.getDayOfMonth();
        String month = dateTime.getMonth().toString();
        int year = dateTime.getYear();
        String hour = String.format("%02d", dateTime.getHour());
        String minute = String.format("%02d", dateTime.getMinute());
        String second = String.format("%02d", dateTime.getSecond());

        return dayOfWeek + ", " + dayOfMonth + " " + month + " " + year + ", " + hour + ":" + minute + ":" + second;
    }
}

Here is the FXML file

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.HBox?>

<HBox alignment="CENTER" xmlns="http://javafx.com/javafx/8.0.152" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.view.ClockLabelController">
    <children>
        <Label alignment="TOP_LEFT" text="Current date:" />
        <Label fx:id="clockDisplayLabel" alignment="CENTER" text="MONDAY, 19 FEBRUARY 2017, 19:49:40" />
        <Button fx:id="stopClockButton" mnemonicParsing="false" onAction="#stopClock" text="Quit" />
    </children>
</HBox>

Here is the controller (exacty the one generated by SceneBuilder)

package application.view;

import java.net.URL;
import java.util.ResourceBundle;

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

public class ClockLabelController {

    @FXML // ResourceBundle that was given to the FXMLLoader
    private ResourceBundle resources;

    @FXML // URL location of the FXML file that was given to the FXMLLoader
    private URL location;

    @FXML // fx:id="clockDisplayLabel"
    private Label clockDisplayLabel; // Value injected by FXMLLoader

    @FXML // fx:id="stopClockButton"
    private Button stopClockButton; // Value injected by FXMLLoader

    @FXML
    void stopClock(ActionEvent event) {
        Platform.exit();
        System.exit(0);
    }

    @FXML // This method is called by the FXMLLoader when initialization is complete
    void initialize() {
        assert clockDisplayLabel != null : "fx:id=\"clockDisplayLabel\" was not injected: check your FXML file 'Clock.fxml'.";
        assert stopClockButton != null : "fx:id=\"stopClockButton\" was not injected: check your FXML file 'Clock.fxml'.";
    }
}

The error I get is the following:

Exception in thread "Thread-3" java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-3
    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$61(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 application.ClockLabel$2.run(ClockLabel.java:55)
    at java.lang.Thread.run(Thread.java:748)
freakguy
  • 1
  • 1
  • 1
    You can't update the UI from a background thread. Some violations of this rule (e.g. `label.setText(...)`) throw an exception; some (e.g. `textField.setText()`) do not throw exceptions, but it is equally invalid to perform UI updates from a background thread whether these throw exceptions or not. For this use case, you don't need a thread anyway; use an animation, as in the linked question. – James_D Feb 22 '18 at 15:18
  • Also: do some research before posting here. There are dozens of questions on this site if you simply [search for that error message](https://stackoverflow.com/search?q=%5Bjavafx%5D+java.lang.IllegalStateException%3A+Not+on+FX+application+thread). – James_D Feb 22 '18 at 15:20
  • Thanks for your answer! Searching for errors sometimes doesn't help, I'm still a novice. I will try to fix it with the Timeline class (which I didn't know previously). – freakguy Feb 22 '18 at 17:03
  • 1
    *None* of the 141 posts from that search helped? Seriously? – James_D Feb 22 '18 at 17:04

0 Answers0