[SOLVED] problem Adding/removing Nodes

[NOTE: Every time I have a question with JavaFX, I think I will post it here before searching elsewhere. If/when I find an answer, I will follow up and include it here, assuming no one beats me to it. Goal: JGO forums become more relevant to Java game programmers using JavaFX if we have more questions and tutorials at this site.]

I ran into the following error when trying to add or delete a Node from a method that is NOT the overridden start(Stage) method.

    java.lang.IllegalStateException: Not on FX application thread; ... 

Example of calling code that elicits this error:

    root.getChildren().add(img); 

For nodes added in the start() method, the code: root.getChildren().remove(img); creates the same error.

The code that tries to add or remove is located in a method in the same class that implements Application . A Group variable named root has been made an instance variable rather than the usual practice of making the root node a local variable of the start() method.

My goal is to manage cross-fades between different visual elements. I was thinking that once a node is faded out and no longer needed, I would delete it. And as new nodes are needed, I would add them. But the Exception is preventing this.

Alternatively, I can access the existing nodes to null them or vary their properties. Is this the way to manage the nodes? Flip the visibility or manage opacity and null them when they are not being used, but leave the nulled nodes as placeholders? That could leave me with a scene with 80% of the nodes being null or invisible at any one time which intuitively seems kind of fishy.

Or is this sort of thing (cross-fading lots of nodes) something that can or should be handled with managing multiple Scene variables?

Found the answer!

The FX Application thread seems to be the JavaFX equivalent of the EDT on Swing. If this error crops up, the solution is to schedule the code to run on the FX Application thread. This can be done as follows:


    Platform.runLater(Runnable runnable);

API for Platform

This being Java 8, the Runnable can be implemented via a Lambda, instead of an anonymous class.


    Platform.runLater(
        () -> { root.getChildren().add(img); }
    );

The above reminds me of the code used in a main() when starting up an application that uses Swing:


    EventQueue.InvokeLater(new Runnable()
    {
        public void run()
        {
            JFrame frame = new JFrame();
            // etc.
        }
    }); 

As with the EDT, the API warns us not to try and do too much on the FX Application thread, as it can be a bottleneck.

Surely you mean never, ever, ever touch JavaFX off the FX Application thread, the same as never, ever, ever touch Swing off the EDT?! :point:

I am still figuring out what is or isn’t “touching” the FX Application thread, to use your terms.

There is a lot that can be done with JavaFX controls off of the FX thread. (And should be, to avoid clogging the thread and slowing down the app.) I can change properties from an external thread. In the opening for Hexara that I’m recoding, an animation thread is handling an opacity fade. The error message came up only when trying to add or remove a node to the current scene.

I didn’t say touching the FX Application thread, I said touching JavaFX on another thread. By which I mean pretty much anything! Neither JavaFX or Swing are thread-safe, which means you should never create any object or call any method (of a JavaFX/Swing class) from anything other than the application thread / EDT unless it’s specifically marked as thread-safe. What you’re doing may or may not work, for now, with this version of Java, with your OS, on your machine.

@nsigma Thanks for clarifying. I will keep your warning in mind.

Fingers crossed, maybe it’s just a situation where property changes might not propagate in the order made or something minor, rather than an outright crash. I suspect that JavaFX is more robust than Swing. But I’ll look into it further, and be on the lookout for problems as Hexara develops.

Really don’t assume that! You’re doing something the library is specifically designed not to support - robustness is therefore irrelevant.

From the Node JavaDoc -

[quote]Node objects may be constructed and modified on any thread as long they are not yet attached to a Scene. An application must attach nodes to a Scene, and modify nodes that are already attached to a Scene, on the JavaFX Application Thread.
[/quote]
Note this is different to Swing where you’re not even supposed to construct or modify an unattached component off the EDT.

What you’re doing looks like premature optimization, and one that probably isn’t an optimization. :wink:

Here is a code example that led me astray in terms of thinking that it might be okay to modify some properties of “live” nodes off of the JavaFX Application thread. In this particle generator, the Node subclass Canvas is being modified intensely: 20,000 objects per 60 fps frame are having location and other attributes altered.

Is the animation timer actually on the JavaFX Application thread? It is a separate thread, it seems to me.

Here’s a link to watch the particle generator in action:

1Nx5Be9BDYg

Anyway, nothing dire is happening. So, I am wondering what is going on. Why doesn’t this program crash on a regular basis? Could it be that the only thing of concern is that some of the property references are being updated during a render iteration, and the differences between old vs. new values do not matter to this process?

I’m having trouble finding more information. The Node documentation does clearly state that modifications to a live node should be done on the FX Application thread. Will continue researching.

I think I have a resolution to the concern given by @nsigma. The situation where I was modifying “live” Nodes from “outside” the FX Application thread turns out to have been an incorrect framing of the situation that was occurring.

The Application API has the following quote.

[quote]Threading

JavaFX creates an application thread for running the application start method, processing input events, and running animation timelines. Creation of JavaFX Scene and Stage objects as well as modification of scene graph operations to live objects (those objects already attached to a scene) must be done on the JavaFX application thread.
[/quote]
Thus, the code areas from where it is okay to modify the live nodes are actually pretty substantial. This would explain why I am able to modify opacity values from a method called by the AnimationTimer. I’m also able to remove the TitleBlocks node via this same method–and not throw the “not on FX application thread” exception.

The thread that was throwing the exception before was actually my Audio thread, while I was still within a NoteListener callback. I’ve rewritten the NoteListener so now it flips a switch that tells the update() method (on the AnimationTimer thread) to do the remove(node).

The NoteListener callbacks really need to be terse anyway (yes, never block the Audio thread!), nothing more than flipping a switch for the AnimationTimer thread to do the brunt of the work.

No, the callbacks are on the JavaFX Application thread, same as the Swing Timer actions are called on the EDT. That’s the whole point of these classes.

You might be better using Platform.invokeLater(…) from your audio thread.

Don’t assume all the places that require updates on the FX thread will throw exceptions if you’re not on it. This is unusual, as it forces the cost of checking what thread you’re on for all usage - I assume there’s a good reason why some of these methods do that, perhaps due to native code interactions.