JavaFX - Dialog box displays blank on first time displayed - java

In my javafx application I have a dialog box which shows the results from a previous action. When first displayed the window just shows as white without the contents. After resizing the contents display, and on the subsequent times the dialog is brought up it behaves normally. I'm sure it's something simple, but it's been bugging me for days while I move to work on other parts. My initialize method and Constructor are empty and something tells me this may be the issue.
In MainApp extends Application:
called from another dialog stage controller after calling close.
public void showEDResult(List<String> path) {
try {
// Load the fxml file and create a new stage for the popup
FXMLLoader loader = new FXMLLoader(MainApp.class.getResource("view/EDResultLayout.fxml"));
VBox page = (VBox) loader.load();
Stage dialogStage = new Stage();
dialogStage.setTitle("Edit Distance Result");
//dialogStage.initModality(Modality.WINDOW_MODAL);
dialogStage.initOwner(primaryStage);
Scene scene = new Scene(page);
dialogStage.setScene(scene);
// Set reference to stage in controller
//BUG -- when first displayed results don't show up until resize window
EDResultController controller = loader.getController();
controller.setDialogStage(dialogStage);
controller.setMainApp(this);
// give controller reference to result
controller.setResult(path);
// give controller reference to scene (cursor)
// Show the dialog and wait until the user closes it
dialogStage.showAndWait();
} catch (IOException e) {
// Exception gets thrown if the fxml file could not be loaded
e.printStackTrace();
}
}
In class ResultController:
public void setResult(List<String> result) {
numStepsLabel.setText(Integer.toString(result.size()-2));
//TODO -- work on layout
String str = buildResultString(result);
pathLabel.setText(str);
}

Related

How to have a button open one scene and close the other (JavaFX)?

I have two scenes for Donuts and Coffee. On a clicking on button_orderDonuts it should open the scene for Donuts but close the scene for Coffee. While clicking on button_orderCoffee it should open the scene for Coffee but close the scene for Donuts. How can I have a button to have two functionalities. Also, I'm using SceneBuilder.
#FXML
private Button button_orderCoffee;
#FXML
private Button button_orderDonuts;
/**
* Go to Coffee UI
* #param event
*/
#FXML
void goToCoffee(ActionEvent event)
{
try
{
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(getClass().getResource("CoffeeView.fxml"));
Scene coffeeView = new Scene(fxmlLoader.load(), 445, 464);
Stage coffeeViewStage = new Stage();
coffeeViewStage.setTitle("Order Coffee");
coffeeViewStage.setScene(coffeeView);
coffeeViewStage.show();
CoffeeController coffeeController = fxmlLoader.getController();
coffeeController.setMainController(this);
}
catch (IOException e)
{
e.printStackTrace();
}
}
/**
* Go to Donut UI
* #param event
*/
#FXML
void goToDonut(ActionEvent event)
{
try
{
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(getClass().getResource("DonutView.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 445, 464);
Stage stage = new Stage();
stage.setTitle("Order Donut");
stage.setScene(scene);
stage.show();
DonutController donutController = fxmlLoader.getController();
donutController.setMainController(this);
}
catch (IOException e)
{
e.printStackTrace();
}
}
So considering that you only need to create a new scene once it is often best practice to just load the scenes once, make a stage, and then modify which scene is loaded into the stage. Something like this would be better in essence, though it is even better still to have a class which loads the scenes and then passes them to a view to handle switching. I see that you are trying to getController() and then set it. From my experience it is best to load the fxml without a controllew ant then set it before calling load. Like mentioned previously, I spent time doing this recently (like hours and hours) and got a very reliable MVC system working, feel free to ask more, I am happy to share (it took a while so better to share the knowledge heh)
That said here is a hopefully working example.
#FXML
private Button button_orderCoffee;
#FXML
private Button button_orderDonuts;
private Scene coffeeScene;
private Scene donutScene;
private Stage stage;
void loadScenes() {
// Load coffee scene and set controller
FXMLLoader coffeeLoader = new FXMLLoader(getClass().getResource("CoffeeView.fxml"));
coffeeLoader.setController(this)
coffeeScene = new Scene(coffeeLoader.load());
// Load donut scene and set controller
FXMLLoader donutLoader = new FXMLLoader(getClass().getResource("DonutView.fxml"));
donutLoader.setController(this)
donutScene = new Scene(donutLoader.load());
}
/**
* Go to Coffee UI
* #param event
*/
#FXML
void goToCoffee(ActionEvent event) {
try {
stage.setTitle("Order Coffee");
stage.setScene(coffeeScene);
sStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Go to Donut UI
* #param event
*/
#FXML
void goToDonut(ActionEvent event) {
try {
stage.setTitle("Order Donut");
stage.setScene(donutScene);
stage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
This way you call load scenes once and create a single stage, just changing which scene is set each time. Again you will need to make sure fx:controller="..." in the fxml files is removed. Do not make them empty, remove it entirely. On any buttons where a function is called, lets say on the button_orderCoffee where onAction="#goToCoffee" is called, this will appear red like an error. That is simply because it cannot see a controller, but since a controller is being set before the scene is loaded the actions will work on compile and run. This allows you to make a single instance of the controller and set it, allowing you to reference the controller and other classes.
The scene loading does not have to be in its own function, it can just be called on load depending how things are setup. If this is in the MVC style I recommend putting this code in the view or the initial setup and referencing the controller.
As #Kleopatra mentioned, it is better practice to use seperate controllers for each scene. I would advise using a model or some other class to mediate between the two if you need to share data amongst the two

Passing object from one window to an existing one

I'm still new in Java, and I have a problem with passing objects between two windows in JavaFX.
I have mainStage with mainStageController. There are two things inside the controller: a label and a button. The button is opening a new window (childStage) with childStageController.
In childStageController I have a method, which gets the text "TEST CONTENT" from textField. I want to pass this text to the existing mainStage and set the label text to "TEST CONTENT".
There are a lot of topics handle similar problems, but in every case I found, the problem is with passing the object to a new created window. My case is exactly the opposite: I need to pass the object from the new opened window to the existing one.
#FXML
private Label label;
#FXML
private Button button;
private void configureButton() {
button.setOnAction(event -> {
newStageOpener(mainPaneResource, mainPaneStageTitle);
// There is magic to do
label.setText(message);
});
}
private void newStageOpener(String resource, String stageTitle){
try {
Parent parent = FXMLLoader.load(getClass().getResource(resource));
Scene scene = new Scene(parent);
Stage stage = new Stage();
stage.setScene(scene);
stage.setTitle(stageTitle);
stage.showAndWait();
} catch (IOException e) {
e.printStackTrace();
}
}
I try to do something like this, but every time I get a NullPointerException (the debugger says that childStageController is null).
FXMLLoader loader = new FXMLLoader(getClass().getResource(childPaneResource));
ChildStageController childStageController = loader.getController();
String message = childStageController.getMessage;
I also try: using setLocation, using loader.load(), "reverse" problem (setText directly from childStageController, instead of getting it from mainStageController), put newStageOpener into a configureButton, but nothing works - I always get the same NullPointerException.
What should I do to fix it?
There was mistake in newStageOpener - I rebuild it like this:
private void newStageOpener(String resource, String stageTitle) {
try {
FXMLLoader loader = new FXMLLoader(ChildStageController.class.getResource(resource));
Parent root = loader.load();
Scene scene = new Scene(root);
Stage stage = new Stage();
stage.setScene(scene);
stage.setTitle(stageTitle);
stage.showAndWait();
getObjectFromResource(resource, loader);
} catch (IOException e) {
e.printStackTrace();
}
}
private void getObjectFromResource(String resource, FXMLLoader loader) {
loader.setLocation(ChildStageController.class.getResource(resource));
ChildStageController childStageController = loader.getController();
message = childStageController.getMessage();
}
getMessage obviously is getting text from TextField.
Now it's working perfect.

JavaFX - Switching Scenes in an EventHandler

I'm creating a signUp page and once the user clicks signUp button I want the scene to switch to my budgetView layout. (Which is an FXML file).
I have tried extending my sign up controller with the Application class, and overrode the start method, but it kept giving me an error. This is the route my teacher tried to get me to try. I have also created another primaryStage in my controller and that worked, but my previous scene didn't close it just created another AnchorPane on top of the existing SignUp Scene. I want to just switch from one FXML view to another, when the event handler is initiated and successful.
Event Handler
try {
SignUpDAO.insertUser(txtFieldEmail.getText(), txtFieldFirst.getText(), txtFieldLast.getText(),
passFieldPassword.getText());
resultArea.setText("User inserted! \n");
// SUPPOSED TO OPEN NEW SCENE THROWS
// Exception in thread "JavaFX Application Thread" java.lang.RuntimeException:
// java.lang.reflect.InvocationTargetException
// start(primaryStage);
Overridden Start Method
// TODO Auto-generated method stub
try {
this.primaryStage = primaryStage;
AnchorPane budgetLayout = FXMLLoader.load(getClass().getResource("MainLayout.fxml"));
// SignUp Layout
Scene scene1 = new Scene(budgetLayout);
primaryStage.setScene(scene1);
primaryStage.show();

How do you change the text in a button from another window (JavaFX + FXML)?

I am very new to using JavaFX and have having some trouble using JavaFX with FXML. I am creating a program with a "Setup" button that when clicked, opens a new window with the connection (to an Arduino) settings. On the menu is another button ("Connect") that connects to the board. This closes the window. I'm looking to have this change the text of the original "setup" button to "disconnect", however, I don't seem to be able to access the button from the "setup window". Whenever I click "connect" I get the following error:
Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
I read online that this is a wrapper for a null pointer exception. I assume that the "setup" button is null and that's why I can't change it, but I can't work out why.
Here is an excerpt from MainController.java:
#FXML
protected void setUpConnection(ActionEvent e) {
SetupController setupController = new SetupController();
setupController.init(this);
}
The above method gets called when the "setup" button is clicked (set in the file: setupMenu.fxml). This then opens up the separate window. Here is the code in SetupController.java that opens the window:
private void openSetupWindow() {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("setupMenu.fxml"));
Parent root1 = (Parent)loader.load();
Stage stage = new Stage();
stage.setTitle("Setup Connection");
stage.setScene(new Scene(root1));
stage.show();
} catch(Exception exc) {
exc.printStackTrace();
}
}
When the connect button is clicked, the following method (in SetupController.java) is called:
private void changeButtonText(ConnectionEventType e) {
Button b = main.getSetupButton();
if(e == ConnectionEventType.CONNECT) {
b.setText("Disconnect");
}
else {
b.setText("Setup Connection...");
}
}
(main is the MainController object that was passed in to setupController.init() )
The above code is where I am getting the error.
Just to clarify, I have 2 separate fxml files, one for the main window and one for the pop up. sample.fxml(the main window) has its controller set to MainController and is set up in Main.java (below):
#Override
public void start(Stage primaryStage) throws Exception{
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("sample.fxml"));
GridPane root = loader.load();
Scene scene = new Scene(root, 1200, 900);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setTitle("Nest Control");
primaryStage.setScene(scene);
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
Am I trying to access the button incorrectly? Is anyone able to help? Like I said, I don't have much experience with using JavaFX or FXML.
I think the answer you are looking for is to store the controllers for each window you open so you can access the variables within the controllers, however without the rest of your code would be hard to advise you, but heres an example of what i mean:
private SetupController yourController;
#Override
public void start(Stage primaryStage) throws Exception{
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("sample.fxml"));
GridPane root = loader.load();
this.yourController=loader.<SetupController>getController();
Scene scene = new Scene(root, 1200, 900);
}
}
You could then pass the variable yourController to other instances in a Model-view-controller type way and access its methods.
or something like this in your case :
private void changeButtonText(ConnectionEventType e) {
Button b = this.yourController.getButton(); //a method that returns your #FXML button object in your controller
if(e == ConnectionEventType.CONNECT) {
b.setText("Disconnect");
}
else {
b.setText("Setup Connection...");
}
}
Or alternatively have a specific method within the controller that will set the text of the button without having to return the button object.
See the examples here and here
However please note the error you get seems to typically attributed to missing #FXML annotations so maybe make sure in this instance that you have annotated all the variables in any controllers also. See here for more details.

I can't add items to either ComboBox or ChoiceBox when adding another stage

So, I have two .fxml files, the other one working as main and the other one as basically window that pops up.
#FXML private void handleSelection() throws Error {
try {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("StudentChoose.fxml"));
Parent root1 = (Parent) fxmlLoader.load();
Stage stage = new Stage(StageStyle.DECORATED);
stage.setScene(new Scene(root1));
stage.show();
} catch(Exception e) {
e.printStackTrace();
}
sBox.setItems(FXCollections.observableArrayList("New Document", "Open ", "Save", "Save as"));
}
This is what I have. I have stated the sBox above with
#FXML private ChoiceBox sBox;
Everything works PERFECTLY when the choicebox/combobox is on the main stage, but when it is on the other stage - it doesn't. I have a .fxml file for this another stage with the content, id is set as 'sBox' for the choicebox - but it doesn't work. Instead it returns me a java.lang.NullPointerException on the line where I call to add items to it. What could be the problem?

Categories

Resources