1

Trying to make a small text based game and I'm having trouble updating the title of a JavaFX stage, from within a Thread. Relevant code is as follows.. The title displays the time, which is why it must be updated every second via a thread.

Main method

    public static void main(String args[]) {
    if(checkVersion() == true) {
        launch(args);
    } else {
        System.exit(0);
    }
}

The class containing main method "Warmth" extends Application, implements Runnable. This is code is executed as a consequence of launch(); in main (just in case you interpreted it as .start(); from Thread

    public void start(Stage primaryStage) throws Exception {
    Warmth warmth = new Warmth();
    warmth.isAlive = true;
    setUserAgentStylesheet(STYLESHEET_MODENA);
    warmth.display = Display.createDisplay(title, width, height);
    (warmth.game = new Thread(warmth)).start();
}

The code from the class Display is as follows

private Display(String title, Dimension size) {
    dimensions = size;
    primaryStage = new Stage();
    primaryStage.setTitle(title);
    logtextArea = new TextArea();
    logtextArea.setEditable(false);
    logtextArea.setText("text area");



    primaryLayout = new BorderPane();
    rightSideLayout = new VBox(20);
    middlelayout = new StackPane();

    middlelayout.getChildren().add(logtextArea);

    rightSideLayout.setMargin(label1, new Insets(20,20,20,100));
    rightSideLayout.getChildren().add(label1);

    primaryLayout.setRight(rightSideLayout);
    primaryLayout.setCenter(middlelayout);

    baseScene = new Scene(primaryLayout, dimensions.width, dimensions.height);

    primaryStage.setScene(baseScene);
    primaryStage.show();
}

public void setTitle(String title) {
    primaryStage.setTitle(title);
}

public static Display createDisplay(String title, int width, int height) {
    Dimension size = new Dimension(width, height);
    return new Display(title, size);
}

Finally here is the run() method resulting from Thread.start()

public void run() {
    while(isAlive) {
        long currentTime = System.currentTimeMillis();
        updateTime();
        title = ("WARMTH | "+hour+":"+minute+":"+second);
        display.setTitle(title);
        long lastTime = System.currentTimeMillis();
        delta+= (currentTime - lastTime);
    }
}

I'm not sure exactly the problem but I have several guesses. It seems like I'm just tying knots with threads that aren't running in parallel. Or trying to do things from within a thread that aren't possible, or reachable. I've tried rearranging things sever times and almost all runtime errors are the application throwing a fit over a thread related exception.

I've never had problems doing things like this using swing components (JFrame) however, I'm very new with the whole FX thing. If you coud shed any light that would be of much help. Also... forgive the hideous code.

EDIT Exception thrown at runtime...

Exception in thread "Thread-4" java.lang.IllegalStateException: This operation is permitted on the event thread only; currentThread = Thread-4
    at com.sun.glass.ui.Application.checkEventThread(Application.java:443)
    at com.sun.glass.ui.Window.setTitle(Window.java:829)
    at com.sun.javafx.tk.quantum.WindowStage.setTitle(WindowStage.java:475)
    at javafx.stage.Stage$5.invalidated(Stage.java:736)
    at javafx.beans.property.StringPropertyBase.markInvalid(StringPropertyBase.java:109)
    at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:144)
    at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:49)
    at javafx.stage.Stage.setTitle(Stage.java:722)
    at warmth.design.Display.setTitle(Display.java:58)
    at warmth.Warmth.run(Warmth.java:52)
    at java.lang.Thread.run(Thread.java:745)
Corderro Artz
  • 41
  • 1
  • 6
  • What actually happens? I would expect some exceptions being thrown from that, since you're updating the UI from a background thread, which violates JavaFX' threading rules (as it would do in Swing as well). If all you are really doing is updating the UI on a periodic basis, you don't need a thread at all, just use a `Timeline`: http://stackoverflow.com/questions/9966136/javafx-periodic-background-task – James_D Nov 13 '16 at 15:14
  • While I'm sure that approach would work fine, there is so much dependent on the timer implemented in run() that it would be much easier and simpler to handle from within that thread. The currentTimeMillis you see and the discrepancy between the two of them are my preferred way to find and handle actions which can be executed at any time as I have the amount of seconds passed. For swing its clean and simple but not working well (clearly) for me in FX. Is there are way to handle this without using more complex classes. I've updated the post with the exception thrown from the last execution. – Corderro Artz Nov 13 '16 at 15:28
  • The JavaFX equivalent of Swing's `SwingUtilities.invokeLater(...)` is `Platform.runLater(...)`. If you really need a background thread (which I'm not convinced about, but you seem determined to reinvent the wheel), just wrap the call to `display.setTitle(...)` in a `Platform.runLater(...)`. – James_D Nov 13 '16 at 15:30
  • Alright, you got me!.. I don't need a background thread (not for the UI updating) but now I feel like an a** so I suppose I should be normal and use the proper means to complete this task... In Java 8, the preferred method of updating UI components based on scheduled or arbitrary time is to use TimeLine.. is this Correct? Thanks for the help btw. – Corderro Artz Nov 13 '16 at 15:41
  • Yes (though "preferred" is to some extent a matter of opinion...). If you are in some sense just changing the UI on a periodic basis (which, if you think about it the right way, is the definition of an "animation"), use the animation API (e.g. `Timeline`). If you are performing one-off tasks that may take a long time and need to process the results in the UI, use the concurrency API (e.g. `Task` or `Service`). If you need a game loop, consider `AnimationTimer`. If you really need to implement low-level threading, update the UI with `Platform.runLater(...)` from the background thread. – James_D Nov 13 '16 at 15:45

0 Answers0