0

I have a method in my Application class called getController(), which returns the controller of that stage. I am having an issue where getController() returns null if ran from an object of the Application class (note: getController() returns the correct controller if ran from the GUIHandler class).
The reason I am doing this is because the GUI code is separated from the rest of the program's code, so I require access to the controller to update specific parts of the GUI in a "Main" class.
My program is a projectile simulator program, where the user can create projectiles and see them on the screen moving around. The business logic lies within a Simulation class, while the GUI code lies within a GUIHandler class and several controllers.
The error occurs on the line "guiHandler.getController().update()" as getController() returns null, but if I run getController() from the GUIHandler's start() method it does not return null. I have included just the necessary parts of the code.

Main

public class Main 
{
    public Simulation currentSimulation = new Simulation();
    public FileHandler fileHandler = new FileHandler();
    public GUIHandler guiHandler = new GUIHandler();
    
    public double dTime = 0;
    public double dTimePerFrame = 20;
    public double dSpeed = 1;

    private Timer timer = new Timer();

    private TimerTask task = new TimerTask() 
    {
        /**
         * This method runs every frame, updating the simulation.
         */
        @Override
        public void run() 
        {           
            dTime = dTime + ( dTimePerFrame / 1000 );

            currentSimulation.update( dTime );
            guiHandler.getController().update();
            
            System.out.println( dTime );
        }

    };

GUIHandler

package simulation;
import controllers.MainController;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class GUIHandler extends Application
{
    private FXMLLoader fxmlLoader = new FXMLLoader( getClass().getResource("/MainGUI.fxml") );
    
    @Override
    public void start(Stage stage) throws Exception 
    {
        
        Parent root = fxmlLoader.load();        
        Scene scene = new Scene( root, 900, 600 );
        
        stage.setTitle("Projectile Simulator");
        stage.setScene(scene);
        stage.show();
        
        stage.setOnCloseRequest( event -> System.exit(0) );
        
        System.out.println( getController() );
        
        
    }
    
    /**
     * Gets the controller that controls the main GUI.
     * @return mainController
     */
    public MainController getController()
    {
        MainController mainController = (MainController) fxmlLoader.getController();
        return mainController;
    }
    
    public static void main( String[] args )
    {
        launch(args);
    }
}

Error

The first line is the value of getController() if run from the GUIHandler's start() method. The rest is the error that is occured if getController() is run from an object of GUIHandler. It should have returned the same thing.

controllers.MainController@65c20942
Exception in thread "Timer-0" java.lang.NullPointerException: Cannot invoke "controllers.MainController.update()" because the return value of "simulation.GUIHandler.getController()" is null
    at simulation.Main$1.run(Main.java:47)
    at java.base/java.util.TimerThread.mainLoop(Timer.java:556)
    at java.base/java.util.TimerThread.run(Timer.java:506)

I would like to make GUIHandler's methods static, but then I am unable to use getClass() in the FXMLLoader. I have tried making an object of the controller class, but then the FXML elements are null when I try to access them. I understand that creating an object is what is causing the issue, but I do not understand why or what else I can do to get what I want?

George
  • 19
  • 8
  • 1
    you don´t need to create a new instance of the class that extends Application in javafx because it's the starting point – Giovanni Contreras Oct 28 '22 at 21:54
  • 1
    Look at the game loop [here](https://gamedevelopment.tutsplus.com/tutorials/introduction-to-javafx-for-game-development--cms-23835) for a basic game loop example. – SedJ601 Oct 28 '22 at 22:00
  • So the main class overseeing everything should be the Application class? I was just using this class for specifically the GUI - so I should have all of the code for the actual simulation stem from here? – George Oct 29 '22 at 02:10
  • 1
    Yes: The `Application` class and its `init()`, `start()` and `stop()` methods are for managing the lifecycle of the entire application. Everything else should stem from there. When you `launch()` the application, an instance of your `Application` subclass is created, `init()` is called, the JavaFX runtime is started, and `start()` is called (on the FX Application Thread). Your `init()` and `start()` methods should be responsible for initializing the application and starting the GUI. – James_D Oct 29 '22 at 02:25
  • 1
    Note that your `Main` class is broken. The `TimerTask` operates on a background thread, and you appear to be modifying the UI from that background thread, which is not allowed. Use the Animation API for functionality like this: see https://stackoverflow.com/a/60685975/2189127 – James_D Oct 29 '22 at 02:28
  • 1
    See the [application documentation](https://openjfx.io/javadoc/17/javafx.graphics/javafx/application/Application.html) to understand the lifecycle, also note that "Calling Platform.exit() is the preferred way to explicitly terminate a JavaFX Application. Directly calling System.exit(int) is an acceptable alternative, but doesn't allow the Application stop() method to run.". Also, when the last window is closed, by default the app will exit, so you probably don't need to explicitly call exit. – jewelsea Oct 29 '22 at 08:49
  • Is it still okay to update the GUI in the Controller class using fxmlLoader.getController().update()? I am also still a bit confused on how to get the Controller and the Application to communicate? For example, the Controller class is used to add more objects (due to user interaction) but will require the currentNanoTime to do this.. how would the Controller class get this value from the Application? – George Oct 29 '22 at 13:02
  • *"how to get the Controller and the Application to communicate? For example, the Controller class is used to add more objects (due to user interaction) but will require the currentNanoTime to do this.. how would the Controller class get this value from the Application"*. That value shouldn't be in the `Application`; it has nothing to do with managing the application lifecycle. Generally, though, consider a [MVC approach](https://stackoverflow.com/questions/32342864/applying-mvc-with-javafx) (also see https://stackoverflow.com/questions/36868391/using-javafx-controller-without-fxml/36873768). – James_D Oct 29 '22 at 16:31

0 Answers0