After playing around with the code sample, I think that this is not the
right way to use the animation. The reason is that there is no point in
starting the animation before the control is attached to the scenegraph, or
even made visible. A small refactoring where, e.g., the controller class
exposes a method to start the animation in onSucceeded or just calls it on
the FX thread is enough. I never start an animation as part of the
construction because it's not the right time. John suggested tying the
lifecycle of the animation to the showing of the node, which also solves
the problem.

There are animations like PauseTransition or other non-interfering
Timelines that could reasonably be run on a background thread. Or maybe
just on an unconnected control. This could be a reason to not limit
animation methods to the FX thread at the expense of possible user errors,
but document the pitfall.

I don't see a good use case for modifying controls in a background thread
while still interacting with the scenegraph, hence for adding multithread
support.

- Nir

On Mon, Jan 22, 2024, 12:59 Jurgen Doll <jav...@ivoryemr.co.za> wrote:

> Here's an example as requested by Nir:
>
> public class FxTimeLineTest extends Application
>
> {
>
> private BorderPane bp = new BorderPane( new Label("Loading") );
>
>
> public static void main( String[] args ) {
>
> launch( FxTimeLineTest.class, args );
>
> }
>
>
> @Override
>
> public void start( Stage primaryStage ) throws Exception {
>
> new Thread( new LoadScene() ).start();
>
> primaryStage.setScene( new Scene( bp, 300, 200 ) );
>
> primaryStage.setTitle( "Memory Usage" );
>
> primaryStage.show();
>
> }
>
>
> private class LoadScene extends Task<Parent> {
>
> @Override protected Parent call() throws Exception {
>
> Parent p = FXMLLoader.load( getClass().getResource("TestView.fxml") );
>
> Thread.sleep( 1000 );
>
> return p;
>
> }
>
>
> @Override protected void succeeded() {
>
> bp.setCenter( getValue() );
>
> }
>
>
> @Override protected void failed() {
>
> getException().printStackTrace();
>
> }
>
> }
>
> }
>
>
> ------------------------------------------------------------------------------------------------------
>
> public class TestView
>
> {
>
> @FXML private Label memory;
>
>
> private static final double MEGABYTE = 1024 * 1024;
>
>
> @FXML private void initialize()
>
> {
>
> var updater = new Timeline
>
> (
>
> new KeyFrame( Duration.seconds(2.5), event ->
>
> {
>
> var runtime = Runtime.getRuntime();
>
> double maxMemory = runtime.maxMemory() / MEGABYTE;
>
> double usedMemory = (runtime.totalMemory() - runtime.freeMemory()) /
> MEGABYTE;
>
> memory.setText( (int) usedMemory + " MB / " + (int) maxMemory +" MB" );
>
> })
>
> );
>
>
> updater.setCycleCount(Animation.INDEFINITE);
> // This FXML is being loaded on a background thread
>
> updater.play();
>
> }
>
> }
>
>
> ------------------------------------------------------------------------------------------------------
>
> TestView.fxml
>
> <?xml version="1.0" encoding="UTF-8"?>
>
>
> <?import javafx.scene.control.Label?>
>
> <?import javafx.scene.layout.StackPane?>
>
>
> <StackPane xmlns:fx="http://javafx.com/fxml/1"; fx:controller="TestView">
>
> <children>
>
> <Label fx:id="memory" text="Current / Max MB" >
>
> <properties hashCode="12345" />
>
> </Label>
>
> </children>
>
> </StackPane>
>
>
>
> On Sat, 20 Jan 2024 17:08:41 +0200, Nir Lisker <nlis...@gmail.com> wrote:
>
> Hi Jurgen,
>
> What I'm confused about the most is what it is you are actually trying to
> do that necessitates the use of animations outside of the FX thread. You
> said that you need to initialize controls on another thread, and that you
> are using Task (both of which are fine), but how does playing animations
> relate? Playing an animation is something that is done explicitly, usually
> in order to manipulate data. Can you give a real use case, like a minimized
> version of what you're doing?
>
> - Nir
>
>

Reply via email to