0

I am working on a project which deals with multiple fxml and corresponding controller files. I need to somehow get access to an fxml element defined in say A.fxml from the controller of B.fxml and use it.

I am not allowed to show the actual code. However, for this purpose, I have built a simple application with two FXMLs and their corresponding controllers.

This application has Button.fxml with ButtonController.java and ProgressIndicator.fxml with ProgressIndicatorController.java

Upon clicking the button, the Progress indicator would come live.

Below is the code :

Button.fxml

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

<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.Pane?>

<AnchorPane xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.ButtonController">
   <children>
      <Pane prefHeight="206.0" prefWidth="281.0">
         <children>
            <Button fx:id="button1" layoutX="59.0" layoutY="63.0" mnemonicParsing="false" prefHeight="63.0" prefWidth="164.0" text="Button" onAction="#onButton1Clicked" />
         </children>
      </Pane>
   </children>
</AnchorPane>

ButtonController.java

package application;

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

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

public class ButtonController implements Initializable{

    @FXML
    private Button button1;

    private ProgressIndicator progressIndicator;

    private ProgressIndicatorController progressIndicatorController;

    @Override
    public void initialize(URL location, ResourceBundle resources) {

        try {
            Object loader = FXMLLoader.load(getClass().getResource("ProgressIndicator.fxml"));
            Scene sceneProgressIndicator = new Scene((Parent) loader, 1400, 800);
            sceneProgressIndicator.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
            sceneProgressIndicator.setFill(Color.TRANSPARENT);
            Stage primaryStageProgressIndicator = new Stage();
            primaryStageProgressIndicator.setScene(sceneProgressIndicator);
            primaryStageProgressIndicator.setResizable(false);
            primaryStageProgressIndicator.setHeight(311);
            primaryStageProgressIndicator.setWidth(523);
            primaryStageProgressIndicator.show();
        } catch (IOException e) {

            e.printStackTrace();
        }




        // TODO Auto-generated method stub



    }

    public void onButton1Clicked() {

        FXMLLoader fxmlLoaderProgressIndicator = new FXMLLoader();
        fxmlLoaderProgressIndicator.setLocation(getClass().getResource("ProgressIndicator.fxml"));
        try {
            Parent root = fxmlLoaderProgressIndicator.load();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        progressIndicatorController = (ProgressIndicatorController) fxmlLoaderProgressIndicator.getController();



        for(double  i = 0.0; i <= 1.0 ; i+=0.2) {
            progressIndicator = progressIndicatorController.getProgressIndicator1();
            progressIndicator.setProgress(i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

    }


}

ProgressIndicator.fxml

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

<?import javafx.scene.control.ProgressIndicator?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.Pane?>


<AnchorPane xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.ProgressIndicatorController">
   <children>
      <Pane prefHeight="200.0" prefWidth="200.0">
         <children>
            <ProgressIndicator fx:id="progressIndicator1" layoutX="47.0" layoutY="31.0" prefHeight="103.0" prefWidth="106.0" progress="0.0" />
         </children>
      </Pane>
   </children>
</AnchorPane>

ProgressindicatorController.java

package application;

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

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.image.Image;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

public class ProgressIndicatorController implements Initializable {


    @FXML
    private ProgressIndicator progressIndicator1;

    @Override
    public void initialize(URL location, ResourceBundle resources) {




        // TODO Auto-generated method stub

    }

    public ProgressIndicator getProgressIndicator1() {

        System.out.println("getteeerrrrrrrr");
        return progressIndicator1;
    }

    public void setProgressIndicator1(ProgressIndicator progressIndicator1) {
        this.progressIndicator1 = progressIndicator1;
    }

}

The getter method is successfully called. However when I click the button, the progress indicator doesnot get updated.

I am also not getting any kind of error messages .

I came across this link Javafx - how to acces FXML "objects" which indeed helped me to a great extent,but I'm not able to get this work too. I just tried to modify the button click method as follows :

onButton1Cliked()

public void onButton1Clicked() {

        progressIndicator = (ProgressIndicator) sceneProgressIndicator.lookup("#progressIndicator1");

        for(double  i = 0.0; i <= 1.0 ; i+=0.2) {
            try {
                System.out.println("Progressingggg   " + i);
                progressIndicator.setProgress(i);
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

Clicking on the button does indeed print out the statements. But the progress indicator doesn't get updated with each for loop iteration. Its only at last that the progress indicator gets updated and shows done.

Any idea why this is happening ?

Also is there any other way to access the fxml elements other than using a lookup method. Can my previous approach(trying to get the fxml elements through controller) be made to work somehow.

Any advice or suggestions are most welcome. Been into this for several hours. Stackoverflow seems to be the last resort.

EDIT 1 : START

It turned out that the progress Indicator was not updating because of the reason explained in Zephyr's first comment. So i created a separte thread for my application and that seemed to work fine as the progress indicator got updated as expected.

Working version of onButton1Clicked() method that is successfully updating Progress Indicator :

public void onButton1Clicked() {

        progressIndicator = (ProgressIndicator) sceneProgressIndicator.lookup("#progressIndicator1");

        Runnable runnable = new Runnable() {

            @Override
            public void run() {

                for(double  i = 0.0; i <= 1.0 ; i+=0.2) {               
                    try {
                        System.out.println("Progressingggg   " + i);
                        progressIndicator.setProgress(i);
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
        }   

            }
        };

        Thread thread = new Thread(runnable);
        thread.start();

    }

Having resolved this threading issue, I tried once again to go by the original way mentioned in my OP(i.e. to get the ProgressIndicatorController instance and access its progress indicator and then try updating it). However, this is not working for me. I tried debugging and found that I am getting back the ProgressIndicatorController and ProgressIndicator instance back.

Why then my code is unable to update the progress indicator if it has a hold on the progress indicator instance. The println methods are executed successfully.Its just the progress indicator not getting updated at all.It stands at "0" all the time.

Version of onButton1Clicked() method that is NOT updating progressIndicator :

public void onButton1Clicked() {        

        FXMLLoader fxmlLoaderProgressIndicator = new FXMLLoader();
        fxmlLoaderProgressIndicator.setLocation(getClass().getResource("ProgressIndicator.fxml"));
        try {
            Parent root = fxmlLoaderProgressIndicator.load();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        progressIndicatorController = (ProgressIndicatorController) fxmlLoaderProgressIndicator.getController();



        progressIndicator = progressIndicatorController.getProgressIndicator1();

        Runnable runnable = new Runnable() {

            @Override
            public void run() {

                for(double  i = 0.0; i <= 1.0 ; i+=0.2) {               
                    try {
                        System.out.println("Progressingggg   " + i);
                        progressIndicator.setProgress(i);
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
        }   

            }
        };

        Thread thread = new Thread(runnable);
        thread.start();

    }

EDIT 1 : END

EDIT 2 : START

Adding other files required to run this sample application

Main.java

package application;

import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.fxml.FXMLLoader;


public class Main extends Application {
    @Override
    public void start(Stage primaryStage) {
        try {
            Object root =FXMLLoader.load(getClass().getResource("Button.fxml"));
            Scene scene = new Scene((Parent) root,400,400);
            scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
            primaryStage.setScene(scene);
            primaryStage.show();
        } catch(Exception e) {
            e.printStackTrace();
        }


    }

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

My application.css file has nothing in it. Its empty.

I'm using JavaFX8 on Java 8. IDE is Eclipse Photon

I created my project using e(fx)clipse plugin and choosing JavaFX Project from create new project window.

EDIT 2 : END

EDIT 3 : START

Under the section EDIT 1 while trying to test the version of onButton1Clicked() that successfully updates the progress indicator please don't forget to include private Scene sceneProgressIndicator as the member variable of the class ButtonController.java.

EDIT 3 : END

Asif Kamran Malick
  • 2,409
  • 3
  • 25
  • 47
  • You're putting the JavaFX Application Thread to sleep... it cannot update any of the visual elements until the `Thread.sleep()` method has finished. Search StackOverflow for how to run code in a background thread; there are a lot of answers already. – Zephyr Jun 07 '19 at 18:12
  • Possible duplicate of [Platform.runLater and Task in JavaFX](https://stackoverflow.com/questions/13784333/platform-runlater-and-task-in-javafx) – Zephyr Jun 07 '19 at 18:16
  • Thank you. @Zephyr I will look into it. But I still seek to know whether there is an alternate way of getting access to the nodes , other than this lookup-by-id method. If there is an alternate way, then which one is better to go with. – Asif Kamran Malick Jun 07 '19 at 19:40
  • Search for "Passing Parameters FXML" – Zephyr Jun 07 '19 at 20:11
  • 2
    "I am not allowed to show the actual code" there is no need. What we need is [mcve] – c0der Jun 08 '19 at 08:32
  • @c0der I have already provided an example project in my OP. My example project consists of two FXML files and corresponding two controllers. "Button.fxml", "ButtonController.java", "ProgressIndicator.fxml" and "ProgressIndicatorController.java". I am trying to animate the progresss indicator from within the "ButtonController.java". I have worked again on my "onButtonClicked()" method will provide a working solution as an edit to my OP. I will also add the other version of method that's not working for me. – Asif Kamran Malick Jun 08 '19 at 12:47
  • updated my post with new details under "EDIT 1" – Asif Kamran Malick Jun 08 '19 at 13:12
  • Please let me know when you have an mcve posted. Sorry, I can't help without one. – c0der Jun 08 '19 at 13:33
  • Do I need to post it on github and then share a link for the repository? I am not able to understand minimal, complete and verifiable example (mcve). My project is just a simple java fx project that contains those two fxml and two controllers in the same default package. Please let me know what do I need to do. I have tried my best to provide all the code that my sample app contains. – Asif Kamran Malick Jun 08 '19 at 13:39
  • The problem should be **r**eproducible and **c**omplete. Meaning we should be able to copy-paste and run it. All the resources needed to execute it (for example images) should be available. The code should demonstrate the problem, and nothing else (**m**) so you need to remove everything which is not essential to demonstrate it. Your code for example does not have a main (`Application`) so we cant invoke it. – c0der Jun 09 '19 at 03:40
  • @c0der i have added in more details under "EDIT 2". Thanks for pointing out the exact issue. I'm sorry for not having included that in the first place. Also, I didn't find anything to remove so as to make it minimal. Its already minimal I believe as I created this sample app just to reproduce the scenario that I was facing in my original application. Thanks again for your inputs. Let me know if I'm on the right path. – Asif Kamran Malick Jun 09 '19 at 05:18
  • 1
    It is certainly more of an MCVE. It can be invoked (with minor changes like making `Scene sceneProgressIndicator` a class variable). – c0der Jun 09 '19 at 05:47
  • Since "sceneProgressIndicator" was part of the "onButton1Clicked()" version that worked fine(i.e. it updated the progress indicator in an expected way), I excluded that. The other version of "onButton1Clicked()" which does not work (i.e. does not update the progress indicator) does not make use of the "sceneProgressIndicator". But I will still update my OP under "EDIT 3" to make note of this. Thanks again. – Asif Kamran Malick Jun 09 '19 at 06:02

1 Answers1

1

I assume you wanted something like this (note the comments):

import java.io.IOException;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

public class ButtonController {

    @FXML
    private Button button1;
    private ProgressIndicator progressIndicator;
    private Scene sceneProgressIndicator;

    @FXML
    public void initialize() {

        try {

            FXMLLoader loader = new FXMLLoader(getClass().getResource("ProgressIndicator.fxml"));
            Pane progressIndicatorPane = loader.load();
            ProgressIndicatorController progressIndicatorController  = loader.getController(); //get a reference to the progress indicator controller
            progressIndicator = progressIndicatorController.getProgressIndicator1(); //get the progress indicator 

            sceneProgressIndicator = new Scene(progressIndicatorPane, 1400, 800);
            sceneProgressIndicator.setFill(Color.TRANSPARENT);
            Stage primaryStageProgressIndicator = new Stage();
            primaryStageProgressIndicator.setScene(sceneProgressIndicator);
            primaryStageProgressIndicator.setResizable(false);
            primaryStageProgressIndicator.setHeight(311);
            primaryStageProgressIndicator.setWidth(523);
            primaryStageProgressIndicator.show();
        } catch (IOException e) {

            e.printStackTrace();
        }
    }

    public void onButton1Clicked() {

        Runnable runnable = () -> {

            for(double  i = 0.0; i <= 1.0 ; i+=0.2) {

                final double fD = i;
                Platform.runLater(() -> progressIndicator.setProgress(fD));//update done on javafx thread 

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

        Thread thread = new Thread(runnable);
        thread.start();
    }
}
c0der
  • 18,467
  • 6
  • 33
  • 65
  • I have tested this. This indeed resolves the issue in my sample app. I haven't tested my original app against this. Hopefully, I should be able to implement the same in my original app without breaking anything. Thanks a ton for your time and effort. This is really appreciable. – Asif Kamran Malick Jun 09 '19 at 13:24
  • 1
    I am glad it helped. I think the most valuable thing to take from here is the concept of mvce or [mre]. If you have more question please make a new post and leave me a message here. – c0der Jun 09 '19 at 14:23