I am creating a board game with JavaFX. I have two controllers for my project and would like to access variables and methods from the main controller. My main controller has most of the game logic and UI but I would like to have a popup window for special cases. Right now I have the popup window, but would like to use variables from the main controller in the popup window controller. I was able to get variables from the secondary controller to the main controller but not the other way around. Any help would be appreciated.
Here is the code in the main controller that opens the new popup window:
public void newWindow() {
try {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("player.fxml"));
//Parent root = fxmlLoader.load();
Stage playerStage = new Stage();
playerStage.setTitle("Pick your move");
playerStage.initStyle(StageStyle.UNDECORATED);
playerStage.setScene(new Scene((Pane) fxmlLoader.load()));
playerStage.show();
ControllerPopup controller =
fxmlLoader.<ControllerPopup>getController();
this.controller = controller;
if(currentCard.getMoves() == 10){
controller.handleTen();
}
else if(currentCard.getMoves() == 11){
controller.handleEleven();
}
else{
controller.handleSeven();
}
disablePawns(bluePawns);
} catch (Exception e) {
e.printStackTrace();
}
}
And here is the secondary controller:
public class ControllerPopup implements Initializable{
#FXML
private RadioButton button1,button2,button3,button4;
#FXML
private Button closeButton;
private RadioButton selectedButton;
private ToggleGroup group;
Controller controller;
#Override
public void initialize(URL location, ResourceBundle resources) {
group = new ToggleGroup();
button1.setToggleGroup(group);
button2.setToggleGroup(group);
button3.setToggleGroup(group);
button4.setToggleGroup(group);
}
public void handleTen(){
button1.setSelected(true);
button1.setText("Move forward 10");
button2.setText("Move backwards 1");
button3.setDisable(true);
button4.setDisable(true);
}
public void handleEleven(){
button1.setSelected(true);
button1.setText("Swap with another pawn");
button2.setText("Move forward 11");
button3.setDisable(true);
button4.setDisable(true);
}
public void handleSeven(){
button1.setSelected(true);
button1.setText("Move forward 1 and 6");
button2.setText("Move forward 2 and 5");
button3.setText("Move forward 3 and 4");
button4.setText("Move forward 7");
}
#FXML
public void handleCloseButtonAction(ActionEvent event) {
selectedButton = (RadioButton) group.getSelectedToggle();
Stage stage = (Stage) closeButton.getScene().getWindow();
stage.close();
}
public RadioButton getSelectedButton(){
return selectedButton;
}
You can just create the appropriate set methods in the "secondary" controller and pass the values to them from the main controller:
controller.setXXX(...);
Related
Currently I have a main controller which controls adding elements to a Border Pane's left pane for navigation and center pane for content. I am also using fx:root to load new FXML layouts, each with a separate controller into the center pane of the Border Pane. Trying to call the setCenter method of the MainController from a center pane controller my center pane is not updated. I'm guessing the newly created controller is not being associated with the mainPane in the main Controller. How do I associate them?
Main Ctonroller
This loads both the center and left panes with the appropriate views and associated controllers using either button.
public class MainController {
#FXML
BorderPane mainPane;
#FXML
Button orderBtn, adminBtn;
#FXML
static
Label statusTxt;
public void initialize(){
orderBtn.setOnAction(event -> {
setCenter(new ControllerConnector("/view/OrderView.fxml"));
setNav(new ControllerConnector("/view/OrderNav.fxml"));
});
adminBtn.setOnAction(event -> {
setCenter(new ControllerConnector("/view/ProductView.fxml"));
setNav(new ControllerConnector("/view/AdminNav.fxml"));
});
public static void updateStatus(String string) {
statusTxt.setText(string);
}
public void setCenter(ControllerConnector connector){
this.mainPane.setCenter(connector);
}
public void setNav(ControllerConnector connector){
this.mainPane.setLeft(connector);
}
}
Product Controller
The search function works in this controller which is loaded from the associated view so I know the controller was initialized. When the newBtn is clicked nothing happens.
public class ProductController {
#FXML
TableView<Product> productTable;
#FXML
TableColumn prodCol, sizeCol, categoryCol, priceCol;
#FXML
TextField searchTxt;
#FXML
Button searchBtn, newBtn, editBtn, deleteBtn;
public void initialize() throws SQLException {
ObservableList<Product> products = ProductDAO.getProducts();
prodCol.setCellValueFactory(new PropertyValueFactory<Product, String>("name"));
sizeCol.setCellValueFactory(new PropertyValueFactory<Product, String>("size"));
priceCol.setCellValueFactory(new PropertyValueFactory<Product, Double>("price"));
categoryCol.setCellValueFactory(new PropertyValueFactory<Product, String>("category"));
productTable.setItems(products);
searchBtn.setOnAction(event -> {
ObservableList<Product> searchProducts = FXCollections.observableArrayList();
String searchString = searchTxt.getText();
for (Product product : products) {
if(product.getName().contains(searchString) || product.getSize().contains(searchString)){
searchProducts.add(product);
}
}
products.removeAll(products);
products.addAll(searchProducts);
});
searchTxt.setOnMouseClicked(event -> {
products.removeAll(products);
try {
products.addAll(ProductDAO.getProducts());
} catch (SQLException exception) {
exception.printStackTrace();
}
});
newBtn.setOnAction(event -> {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/MainView.fxml"));
try {
loader.load();
} catch (IOException e) {
e.printStackTrace();
}
MainController mainController = loader.getController();
mainController.setCenter(new ControllerConnector("/view/NewProductView.fxml"));
});
}
}
ControllerConnector
Using this class to create a Node to pass into the Border Pane set mehtods.
public class ControllerConnector extends GridPane {
public ControllerConnector(String fxmlPath){
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(fxmlPath));
fxmlLoader.setRoot(this);
try{
fxmlLoader.load();
}catch (IOException e){
e.printStackTrace();
}
}
}
I'm making an application with JavaFX and Scene Builder.
I have two controllers:Controller and FontController
I have Main class that launch my program and open Stage with first fontroller (Controller)
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
try {
Parent root = FXMLLoader.load(getClass().getResource("/card/card.fxml"));
Scene scene = new Scene(root, 1600, 600);
primaryStage.setScene(scene);
scene.getStylesheets().add(getClass().getResource("style.css").toExternalForm());
primaryStage.initStyle(StageStyle.UNDECORATED);
primaryStage.setMaximized(true);
primaryStage.setResizable(true);
primaryStage.getIcons().add(new Image("card/resources/logo-icon.png"));
primaryStage.show();
//adding resize and drag primary stage
ResizeHelper.addResizeListener(primaryStage);
//assign ALT+ENTER to maximize window
final KeyCombination kb = new KeyCodeCombination(KeyCode.ENTER, KeyCombination.CONTROL_DOWN);
scene.addEventHandler(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
if (kb.match(event)) {
primaryStage.setMaximized(!primaryStage.isMaximized());
primaryStage.setResizable(true);
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}
There is a label and a button in Controller. When I click on the button a method is called and new window with second controller appears(FontController):
#FXML private Button btnFont;
#FXML private Label category1
#FXML
void changeFont(ActionEvent event) {
try {
FXMLLoader fxmlLoader = new
FXMLLoader(getClass().getResource("font.fxml"));
Parent rootFont = (Parent) fxmlLoader.load();
Stage stage = new Stage();
stage.setTitle("Select Font");
stage.setScene(new Scene(rootFont));
stage.show();
} catch (Exception e) {
System.out.println("can't load new window");
}
}
There is the button "OK" and label in FontCOntroller:
#FXML private Label fontLabel;
#FXML private Button btnFontOk;
Please tell me, what should I do to send and apply text from label in FontController when I click on the burtton "OK" to label in Controller?
SOLUTION FOUND:
I created class "Context" in my project directory to make all controllers communicate each other. You can add as many controllers as you want there.
Here it looks like:
package card;
public class Context {
private final static Context instance = new Context();
public static Context getInstance() {
return instance;
}
private Controller controller;
public void setController(Controller controller) {
this.controller=controller;
}
public Controller getController() {
return controller;
}
private FontController fontController;
public void setFontController(FontController fontController) {
this.fontController=fontController;
}
public FontController getFontController() {
return fontController;
}
}
Controller:
I created getters and setters (ALT + Insert in IDEA) for Label that I wanna change
public Label getCategory1() {
return category1;
}
public void setCategory1(Label category1) {
this.category1 = category1;
}
To get FontController variables and methods through Context class I placed line of code
//getting FontController through Context Class
FontController fontCont = Context.getInstance().getFontController();
I registered Controller in Context class through my initialize method (my class implements Initializable)
#FXML
public void initialize(URL location, ResourceBundle resources) {
//register Controller in Context Class
Context.getInstance().setController(this);
}
FontController:
to get Controller variables and methods I placed this code:
//getting Controller variables and methods through Context class
Controller cont = Context.getInstance().getController();
I also registered FontController in Context class through initialize method:
#Override
public void initialize(URL location, ResourceBundle resources) {
//register FontController in Context Class
Context.getInstance().setFontController(this);
}
Method that send text and text color from label in this FontController to label in Controller when I click on button:
#FXML
void applyFont(ActionEvent event) {
cont.getCategory1().setText(fontLabel.getText());
cont.getCategory1().setTextFill(fontLabel.getTextFill());
}
*By creating Context class you can make controllers communicate each other and create as many controllers as you want there. Controllers see variables and methods of each other
Every time I ran JavaFX app I encounter the error below.
15:11:52.778 [JavaFX Application Thread] ERROR org.fhl.Manifesto - javafx.fxml.LoadException:
/C:/ManGenFX/target/classes/dataController.fxml
/C:/ManGenFX/target/classes/ManifestoMain.fxml:7
But it only happens if I add the initialize method on Main Controller.
public class MainController{
#FXML
GenerateController genCont;
#FXML
private Pane generatePane;
#FXML
AnchorPane mainFx;
private Node source;
//Other Button, TextFields and Labels declaration
#FXML
private void browseFile(ActionEvent x) {
//Browse file definition
}
#FXML
private void savePath(ActionEvent x) {
//Save file definition
}
//Problem with this method
#FXML
public void initialize() {
System.out.println("Initialize generate controller panel");
logger.info("Initialize generate controller panel");
genCont.init(this);
}
}
If I remove the initialize method on MainController class it will not throw any error but nothing happens either if I click on the generate button which is defined in GenerateController class below.
public class GenerateController {
#FXML
Button btnGenerate;
#FXML
Pane paneGen;
#FXML
public void generateMan(ActionEvent event) {
//generateMan body when Generate button is clicked
}
public void init(MainController mc) {
System.out.println("Init mainControl");
mainControl = mc;
}
public void validate() {
//Definition of method body here
}
}
This is the main Class
public class Man extends Application {
private Stage mainStage;
#Override
public void start(Stage primaryStage) {
this.mainStage = primaryStage;
this.mainStage.setTitle("Manifest");
try{
Parent root = FXMLLoader.load(getClass().getResource("/ManifestoMain.fxml"));
Scene sMain = new Scene(root);
mainStage.setScene(sMain);
mainStage.show();
}catch(IOException ioE) {
logger.error(ioE);
}
}
public static void main(String[] args) {
launch(args);
}
}
Also, I placed my dataController.fxml, ManifestoMain.fxml, and generateView.fxml under resources folder but it don't have any problem accessing the files. Appreciate help on this.
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.
i made a mainEntredController which contains a TabPane with two tabs (HomeController) and (PatientController), The MainEntred Controle the Parameter Passage Between this two tabs but i have liitls issu that the tabs doesnet reconize the Parent (MainEntredController) or each other. it retuns an error inn this line
*** labelpatient.setText(mainec.LoadtxtFromlabelhometabhome()); ******
the idea is to get a text from the first tab to show it in a label in the second tab
hear's my code
the first is the MainEntred Class the Container
public class MainEntredController {
#FXML MenuItem closemi;
#FXML HomeController homecontroller;
#FXML PatientController patientcontroller;
public void initialize(URL location, ResourceBundle resources) {
System.out.println("Application Started");
homecontroller.intit(this);
patientcontroller.init(this);
}
public void ClickMenuItemClose(){
System.exit(0);
}
public String LoadtxtFromlabelhometabhome() {
// System.out.println(homecontroller.labelhome.getText());
return homecontroller.labelhome.getText();
}
public void setTabpatientlabel(String text) {
//patientcontroller.labelpatient.setText(text);
}
}
the Second is the First Tab HomeController
public class HomeController {
public MainEntredController mainec;
#FXML public Label labelhome;
#FXML private TextField tfhome;
#FXML private Button savehome;
#FXML private Button sendhome;
//public HomeController(){}
public void btnSaveHomeClicked(ActionEvent event){
System.out.println("le boutton save home est cliqué");
labelhome.setText(tfhome.getText());
}
public void btnSendHomeClicked(ActionEvent event){
System.out.println("le boutton send home est cliqué");
// send data to tab patient
mainec.setTabpatientlabel(labelhome.getText());
}
public void intit(MainEntredController mainEntredController) {
mainec = mainEntredController;
}
}
the Third one is the Second tab PatientController
public class PatientController {
public MainEntredController mainec;
#FXML public Label labelpatient;
#FXML private TextField tfpatient;
#FXML private Button savepatient;
#FXML private Button loadpatient;
public void btnSavePatientClicked(ActionEvent event){
System.out.println("le boutton save Patient est cliqué");
labelpatient.setText(tfpatient.getText());
}
public void btnLoadPatientClicked(ActionEvent event){
System.out.println("le boutton Load patient est cliqué");
labelpatient.setText(mainec.LoadtxtFromlabelhometabhome());
}
public void init(MainEntredController mainEntredController) {
mainec = mainEntredController;
}
}
And the FXML File of the MainEntred
<fx:include id="homecontroller" source="home.fxml" />
<fx:include id="patientcontroller" source="patient.fxml" />
i guess those two are madding the problem any advise how to solve it Please
i dont know why i am having java.lang.NullPointerException at the line declarred erlier he cant get the source (unknown source)
If i understand correctly then you want to communicate between controller. well, this can be done pretty easy.
in you main type something likes this:
FXMLLoader loader = new FXMLLoader(Main.class.getResource("your.fxml"));
Parent node = loader.load(); // this loads view
YourController instance = loader.getController();
some_class.initialize(instance);
Finelly i found what is wrong. the thing is that in the declaration in the MainController of the two Tabs we put the fx:id declared on the fxml file
and adding the word "Controller"
like this
#FXML private HomeController homeController;
#FXML private PatientController patientController;
the fx:id="home"
the second includes fx:id="patient"