Communicating with an FXML Controller that's already open - java

I have searched again and again on this to no avail. I have a JavaFX FXML window which is connected to a Controller; this window is open. Clicking a button on the window triggers the opening of another FXML file, linked to its respective controller.
The second window (optionsUI.fxml and optionsController) has a few radio buttons. When one is clicked, I want the location of an image/button to change in the mainUI window. How do I go about doing that?
mainController:
public void assetPressed(MouseEvent event) {
//Get the source of Handler
HUDButton button = (HUDButton) event.getSource();
//Check if an asset is already selected
//----do a thing
//Open stage
openStage(currentAsset);
} else {
//if the current asset selected and the new asset clicked are the same
//----do something
closeStage();
}
//if the current asset selected and the new asset clicked are different
else {
//----do something else
assetIsSelected = true;
openStage(currentAsset);
}
}
}
//opening optionsUI.fxml
public void openStage(Asset asset) {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("optionsUI.fxml"));
Parent root = null;
try {
root = fxmlLoader.load();
} catch (IOException e) {
e.printStackTrace();
}
optionsController controller = fxmlLoader.getController();
Scene scene = new Scene(root, 300, 450);
stage.setScene(scene);
if (alreadyExecuted == false) {
stage.initStyle(StageStyle.UNDECORATED);
stage.initOwner(stageControls); //Making the mainUI the owner of the optionsUI
stage.setTitle("HUDEdit Version 3.0.0");
alreadyExecuted = true;
}
The main issue I am having is adding an event handler on the radio buttons which will change a property of the Button that was pressed (currentButton). I searched on this issue, but what I got was what I have already done: to open a new stage with the new values present in the other FXML file.

You can do something like this in your OptionsController (I am going to rename things to conform to standard naming conventions, btw.)
The basic idea here is just to expose a property representing what the user has selected via the radio buttons.
public class OptionsController {
#FXML
private RadioButton radioButton1 ;
#FXML
private RadioButton radioButton2 ;
private SomeType someValue1 = new SomeType();
private SomeType someValue2 = new SomeType();
private final ReadOnlyObjectWrapper<SomeType> selectedThing = new ReadOnlyObjectWrapper<>();
public ReadOnlyObjectProperty<SomeType> selectedThingProperty() {
return selectedThing.getReadOnlyProperty() ;
}
public final SomeType getSelectedThing() {
return selectedThingProperty().get();
}
public void initialize() {
radioButton1.selectedProperty().addListener((obs, wasSelected, isNowSelected) -> {
if (isNowSelected) {
selectedThing.set(someValue1);
}
});
radioButton2.selectedProperty().addListener((obs, wasSelected, isNowSelected) -> {
if (isNowSelected) {
selectedThing.set(someValue2);
}
});
}
// ...
}
And now when you load Options.fxml you can just observe that property and do whatever you need when it's value changes:
public void openStage(Asset asset) {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("optionsUI.fxml"));
Parent root = null;
try {
root = fxmlLoader.load();
} catch (IOException e) {
e.printStackTrace();
}
OptionsController controller = fxmlLoader.getController();
controller.selectedThingProperty().addListener((obs, oldSelection, newSelection) -> {
// do whatever you need with newSelection....
});
// etc...
}

Related

JavaFX Initialize function and things in there

Is it a proper way to locate ContextMenu, setCellValueFactory, setRowFactory, etc. in initialize function?
public void initialize() {
ContextMenu contextMenu = new ContextMenu();
MenuItem cut = new MenuItem("Cut");
MenuItem copy = new MenuItem("Copy");
MenuItem paste = new MenuItem("Paste");
contextMenu.getItems().addAll(cut, copy, paste);
empId.setCellValueFactory(cellData -> cellData.getValue().employeeIdProperty().asObject());
empName.setCellValueFactory(cellData -> cellData.getValue().firstNameProperty());
empLastName.setCellValueFactory(cellData -> cellData.getValue().lastNameProperty());
//Show info when double clicked
employeeTable.setRowFactory(tv -> {
TableRow<Employee> row = new TableRow<>();
row.setOnMouseClicked(event -> {
if(!row.isEmpty() && event.getButton() == MouseButton.PRIMARY && event.getClickCount() == 2) {
Employee emp = row.getItem();
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getClassLoader().getResource("view/ViewEmployeeView.fxml"));
try {
loader.load();
} catch (IOException e) {
e.printStackTrace();
}
ViewEmployeeController viewEmployee = loader.getController();
viewEmployee.setText(emp.getFirstName(), emp.getLastName(), emp.getEmail(), emp.getPhoneNumber(), String.valueOf(emp.getSalary()), String.valueOf(emp.getManagerId()), String.valueOf(emp.getDepartmentId()));
Parent root = loader.getRoot();
Stage stage = new Stage();
stage.setTitle("View employee");
stage.initModality(Modality.WINDOW_MODAL);
stage.initOwner(addBtn.getScene().getWindow());
stage.setScene(new Scene(root));
stage.show();
stage.setResizable(false);
} else if (event.getButton() == MouseButton.SECONDARY) {
row.setContextMenu(contextMenu);
}
});
return row;
});
I don't know If I clarified my thoughts right but here is the example to understand me better:
I create FXML with different buttons(with OnClicked methods), fields, labels, etc. Then I generate FXML controller where OnClicked methods are generated as well. But what If I need to add additional things as ContextMenu, rowFactory, make dynamic columns for TableView or add setOnAction event, let's say for MenuItem in ContextMenu. Where should I write those things? Is initialize function acceptable for this need?

Java GUI (using FXML):

I am creating an application where I'm combining FXML and regular javaFX to create an application. I'm, however, experiencing problems writing EventHandlers for a Stage-subclass called AddItemWindow that generates custom windows.
The application shows a list of items (keys and weapons) in an inventory. The user can add items, and needs to press a button to add the item of his choice (Add Key or Add Weapon).
A new window then pops up, where the user has to input the relevant data. It will generate a GridPane with the TextFields where the user can input the data. This will be a custom GridPane, depending on the ItemType. It will then load the FXML and add the GridPane.
With the below code, I am getting LoadExceptions for my SetOnAction-code for the buttons cancelling the window or confirming the new item.
Would any-one know where I'm making an error?
/* StartUp Class*/
package main;
//imports from javafx and java
import domain.DomainController;
import gui.OverviewWindow;
public class StartUpGUI extends Application {
#Override
public void start(Stage primaryStage) {
Parent root = new OverviewWindow(new DomainController());
Scene scene = new Scene(root);
primaryStage.setTitle("Weapons and Keys");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String... args) {
Application.launch(StartUpGUI.class, args);
}
}
/* OverviewWindow, subclass of BorderPane */
package gui;
//imports from javafx and java
import domain.DomainController;
import domain.ItemType;
public class OverviewWindow extends BorderPane{
#FXML
private Button btnAddWeapon;
#FXML
private Button btnAddKey;
#FXML
private TextArea txaOverview;
private DomainController dc;
this.dc = dc;
FXMLLoader loader = new FXMLLoader(getClass().getResource("OverviewWindow.fxml"));
loader.setRoot(this);
loader.setController(this);
try{
loader.load();
txaOverview.setText(dc.showOverview()); // showOverview returns a String containing a toString of all items in the inventory
}
catch (IOException ex){
throw new RuntimeException(ex);
}
}
#FXML
public void btnAddWeaponOnAction(ActionEvent event){
try{
add(ItemType.WEAPON); // ItemType is an Enum where all the properties of the items are defined; for Weapon: name, weight, level, power, used(boolean)
}
catch (Exception e){
throw e;
}
}
#FXML
public void btnAddKeyOnAction(ActionEvent event){
try{
add(ItemType.SLEUTEL); // ItemType is an Enum where all the properties of the items are defined; for Key: name, weight, level, door
}
catch (Exception e){
throw e;
}
}
private void add(ItemType itemType){
Stage stage = new Stage();
stage.setTitle(itemType== VoorwerpSoort.WEAPON ? "Add Weapon" : "Add Key");
AddItem addItem = new AddItem(dc,itemType,this);
addItem.setOnHiding(new EventHandler<WindowEvent>(){
#Override
public void handle(WindowEvent e){
txaOverview.setText(dc.showOverview()); // when we close the AddItemWindow, we will update the item overview by having the domain controller get this data from the repository
}
});
addItem.show();
}
}
/* AddItemWindow, a subclass of Stage*/
package gui;
// import relevant javafx and java classes
import domain.DomainController;
import domain.ItemType;
public class AddItemWindow extends Stage {
#FXML
private BorderPane addRoot;
#FXML
private Button btnOK;
#FXML
private Button btnCancel;
private DomainController dc;
private ItemType itemType;
private Parent parent;
private TextField[] txfAttributes;
public AddItemWindow(DomainController dc, ItemType itemType, OverviewWindow overviewWindow){
this.dc = dc;
this.itemType = itemType;
this.parent = overviewWindow;
this.setScene(buildGUI(dc,itemType,overviewWindow));
}
private Scene buildGUI(DomeinController dc, VoorwerpSoort vwps, OverzichtSchermController ovsController){
Parent root = new BorderPane();
GridPane properties = new GridPane();
properties.setPadding(new Insets(10));
properties.setHgap(10);
properties.setVgap(10);
ColumnConstraints col1 = new ColumnConstraints();
col1.setHalignment(HPos.RIGHT);
ColumnConstraints col2 = new ColumnConstraints();
properties.getColumnConstraints().addAll(col1, col2);
String[] attributes = itemType.attributeNames();
txfAttributes = new TextField[attributes.length];
for(int i = 0; i<attributes.length; i++){
properties.add(new Label(attributes[i]),0,i);
properties.add(txfAttributes[i] = new TextField(),1,i);
}
((BorderPane) root).setCenter(properties);
FXMLLoader loader = new FXMLLoader(getClass().getResource("AddItemWindow.fxml"));
loader.setRoot(root);
loader.setController(root);
try{
loader.load();
return new Scene(root);
}
catch(IOException e){
throw new RuntimeException(e);
}
}
// NOT WORKING
#FXML
public void btnOKOnAction(){
addItem();
}
// NOT WORKING
#FXML
public void btnCancelOnAction(ActionEvent event){
hide();
}
private void voorwerpToevoegen(){ // we're calling the domaincontroller to add the new item to the repository
switch (itemType)
{
// for the item, add an item by getting the value of each TextField, which are the
// parameters for a constructor of the new item
case WEAPON:
dc.addWeapon(txfAttributes[0].getText(),
Double.parseDouble(txfAttributes[1].getText()),
Integer.parseInt(txfAttributes[2].getText()),
Integer.parseInt(txfAttributes[3].getText()),
Boolean.parseBoolean(txfAttributes[4].getText()));
break;
case KEY:
dc.addKey(txfAttributes[0].getText(),
Double.parseDouble(txfAttributes[1].getText()),
Integer.parseInt(txfAttributes[2].getText()),
Integer.parseInt(txfAttributes[3].getText()));
break;
}
hide();
}
}
At the top you have #FXML private Button btnOK; which is good.
To specify the button's action in Java code, you can use this Java 8 syntax in your buildGUI() method (see JavaFX 8 Event Handling Examples):
btnOK.setOnAction((event) -> addItem());
If you're not using Java 8, see UI Control Sample.
You don't need the methods annotated with #FXML.

How to load fxml file inside Pane?

If we have a Stage then Scene includes 2 Panes
the 1st Pane contains Button and the 2nd Pane is empty
could we load other fxml file inside this 2nd Pane?
fxml1: VBox
|_Pane1-->Button
|_Pane2
///////////////
fxml2: Pane--> Welcome to fxml 2
"when we click the button load the fxml2 inside Pane2 of fxml1"
Then after click
====I finally found this works after trying !====Thank you guys
#FXML Pane secPane;
public void loadFxml (ActionEvent event) {
Pane newLoadedPane = FXMLLoader.load(getClass().getResource("/application/fxml2.fxml"));
secPane.getChildren().add(newLoadedPane);
}
I finally found this works after trying !
#FXML Pane secPane;
public void loadFxml (ActionEvent event) {
Pane newLoadedPane = FXMLLoader.load(getClass().getResource("/application/fxml2.fxml"));
secPane.getChildren().add(newLoadedPane);
}
Just replacing the field in your controller class won't change the scene graph.
secPane is just a reference to a node in the scene graph.
If secPane is just a placeholder, you could replace it in the parent's child list:
public void loadFxml (ActionEvent event) {
// load new pane
Pane newPane = FXMLLoader.load(getClass().getResource("/application/Login2.fxml"));
// get children of parent of secPane (the VBox)
List<Node> parentChildren = ((Pane)secPane.getParent()).getChildren();
// replace the child that contained the old secPane
parentChildren.set(parentChildren.indexOf(secPane), newPane);
// store the new pane in the secPane field to allow replacing it the same way later
secPane = newPane;
}
This assumes of course, that getClass().getResource("/application/Login2.fxml") yields the correct resource and does not return null (which happens if no resource with the given name is available)
You can implement something like this :
public void start(Stage primaryStage) throws IOException {
primaryStage.setTitle("Title");
primaryStage.setScene(createScene(loadMainPane("path_of_your_fxml")));
primaryStage.show();
}
private Pane loadMainPane(String path) throws IOException {
FXMLLoader loader = new FXMLLoader();
Pane mainPane = (Pane) loader.load(
getClass().getResourceAsStream(path));
return mainPane;
}
private Scene createScene(Pane mainPane) {
Scene scene = new Scene(mainPane);
return scene;
}
Then you can create a separate class call Navigation to store all your fxml paths:
public class Navigator {
private final String P1;
private final String P2;
//then you can implement getters...
public String getP1() {
return P1;
}
public String getP2() {
return p2;
}
private static FxmlController Controller;
public static void loadPane(String fxml) {
try {
FxmlController.setPane(
(Node) FXMLLoader.load(Navigator.class.getResource(fxml)));
} catch (IOException e) {
e.printStackTrace();
}
}
public Navigator() throws IOException {
this.P1 = "p1.fxml";
this.P2 = "p2.fxml";}
Then you can load your pane in your button like below:
#FXML
private void btnAction(ActionEvent event) throws IOException {
Navigator.load(new Navigator().getP1());
..
.

How to reset a selected path from file chooser in JavaFx

I have a reset and Draw button on a slide panel. I choose a desired file from file chooser, which is in RootLayout class, and pass the file path to a controller class. Then it does some processes and initializes field in DataCunstructor class. By clicking on Draw a TreeTableView will be shown on slide pane, which is in MainController class. When I click my reset button the table will be cleared but I do not know how to reset the chosen path. After reseting if I click Draw again the same treetable comes up. and If I choose another file and hit Draw, the program breaks.
How can I reset all fields including the path to null, and be able to choose another file and process that one?
Here is my Draw and Reset in MainController class:
public void treeTableDraw(ActionEvent event) {
drawTable();//creates the TreeTableView
numberOfFunctions= dc.getFuncAll().size();
numberOfOrganizations = dc.getSortedAssignedOrg().size();
funcLabel.setText(numberOfFunctions+"");//set Lable value
orgLabel.setText(numberOfOrganizations + "");//set Lable value
}
public void treeTableReset(ActionEvent event){
funcLabel.setText("0");//reset Label
orgLabel.setText("0");
treeTable.getColumns().clear(); //clears columns (TreeTable)
///////////////////////////////////////
//non of the following did the path reset//
///////////////////////////////////////
//dc = new DataConstructor();
//Controller controller = new Controller();
//controller.setPath(null);
RootLayoutController rlc = loader.getController();
rlc.reset();
}
My File Chooser in RootLayout class:
#FXML
private void handleOpen() {
FileChooser fileChooser = new FileChooser();
// Set extension filter
FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter(
"3lgm2 files (*.z3lgm)", "*z3lgm");
fileChooser.getExtensionFilters().add(extFilter);
// Show save file dialog
File file = fileChooser.showOpenDialog(main.getPrimaryStage());
path = file.toString();
if (path != null) {
new Controller(path);
}
}
public void reset(){
path = null;
}
I add OverView at the center of rootlayout here at main class:
public class Main extends Application {
private Stage primaryStage;
private BorderPane rootLayout;
//private ObservableList<DataConstructor> treeTableData = FXCollections.observableArrayList();
#Override
public void start(Stage primaryStage) {
this.primaryStage = primaryStage;
this.primaryStage.setTitle("IT-Saturation");
initRootLayout();
showOverView();
}
private void showOverView() {
try{
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("/view/OverView.fxml"));
AnchorPane overView = (AnchorPane) loader.load();
rootLayout.setCenter(overView);
}catch(IOException e){
e.printStackTrace();
}
}
private void initRootLayout() {
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("/view/RootLayout.fxml"));
rootLayout = (BorderPane) loader.load();
//show scene containing the root layout
Scene scene = new Scene(rootLayout);
scene.getStylesheets().add(
getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
//gives controller access to main app
RootLayoutController controller = loader.getController();
controller.setMainApp(this);
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
File file = getFilePath();
if (file != null) {
loadDataFromFile(file);
}
}
/**
* Returns the main stage.
* #return primaryStage
*/
public Stage getPrimaryStage() {
return primaryStage;
}
public static void main(String[] args) {
launch(args);
}
}
Just declare a reset() in your RootLayout class:
public class RootLayout {
private Path path;
#FXML
private void handleOpen() {
...
path = file.toString();
}
public void reset() {
path= null;
}
}
Never construct the constructor using the keyword new, always get it from the FXMLLoader.
public class MainController {
...
RootLayoutController controller = loader.getController();
controller.reset();
...
}

How to rerun a program in JavaFX

I have 2 controller for 2 fxml files. In one controller I have a handleOpen function which opens a file chooser and gives the path to a Class which I have called model. Then on the other controller a function, treeTableDraw gets this path, after clicking on Draw Button and runs the program. I have Another Button to reset the program. It sets the result back to null, but when open another file to run, the program crashes, because the path is null. How can I reset the program and make it use the new path which is selected from open file chooser?
//Gets the path from model and runs the program
public void treeTableDraw(ActionEvent event) {
new Controller(model.getText());
drawTable();
numberOfFunctions = dc.getFuncAll().size();
numberOfOrganizations = dc.getSortedAssignedOrg().size();
funcLabel.setText(numberOfFunctions + "");
orgLabel.setText(numberOfOrganizations + "");
btnDraw.setDisable(true);
}
/**
* Clrears TreeTableView and sets back labels
*
* #param event
*/
public void treeTableReset(ActionEvent event) {
btnDraw.setDisable(false);
model.setText(null);
funcLabel.setText("0");
orgLabel.setText("0");
treeTable.getColumns().clear();
}
This is RootLayout class which has open file function:
#FXML
private void handleOpen() {
FileChooser fileChooser = new FileChooser();
// Set extension filter
FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter(
"3lgm2 files (*.z3lgm)", "*z3lgm");
fileChooser.getExtensionFilters().add(extFilter);
// Show open file dialog
File file = fileChooser.showOpenDialog(main.getPrimaryStage());
if (file != null) {
path = file.toString();
model.setText(path);
}
}
Here is the model class
public class Model {
private final StringProperty text = new SimpleStringProperty();
public StringProperty textProperty() {
return text;
}
public final String getText() {
return textProperty().get();
}
public final void setText(String text) {
textProperty().set(text);
}
}
This is the main, where I combine two fxmls and set stage:
public class Main extends Application {
private Stage primaryStage;
private BorderPane rootLayout;
private Model model = new Model();
#Override
public void start(Stage primaryStage) {
this.primaryStage = primaryStage;
this.primaryStage.setTitle("IT-Saturation");
initRootLayout();
showOverView();
}
private void showOverView() {
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("/view/OverView.fxml"));
loader.setController(new OverViewController(model));
AnchorPane overView = (AnchorPane) loader.load();
rootLayout.setCenter(overView);
} catch (IOException e) {
e.printStackTrace();
}
}
private void initRootLayout() {
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("/view/RootLayout.fxml"));
loader.setController(new RootLayoutController(model));
rootLayout = (BorderPane) loader.load();
// show scene containing the root layout
Scene scene = new Scene(rootLayout);
scene.getStylesheets().add(
getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
// gives controller access to main
RootLayoutController controller = loader.getController();
controller.setMainApp(this);
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Returns the main stage.
*
* #return primaryStage
*/
public Stage getPrimaryStage() {
return primaryStage;
}
public static void main(String[] args) {
launch(args);
}
public void showMostComputerizedStatistics() {
try {
// Load the fxml file and create a new stage for the popup.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class
.getResource("view/BirthdayStatistics.fxml"));
AnchorPane page = (AnchorPane) loader.load();
Stage dialogStage = new Stage();
dialogStage.setTitle("Birthday Statistics");
dialogStage.initModality(Modality.WINDOW_MODAL);
dialogStage.initOwner(primaryStage);
Scene scene = new Scene(page);
dialogStage.setScene(scene);
// Set the persons into the controller.
MostComputerizedController controller = loader.getController();
dialogStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
}
The problem was not the path. I had to reset the data which got initialized at the the former run of program. So after setting path to null I just newed the instance of class which had reference to data.
...
public void treeTableReset(ActionEvent event) {
btnDraw.setDisable(false);
//model.setText(null);
funcLabel.setText("0");
orgLabel.setText("0");
treeTable.getColumns().clear();
dc = new DataConstructor();
}

Categories

Resources