Hi I'm very new to Java and Javafx so I hope you can help me with a problem. I'm trying to do a proper MVC Pattern with Scene Builder but my code doesn't work and I don't know why.
I have understood that the Model class have to get the data and the Controller class should use and handle the data but I have big problems with that the Scene builder does accept one controller class for one FXML file. That is why I tried it with getters and setters to have the connection between Model and Controller.
But also I think I am not doing it right.
Main class:
package application;
import javafx.application.Application;
import javafx.fxml.*;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application{
#Override
public void start(Stage primaryStage) throws Exception{
try {
Parent root = FXMLLoader.load(getClass().getResource("/login/LoginUI.fxml"));
Scene scene = new Scene(root, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
public void startApp(Stage Stage) throws Exception{
try {
Parent root = FXMLLoader.load(getClass().getResource("/financeApp/UI.fxml"));
Scene scene = new Scene(root, 1022, 593);
Stage.setScene(scene);
Stage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
Controller class:
package login;
import application.Main;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import javafx.stage.Stage;
import login.ModelLogin;
public class ControllerLogin {
#FXML TextField userNameField;
#FXML PasswordField passwordField;
#FXML Button loginButton;
ModelLogin model = new ModelLogin();
public void setUserName() {
model.setUserNameField(userNameField);
}
public void setPassword() {
model.setPasswordField(passwordField);
}
public void login(ActionEvent event) {
if (model.getUserNameField().getText().equals("test") && model.getPasswordField().getText().equals("1234")) {
Stage stage = new Stage();
Main startUI = new Main();
try {
startUI.startApp(stage);
} catch (Exception e) {
e.printStackTrace();
}
}else {
System.out.println("Try again");
}
}
}
Model class:
package login;
import javafx.scene.control.Button;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
public class ModelLogin {
private TextField userNameField;
private PasswordField passwordField;
public TextField getUserNameField() {
return userNameField;
}
public void setUserNameField(TextField userNameField) {
this.userNameField = userNameField;
}
public PasswordField getPasswordField() {
return passwordField;
}
public void setPasswordField(PasswordField passwordField) {
this.passwordField = passwordField;
}
}
and this is the FXML file created with Scene builder:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.text.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="290.0" prefWidth="400.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="login.ControllerLogin">
<children>
<AnchorPane prefHeight="290.0" prefWidth="400.0">
<children>
<Label alignment="CENTER" layoutX="150.0" layoutY="38.0" prefHeight="30.0" prefWidth="100.0" text="Login">
<font>
<Font name="System Bold" size="20.0" />
</font>
</Label>
<Label layoutX="159.0" layoutY="108.0" text="Benutzername">
<font>
<Font name="System Bold" size="12.0" />
</font>
</Label>
<TextField fx:id="userNameField" layoutX="126.0" layoutY="125.0" onAction="#setUserName" />
<Label layoutX="175.0" layoutY="165.0" text="Passwort">
<font>
<Font name="System Bold" size="12.0" />
</font>
</Label>
<PasswordField fx:id="passwordField" layoutX="126.0" layoutY="182.0" onAction="#setPassword" />
<Button fx:id="loginButton" layoutX="175.0" layoutY="233.0" mnemonicParsing="false" onAction="#login" text="Login" />
</children>
</AnchorPane>
</children>
</VBox>
Folders
I would be happy about some Feedback. Thank you!!
All UI elements should be in the view.
A model should have only information and logic that the view and controller use.
public class ModelLogin {
private final String userName;
private final String password;
ModelLogin(String userName, String password) {
this.userName = userName;
this.password = password;
}
boolean isCorrectCredentials(String userName, String password){
return this.userName.equals(userName)&&this.password.equals(password);
}
}
The controller "wires" the view and the model: it handles credential verification and
change of scene.
Note that it is modified to accept a reference of Main so it can change scene:
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
public class ControllerLogin {
#FXML TextField userNameField;
#FXML PasswordField passwordField;
private ModelLogin model;
private Main main;
#FXML
void initialize() {
model = new ModelLogin("test", "1234");
}
public void login(ActionEvent event) {
if (model.isCorrectCredentials(userNameField.getText(), passwordField.getText() )) {
try {
main.startApp();
} catch (Exception e) {
e.printStackTrace();
}
}else {
System.out.println("Try again");
}
}
void setMain(Main main) {
this.main = main;
}
}
The text field onAction is not used, so it was removed from the fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.text.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="290.0" prefWidth="400.0" xmlns="http://javafx.com/javafx/8"
xmlns:fx="http://javafx.com/fxml/1" fx:controller="login.ControllerLogin">
<children>
<AnchorPane prefHeight="290.0" prefWidth="400.0">
<children>
<Label alignment="CENTER" layoutX="150.0" layoutY="38.0" prefHeight="30.0" prefWidth="100.0" text="Login">
<font>
<Font name="System Bold" size="20.0" />
</font>
</Label>
<Label layoutX="159.0" layoutY="108.0" text="Benutzername">
<font>
<Font name="System Bold" size="12.0" />
</font>
</Label>
<TextField fx:id="userNameField" layoutX="126.0" layoutY="125.0"/>
<Label layoutX="175.0" layoutY="165.0" text="Passwort">
<font>
<Font name="System Bold" size="12.0" />
</font>
</Label>
<PasswordField fx:id="passwordField" layoutX="126.0" layoutY="182.0" />
<Button fx:id="loginButton" layoutX="175.0" layoutY="233.0" mnemonicParsing="false" onAction="#login" text="Login" />
</children>
</AnchorPane>
</children>
</VBox>
The Main was modified to get a reference to the controller, and to change scene:
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application{
private Stage primaryStage;
private Parent root;
#Override
public void start(Stage primaryStage) throws Exception{
try {
this.primaryStage = primaryStage;
FXMLLoader loader = new FXMLLoader(getClass().getResource("/login/LoginUI.fxml"));
root = loader.load();
ControllerLogin controller = loader.getController();
controller.setMain(this);
Scene scene = new Scene(root, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
public void startApp() throws Exception{
try {
root = FXMLLoader.load(getClass().getResource("/financeApp/UI.fxml"));
Scene scene = new Scene(root, 1022, 593);
primaryStage.setScene(scene);
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
Related
I have tried researching various pages regarding how to switch scenes or even stages with no luck to help my specific case. I am trying to create a program with a login form that manages a school library.
I have a login form that authenticates the user input with the method validateLogin(). If the login is successful (i.e. isLoginSuccess = true), I want my program to close primaryStage and open a new stage/window with the library management system.
Is it better to switch the scene on primaryStage or close primaryStage and open a new stage? How do you do that and should you do that from Main.java or is it fine to do it from LoginController.java? I'm having trouble switching scenes or even just creating a new stage from LoginController.java since primaryStage isn't recognized in that class.
Main.java
package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class Main extends Application
{
#Override
public void start(Stage primaryStage) throws Exception
{
Parent root = FXMLLoader.load(getClass().getResource("login.fxml"));
primaryStage.initStyle(StageStyle.UNDECORATED);
primaryStage.setScene(new Scene(root, 520, 400));
primaryStage.setResizable(false);
primaryStage.show();
}
//main() method is ignored in correctly deployed JavaFX application.
public static void main(String[] args)
{
launch(args);
}
}
LoginController.java
package sample;
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.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import javafx.event.ActionEvent;
import javafx.stage.StageStyle;
import java.io.File;
import java.util.ResourceBundle;
//Can be removed, StackOverflow import
import java.io.BufferedReader;
import java.io.FileReader;
import java.net.URL;
public class LoginController implements Initializable
{
#FXML
private Button cancelButton;
#FXML
private Label loginMessageLabel;
#FXML
private ImageView brandingImageView;
#FXML
private ImageView lockImageView;
#FXML
private TextField usernameTextField;
#FXML
private PasswordField enterPasswordField;
#Override
public void initialize(URL url, ResourceBundle resourceBundle)
{
File brandingFile = new File("Images/LibManager.png");
Image brandingImage = new Image(brandingFile.toURI().toString());
brandingImageView.setImage(brandingImage);
File lockFile = new File("Images/LoginLock.png");
Image lockImage = new Image(lockFile.toURI().toString());
lockImageView.setImage(lockImage);
}
//Activates validateLogin() if there is input in text fields.
public void loginButtonOnAction(ActionEvent event)
{
if (usernameTextField.getText().isBlank() == false && enterPasswordField.getText().isBlank() == false)
{
validateLogin();
}
else
{
loginMessageLabel.setText("Please enter username and password");
}
}
//Closes login window.
public void cancelButtonOnAction(ActionEvent event)
{
Stage stage = (Stage) cancelButton.getScene().getWindow();
stage.close();
}
//Compares user input to txt file contents to authenticate.
public void validateLogin() {
{
try {
String location = "userdatabase.txt";
String username = usernameTextField.getText();
String password = enterPasswordField.getText();
FileReader fr = new FileReader(location);
BufferedReader br = new BufferedReader(fr);
String line, user, pass;
boolean isLoginSuccess = false;
while ((line = br.readLine()) != null) {
user = line.split(" ")[1].toLowerCase();
pass = line.split(" ")[2].toLowerCase();
if (user.equals(usernameTextField.getText()) && pass.equals(enterPasswordField.getText())) {
isLoginSuccess = true;
break;
}
}
if (!isLoginSuccess)
{
/*
public void startLibManager(Stage primaryStage) {
FXMLLoader loader = new FXMLLoader(getClass().getResource("LibDatabase.fxml"));
Parent mainCallWindowFXML = loader.load();
secondaryStage.initStyle(StageStyle.UNDECORATED);
secondaryStage.setScene(new Scene(root, 520, 400));
secondaryStage.setResizable(false);
secondaryStage.show();
}
*/
/*
//use one of the components on your scene to get a reference to your scene object.
Stage stage = (Stage)tfCallerName.getScene.getWindow();//or use any other component in your controller
Scene mainCallWindow = new Scene (mainCallWindowFXML, 800, 600);
stage.setScene(newCallDetails);
stage.show(); //this line may be unnecessary since you are using the same stage.
*/
}
fr.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
login.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.PasswordField?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.BorderPane?>
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="520.0" style="-fx-background-color: #FFFFFF;" xmlns="http://javafx.com/javafx/15.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.LoginController">
<left>
<AnchorPane prefHeight="407.0" prefWidth="227.0" BorderPane.alignment="CENTER">
<children>
<ImageView fx:id="brandingImageView" fitHeight="400.0" fitWidth="226.0" pickOnBounds="true" preserveRatio="true">
<image>
<Image url="#../../Images/LibManager.png" />
</image>
</ImageView>
</children></AnchorPane>
</left>
<right>
<AnchorPane prefHeight="400.0" prefWidth="332.0" style="-fx-background-color: FFFFFF;" BorderPane.alignment="CENTER">
<children>
<ImageView fx:id="lockImageView" fitHeight="32.0" fitWidth="46.0" layoutX="134.0" layoutY="65.0" pickOnBounds="true" preserveRatio="true">
<image>
<Image url="#../../Images/LoginLock.png" />
</image>
</ImageView>
<Label layoutX="23.0" layoutY="157.0" prefHeight="17.0" prefWidth="60.0" text="Username" textFill="#01989e" />
<TextField fx:id="usernameTextField" layoutX="96.0" layoutY="152.0" prefWidth="173.0" promptText="Username" />
<Label layoutX="26.0" layoutY="205.0" text="Password" textFill="#01989e" />
<PasswordField fx:id="enterPasswordField" layoutX="96.0" layoutY="200.0" prefHeight="27.0" prefWidth="173.0" promptText="Password" />
<Button fx:id="loginButton" layoutX="22.0" layoutY="294.0" mnemonicParsing="false" onAction="#loginButtonOnAction" prefHeight="27.0" prefWidth="249.0" style="-fx-background-color: ff914d;" text="Login" textFill="WHITE" />
<Button fx:id="cancelButton" layoutX="22.0" layoutY="342.0" mnemonicParsing="false" onAction="#cancelButtonOnAction" prefHeight="27.0" prefWidth="249.0" style="-fx-background-color: ff914d;" text="Cancel" textFill="WHITE" />
<Label fx:id="loginMessageLabel" layoutX="26.0" layoutY="248.0" prefHeight="17.0" prefWidth="160.0" textFill="RED" />
</children></AnchorPane>
</right>
</BorderPane>
#Loritt I might be a bit late, but I use this method to change scenes when using JavaFX.
If you are going to be changing back and forth between scene, this method keeps you from having to write redundant code.
Stage stage;
Parent scene;
public void switchViews(ActionEvent event, String fileLocation) throws IOException {
stage = (Stage) ((Button) event.getSource()).getScene().getWindow();
scene = FXMLLoader.load(getClass().getResource(fileLocation));
stage.setScene(new Scene(scene));
stage.show();
}
I usually put this method inside the controller class which is completely acceptable as it has to do with the controlling of the visuals in the application and not any business logic.
Also, when I was originally learning how to do this myself and then pass information between the scenes I used this video and youtuber who has some good content on JavaFX/Scene Builder. (Link: https://www.youtube.com/watch?v=XCgcQTQCfJQ)
Hope this helps answer your question.
Happy coding! :)
I've started a project requested by our instructor building an application in JavaFX.
I used SceneBuilder for FXML Part when I want to add buttons on gridpane, but it didn't work. The connection with the database is okay. On the first part of the application, the user logs in. After that he/she comes on UserWindow, but the buttons aren't displayed.
File Main.java:
package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
Stage window;
Controller c;
ControllerB cB;
#Override
public void start(Stage primaryStage) throws Exception {
window = primaryStage;
FXMLLoader f = new FXMLLoader();
FXMLLoader f2 = new FXMLLoader();
f.setLocation(Main.class.getResource("sample.fxml"));
f2.setLocation(Main.class.getResource("UserWindow.fxml"));
Parent root = f.load();
Parent root2 = f2.load();
Scene s = new Scene(root, 500, 340);
// Controllers
c = f.getController();
c.setMain(this);
c.setScene2(new Scene(root2, 900, 500));
cB = f2.getController();
cB.setMain(this);
cB.setScene(s);
window.setTitle("Remote Works");
window.setScene(s);
window.show();
}
public static void main(String[] args) {
launch(args);
}
public void setUserData(Object o) {
window.setUserData(o);
}
public void setScene(Scene scene) {
window.setScene(scene);
window.show();
}
public void setScene2(Scene scene) {
window.setScene(scene);
window.show();
}
}
File ControllerB.java:
package sample;
import javafx.collections.*;
import javafx.event.ActionEvent;
import javafx.fxml.*;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.GridPane;
import java.io.IOException;
import java.net.URL;
import java.sql.*;
import java.util.*;
public class ControllerB implements Initializable {
#FXML
private GridPane Box;
#FXML
private Label nameUser;
#FXML
private Label matriculeUser;
#FXML
private Label fonctionUser;
#FXML
private Button deconnect;
private Scene scene1;
private Main main;
ObservableList<String> ecoles;
#Override
public void initialize(URL url, ResourceBundle resourceBundle) {
try {
schoolretrieve();
int size = ecoles.size();
Node[] n = new Node[size];
for (int i = 0; i < n.length; i++) {
FXMLLoader loader = new FXMLLoader(getClass().getResource("Bloc.fxml"));
ControllerItem controller = new ControllerItem();
loader.setController(controller);
n[i] = loader.load();
controller.setButtonInst(ecoles.get(i)); // Until this part all works
Box.getChildren().add(n[i]); // But it doesn't add buttons to GridPane
}
} catch (IOException | NullPointerException | SQLException e) {
//System.out.println(e.getMessage());
e.printStackTrace();
}
}
}
File ControllerItem.java:
package sample;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
public class ControllerItem {
#FXML
private Button buttonInst;
public Button getButtonInst() {
return buttonInst;
}
public void setButtonInst(String s) {
buttonInst.setText(s);
}
}
File Bloc.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<Button fx:id="buttonInst" mnemonicParsing="false" prefHeight="132.0" prefWidth="201.0" stylesheets="#../styles.css" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1" />
File UserWindow.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.image.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.shape.*?>
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="497.0" prefWidth="710.0" styleClass="pane" stylesheets="#../styles.css" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.ControllerB">
<left>
<Pane prefHeight="497.0" prefWidth="156.0" BorderPane.alignment="CENTER">
<children>
<ImageView fitHeight="150.0" fitWidth="151.0" layoutX="11.0" pickOnBounds="true" preserveRatio="true" />
<Circle fill="#e1ebf5" layoutX="78.0" layoutY="88.0" opacity="0.18" radius="74.0" stroke="BLACK" strokeType="INSIDE" />
<Label fx:id="nameUser" layoutX="10.0" layoutY="196.0" prefHeight="50.0" prefWidth="136.0" text="Nom Utilisateur :" />
<Label fx:id="fonctionUser" layoutX="11.0" layoutY="259.0" prefHeight="37.0" prefWidth="136.0" text="Statut : " />
<Label fx:id="matriculeUser" layoutX="10.0" layoutY="306.0" prefHeight="37.0" prefWidth="136.0" text="Identifiant : " />
<Button fx:id="deconnect" layoutX="20.0" layoutY="364.0" mnemonicParsing="false" onAction="#onAction" prefHeight="27.0" prefWidth="117.0" text="deconnexion" />
</children>
</Pane>
</left>
<center>
<Pane opacity="0.0" prefHeight="200.0" prefWidth="200.0" style="-fx-background-color: #f7cc3d;"
BorderPane.alignment="CENTER">
<GridPane fx:id="Box" alignment="TOP_CENTER" hgap="3.0" layoutX="70.0" layoutY="96.0"
nodeOrientation="LEFT_TO_RIGHT" opacity="0.95" prefHeight="305.0" prefWidth="415.0"
styleClass="gridpane" stylesheets="#../ss.css" vgap="3.0"/>
</Pane>
</center>
</BorderPane>
So you have file UserWindow.fxml and you have declared a GridPane in it. I suggest you to don't make a separate .fxml file for the button.
Just do it like this in your ControllerB:
#Override
public void initialize(URL url, ResourceBundle resourceBundle) {
schoolretrieve();
int size = ecoles.size();
//Node[] n = new Node[size];
for (int i = 0; i < size; i++) {
//FXMLLoader loader = new FXMLLoader(getClass().getResource("Bloc.fxml"));
//ControllerItem controller = new ControllerItem();
//loader.setController(controller);
//n[i] = loader.load();
//controller.setButtonInst(ecoles.get(i));
//Box.getChildren().add(n[i]);
Button btn = new Button(ecoles.get(i));
Box.add(btn, 0, i); // Button is added to next row every time.
}
}
I know that there are a lot of questions already answered for this, but I just cant get my head around it. It is a possible duplicate of:
accessing a Pane from another class in javafx
JavaFX change Pane color from a different class
In my app I want to clear the changablePane (StackPane) in the MainWindowController from a mouseEvent in the NotesScreenController so that only the marked as done notes will be displayed.
MainWindowController.java
package gui;
import gui.mainWindow.issues.NotesScreenController;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.control.*;
import javafx.scene.control.Button;
import javafx.scene.control.MenuBar;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;
import java.util.Stack;
public class MainWindowController implements Initializable {
private Stage stage = null;
private StackPane paneNotes = null;
private NotesScreenController secondPane;
#FXML
private AnchorPane mainContainer;
#FXML
private VBox vBoxContainer;
#FXML
private MenuBar menuBarTop;
#FXML
private HBox hBoxContainer;
#FXML
private StackPane navigationSection;
#FXML
private TreeView<Button> treeView;
#FXML
private StackPane changablePane;
public static Button issuesNotes;
//TAB SO FILTRI
#Override
public void initialize(URL location, ResourceBundle resources) {
createTreeView();
}
private void createTreeView() {
Button treeViewHeader = new Button("Zdravo");
TreeItem<Button> treeViewHeaderItem = new TreeItem<>(treeViewHeader);
treeViewHeader.getStylesheets().add("styles/Notes/TreeViewStyles/TreeButton.css");
treeViewHeaderItem.setExpanded(true);
//ROOT FOR ISSUES TRACKING
Button rootForIssues = new Button("Issues Tracker");
Button issuesTable = new Button("Issues Table");
issuesNotes = new Button("Request Notes");
TreeItem<Button> rootForIssuesItem = new TreeItem<>(rootForIssues);
TreeItem<Button> issuesTableItem = new TreeItem<>(issuesTable);
TreeItem<Button> issuesNotesItem = new TreeItem<>(issuesNotes);
rootForIssues.getStylesheets().add("styles/Notes/TreeViewStyles/TreeButton.css");
issuesTable.getStylesheets().add("styles/Notes/TreeViewStyles/TreeButton.css");
issuesNotes.getStylesheets().add("styles/Notes/TreeViewStyles/TreeButton.css");
rootForIssuesItem.setExpanded(true);
rootForIssuesItem.getChildren().addAll(issuesTableItem, issuesNotesItem);
//ROOT ZA NEKOE DRUGO - PROBNO
Button buttonA = new Button("Proba");
Button buttonB = new Button("Proba");
Button buttonC = new Button("Proba");
TreeItem<Button> nodeA = new TreeItem<>(buttonA);
TreeItem<Button> nodeB = new TreeItem<>(buttonB);
TreeItem<Button> nodeC = new TreeItem<>(buttonC);
buttonA.getStylesheets().add("styles/Notes/TreeViewStyles/TreeButton.css");
buttonB.getStylesheets().add("styles/Notes/TreeViewStyles/TreeButton.css");
buttonC.getStylesheets().add("styles/Notes/TreeViewStyles/TreeButton.css");
nodeA.setExpanded(true);
nodeA.getChildren().addAll(nodeB, nodeC);
//ADDING ALL ROOTs OF THE TREEVIEW
treeViewHeaderItem.getChildren().addAll(rootForIssuesItem, nodeA);
issuesNotes.setOnAction(event ->{
clearPane();
URL paneOneUrl = getClass().getClassLoader().getResource("gui/mainWindow/issues/NotesScreen.fxml");
FXMLLoader loader = new FXMLLoader();
NotesScreenController nsc = new NotesScreenController();
loader.setController(nsc);
try {
paneNotes = loader.load(paneOneUrl);
changablePane.getChildren().add(paneNotes);
} catch (IOException e) {
e.printStackTrace();
}
});
issuesTable.setOnAction(event -> {
clearPane();
});
//TREE VIEW
treeView.setId("tree-view-issues");
treeView.getStylesheets().addAll("styles/Notes/TreeViewStyles/TreeView.css");
treeView.setRoot(treeViewHeaderItem);
}
public void clearPane() {
changablePane.getChildren().clear();
}
public void getMainScreenController() {
}
public static Button getIssuesNotes() {
return issuesNotes;
}
public void setStage(Stage stage) {
this.stage = stage;
stage.setResizable(true);
stage.setTitle("SoloStats - Welcome");
}
public void closeStage() {
if (this.stage != null) {
this.stage.close();
}
}
}
MainWindow.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Menu?>
<?import javafx.scene.control.MenuBar?>
<?import javafx.scene.control.MenuItem?>
<?import javafx.scene.control.TreeView?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>
<AnchorPane fx:id="mainContainer" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="1200.0" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="gui.MainWindowController">
<children>
<VBox fx:id="vBoxContainer" layoutX="530.0" layoutY="230.0" prefHeight="25.0" prefWidth="1200.0" AnchorPane.bottomAnchor="572.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<children>
<MenuBar fx:id="menuBarTop" prefHeight="25.0">
<menus>
<Menu mnemonicParsing="false" text="File">
<items>
<MenuItem mnemonicParsing="false" text="Close" />
</items>
</Menu>
<Menu mnemonicParsing="false" text="Edit">
<items>
<MenuItem mnemonicParsing="false" text="Delete" />
</items>
</Menu>
<Menu mnemonicParsing="false" text="Help">
<items>
<MenuItem mnemonicParsing="false" text="About" />
</items>
</Menu>
</menus>
</MenuBar>
</children>
</VBox>
<HBox fx:id="hBoxContainer" layoutX="384.0" layoutY="238.0" prefHeight="600.0" prefWidth="1200.0" style="-fx-background-color: rgb(247, 247, 247);" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="25.0">
<children>
<StackPane fx:id="navigationSection" prefHeight="150.0" prefWidth="300.0" style="-fx-background-color: #222;">
<children>
<TreeView fx:id="treeView" fixedCellSize="24.0" prefHeight="200.0" prefWidth="200.0">
<StackPane.margin>
<Insets left="-25.0" />
</StackPane.margin>
</TreeView>
</children>
</StackPane>
<StackPane fx:id="changablePane" prefHeight="575.0" prefWidth="950.0" stylesheets="#../styles/MainWindow/StackPaneChangable.css" HBox.hgrow="ALWAYS" />
</children>
</HBox>
</children>
</AnchorPane>
NotesScreenController.java
package gui.mainWindow.issues;
import gui.MainWindowController;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.*;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
public class NotesScreenController extends AnchorPane implements Initializable {
private NotesDirectory notesDirectory = new NotesDirectory();
private MainWindowController mainController;
#FXML
private StackPane mainContainer;
#FXML
private VBox vBoxContainer;
#FXML
private HBox hBoxFilterContainer;
#FXML
private ScrollPane scrollPane;
#FXML
private TilePane tilePaneNotesScreen;
#FXML
private ComboBox<String> generalSortBox;
#FXML
private ComboBox<String> sortByNameBox;
#Override
public void initialize(URL location, ResourceBundle resources) {
loadScreen();
}
public void loadScreen() {
try {
notesDirectory.insertNotesToTilePane(tilePaneNotesScreen, notesDirectory.deserializedNotesList(notesDirectory.getFileName(new File("src/notesDirectory")), "src/notesDirectory/"));
} catch (IOException e) {
e.printStackTrace();
}
generalSortBox.setItems(FXCollections.observableArrayList("Flagged", "Date added", "Done Notes"));
sortByNameBox.setItems(FXCollections.observableArrayList("Priority", "Priority", "Priority", "Priority",
"Priority", "Priority", "Priority", "Priority", "Priority", "Priority", "Priority", "Priority",
"Priority", "Priority"));
generalSortBox.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> selected, String oldValue, String newValue) {
if (newValue != null) {
switch (newValue) {
case "Done Notes": try {
//THE ACTION NEED TO TAKE PLACE HERE
notesDirectory.insertNotesToTilePane(tilePaneNotesScreen, notesDirectory.deserializedNotesList(notesDirectory.getFileName(new File("src/notesRecycleBin")), "src/notesRecycleBin/"));
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
});
}
public void setMainController(MainWindowController controller) {
this.mainController = controller;
}
}
NotesScreen
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.ComboBox?>
<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.TilePane?>
<?import javafx.scene.layout.VBox?>
<StackPane fx:id="mainContainer" prefHeight="575.0" prefWidth="950.0" stylesheets="#../../../styles/MainWindow/StackPaneChangable.css" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="gui.mainWindow.issues.NotesScreenController">
<children>
<VBox fx:id="vBoxContainer" prefHeight="575.0" prefWidth="950.0">
<children>
<HBox fx:id="hBoxFilterContainer" stylesheets="#../../../styles/MainWindow/ChoiceBox/HBoxFilters.css">
<children>
<ComboBox fx:id="generalSortBox" prefHeight="25.0" prefWidth="220.0" promptText="Sort by..." stylesheets="#../../../styles/MainWindow/ChoiceBox/ComboBox.css" />
<ComboBox fx:id="sortByNameBox" prefHeight="25.0" prefWidth="220.0" promptText="Choose name" stylesheets="#../../../styles/MainWindow/ChoiceBox/ComboBox.css" />
</children>
</HBox>
<ScrollPane fx:id="scrollPane" fitToHeight="true" fitToWidth="true" hbarPolicy="NEVER" prefHeight="575.0" prefWidth="950.0" stylesheets="#../../../styles/MainWindow/StackPaneChangable.css" VBox.vgrow="ALWAYS">
<content>
<TilePane fx:id="tilePaneNotesScreen" hgap="25.0" prefColumns="4" prefHeight="575.0" prefTileWidth="228.0" prefWidth="950.0" stylesheets="#../../../styles/MainWindow/StackPaneChangable.css" vgap="25.0">
<padding>
<Insets bottom="25.0" left="25.0" right="25.0" top="35.0" />
</padding>
</TilePane>
</content>
</ScrollPane>
</children>
</VBox>
</children>
</StackPane>
Basically all you are missing is setting the main screen controller in the NotesScreenController instance:
FXMLLoader loader = new FXMLLoader();
NotesScreenController nsc = new NotesScreenController();
loader.setController(nsc);
nsc.setMainController(this);
And then from the NotesScreenController you can clear the pane simply by calling
mainController.clearPane();
i.e. you can do:
generalSortBox.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> selected, String oldValue, String newValue) {
if (newValue != null) {
switch (newValue) {
case "Done Notes": try {
//THE ACTION NEED TO TAKE PLACE HERE
mainController.clearPane();
notesDirectory.insertNotesToTilePane(tilePaneNotesScreen, notesDirectory.deserializedNotesList(notesDirectory.getFileName(new File("src/notesRecycleBin")), "src/notesRecycleBin/"));
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
});
Note you have one subtle bug: when you call
paneNotes = loader.load(paneOneUrl);
you are calling the static FXMLLoader.load(URL) method. Since it's a static method, it's not invoked on the FXMLLoader instance you created, and so the previous call to setController(...) is effectively ignored. You need to set the location of the FXMLLoader instance, and then call the no-arg load() method. So you should have:
issuesNotes.setOnAction(event ->{
clearPane();
URL paneOneUrl = getClass().getClassLoader().getResource("gui/mainWindow/issues/NotesScreen.fxml");
FXMLLoader loader = new FXMLLoader();
NotesScreenController nsc = new NotesScreenController();
nsc.setMainController(this);
loader.setController(nsc);
loader.setLocation(paneOneUrl);
try {
// note call to no-arg load() method:
paneNotes = loader.load();
changablePane.getChildren().add(paneNotes);
} catch (IOException e) {
e.printStackTrace();
}
});
Finally, since you are setting the controller from the Java code, you need to remove the fx:controller attribute from the NotesScreen.fxml root element:
<StackPane fx:id="mainContainer" prefHeight="575.0" prefWidth="950.0" stylesheets="#../../../styles/MainWindow/StackPaneChangable.css" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1">
<!-- ... -->
</StackPane>
I created a circuit using Arduino and a couple of components and also a small test application in JavaFX (code shown). The issue is I can't get
COM Ports (using Windows 10) displayed in the combobox however I can see them in the output console (Arduino on COM5). When I run code inspection
in intellij i get
"Problem synopsis - Unchecked call to 'addListener(ChangeListener? super T>)' as a member of raw type 'javafx.beans.value.ObservableValue' (at line 92)"
which seems useful somehow. I thought it meant that the listener is not listening anymore but I honestly don't know.
Code for fxml file:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ComboBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="538.0" prefWidth="734.0" xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1" fx:controller="labrat.Controller">
<top>
<VBox prefHeight="148.0" prefWidth="723.0" style="-fx-background-color: #1f3641;" BorderPane.alignment="CENTER">
<children>
<HBox prefHeight="25.0" prefWidth="723.0" style="-fx-background-color: #d2d4df;">
<children>
<Label text="LabRat Version R" textFill="#5b5b5b">
<font>
<Font size="14.0" />
</font>
<HBox.margin>
<Insets bottom="5.0" left="3.0" right="3.0" top="3.0" />
</HBox.margin>
</Label>
</children>
</HBox>
<HBox>
<children>
<Button fx:id="changeText" mnemonicParsing="false" onAction="#setChangeText" prefHeight="50.0" prefWidth="200.0" style="-fx-background-color: #ffffff;" text="Change text" textFill="#4a4a4a">
<HBox.margin>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
</HBox.margin>
<font>
<Font size="16.0" />
</font>
</Button>
<Label fx:id="dynamicText" text="Default Text" textFill="WHITE">
<font>
<Font size="24.0" />
</font>
<HBox.margin>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
</HBox.margin>
</Label>
</children>
<VBox.margin>
<Insets top="10.0" />
</VBox.margin>
</HBox>
<HBox layoutX="10.0" layoutY="10.0">
<children>
<Button fx:id="addElement" mnemonicParsing="false" onAction="#setAddElement" prefHeight="50.0" prefWidth="200.0" style="-fx-background-color: #ffffff;" text="Add element" textFill="#4a4a4a">
<HBox.margin>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
</HBox.margin>
<font>
<Font size="16.0" />
</font>
</Button>
<TextField fx:id="typeToAdd" prefHeight="25.0" prefWidth="231.0" promptText="type to add to combobox">
<HBox.margin>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
</HBox.margin>
</TextField>
<ComboBox fx:id="element" prefHeight="25.0" prefWidth="238.0" promptText="select element" style="-fx-background-color: #ffcc99;">
<HBox.margin>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
</HBox.margin>
</ComboBox>
</children>
<VBox.margin>
<Insets top="10.0" />
</VBox.margin>
</HBox>
</children>
</VBox>
</top>
<center>
<VBox prefHeight="200.0" prefWidth="100.0" style="-fx-background-color: #1f3641;" BorderPane.alignment="CENTER">
<children>
<HBox VBox.vgrow="ALWAYS">
<children>
<ImageView fx:id="imgVw" fitHeight="319.0" fitWidth="421.0" pickOnBounds="true" preserveRatio="true" />
<VBox prefHeight="200.0" prefWidth="100.0" style="-fx-background-color: #cc0000;" HBox.hgrow="ALWAYS">
<children>
<Label fx:id="labelValue" text="Label Value" textFill="WHITE">
<font>
<Font size="18.0" />
</font>
<VBox.margin>
<Insets bottom="20.0" />
</VBox.margin>
</Label>
<ComboBox fx:id="comboBoxPorts" prefHeight="25.0" prefWidth="277.0" promptText="COM PORTS" style="-fx-background-color: #bdc3c7;" styleClass="comboBox" stylesheets="#testSS.css" />
</children>
<padding>
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
</padding>
</VBox>
</children>
</HBox>
</children>
</VBox>
</center>
<bottom>
<HBox prefHeight="66.0" prefWidth="723.0" style="-fx-background-color: #222222;" BorderPane.alignment="CENTER">
<children>
<Button fx:id="nextScene" mnemonicParsing="false" onAction="#setNextScene" prefHeight="40.0" prefWidth="150.0" style="-fx-background-color: #d2d4df;" text="Next Scene">
<HBox.margin>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</HBox.margin>
<font>
<Font size="16.0" />
</font>
</Button>
<Button fx:id="exit" mnemonicParsing="false" prefHeight="40.0" prefWidth="150.0" style="-fx-background-color: #ff0000;" text="Exit Lab" textFill="WHITE">
<HBox.margin>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</HBox.margin>
<font>
<Font size="16.0" />
</font>
</Button>
<Button fx:id="version" layoutX="190.0" layoutY="20.0" mnemonicParsing="false" onAction="#setVersion" prefHeight="40.0" prefWidth="150.0" style="-fx-background-color: #ffffff;" text="Alert Version">
<font>
<Font size="16.0" />
</font>
<HBox.margin>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</HBox.margin>
</Button>
<Button fx:id="showTheCar" layoutX="360.0" layoutY="20.0" mnemonicParsing="false" onAction="#setShowTheCar" prefHeight="40.0" prefWidth="150.0" style="-fx-background-color: #ffffff;" text="Show the car!">
<font>
<Font size="16.0" />
</font>
<HBox.margin>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</HBox.margin>
</Button>
</children>
</HBox>
</bottom>
</BorderPane>
Code for JavaFX class:
package labrat;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.stage.Stage;
import jssc.SerialPort;
import jssc.SerialPortEvent;
import jssc.SerialPortException;
import jssc.SerialPortList;
import java.io.IOException;
public class Controller extends Application
{
#FXML
Button changeText;
#FXML
Button showTheCar;
#FXML
Label dynamicText;
#FXML
Button addElement;
#FXML
TextField typeToAdd;
#FXML
ComboBox<String> element;
#FXML
Button nextScene;
#FXML
Button exit;
#FXML
Button version;
#FXML
ImageView imgVw;
#FXML
private Image img;
// for serial com
SerialPort arduinoPort = null;
ObservableList<String> portList;
#FXML
private Label labelValue;
#FXML
ComboBox comboBoxPorts;
#Override
public void start(Stage primaryStage)
{
Parent rootParent = null;
try
{
rootParent = FXMLLoader.load(getClass().getResource("firstScene.fxml"));
primaryStage.setTitle("Test Lab version 3200");
primaryStage.setScene(new Scene(rootParent, 1100, 600));
primaryStage.show();
System.out.println("First Stage now showing");
}
catch (IOException e)
{
e.printStackTrace();
}
labelValue = new Label();
detectPort();
comboBoxPorts = new ComboBox(portList);
comboBoxPorts.valueProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
System.out.println("\nJust making sure this was executed!");
disconnectArduino();
connectArduino(newValue);
}
});
/*
comboBoxPorts.getItems().addAll(portList);
if(comboBoxPorts.getItems().addAll(portList))
{
System.out.println("\nAdded port-list(from observable list) to the CB!");
}
if(!(comboBoxPorts.getItems().addAll(portList)))
{
System.out.println("\nPort-list not added to CB!");
}
*/
}
#Override
public void stop() throws Exception
{
disconnectArduino();
super.stop();
}
// port detector method
private void detectPort(){
System.out.println("\n1/3 Now detecting port...");
portList = FXCollections.observableArrayList();
String[] serialPortNames = SerialPortList.getPortNames();
for(String name: serialPortNames){
System.out.println("\nDetected Port: ");
System.out.println(name);
portList.add(name);
}
}
// connect the Arduino
public boolean connectArduino(String port)
{
System.out.println("\n2/3 Connect Arduino now running...");
boolean success = false;
SerialPort serialPort = new SerialPort(port);
try {
serialPort.openPort();
serialPort.setParams(
SerialPort.BAUDRATE_9600,
SerialPort.DATABITS_8,
SerialPort.STOPBITS_1,
SerialPort.PARITY_NONE);
serialPort.setEventsMask(SerialPort.MASK_RXCHAR);
serialPort.addEventListener((SerialPortEvent serialPortEvent) -> {
if(serialPortEvent.isRXCHAR()){
try {
String st = serialPort.readString(serialPortEvent
.getEventValue());
System.out.println("\nSPE Listener: ");
System.out.println(st);
//Update label in ui thread
Platform.runLater(() -> {
System.out.println("\nAttempted to update label in ui thread");
labelValue.setText(st);
});
} catch (SerialPortException ex) {
ex.printStackTrace();
}
}
});
arduinoPort = serialPort;
success = true;
} catch (SerialPortException ex) {
ex.printStackTrace();
System.out.println("SerialPortException: " + ex.toString());
}
return success;
}
// disconnect the Arduino
public void disconnectArduino()
{
System.out.println("\n3/3 Now disconnecting Arduino...");
if(arduinoPort != null)
{
try
{
arduinoPort.removeEventListener();
if(arduinoPort.isOpened())
{
arduinoPort.closePort();
}
}
catch (SerialPortException e)
{
e.printStackTrace();
}
}
}
#FXML
public void setChangeText()
{
dynamicText.setText("Text changed successfully!");
}
#FXML
public void setShowTheCar()
{
img = new Image("labrat/images/megane.jpg");
imgVw.setImage(img);
imgVw.isPreserveRatio();
}
#FXML
public void setVersion()
{
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("LabRat Version");
alert.setContentText("Version after the previous one! v0.001");
alert.showAndWait();
}
#FXML
public void setNextScene(ActionEvent ev)
{
try
{
Parent secondParent = FXMLLoader.load(getClass().getResource("secondScene.fxml"));
Scene secondScene = new Scene(secondParent);
Stage ourStage = (Stage) ((Node) ev.getSource()).getScene().getWindow();
ourStage.setTitle("Test Lab Page II");
ourStage.setScene(secondScene);
ourStage.show();
}
catch (IOException e)
{
e.printStackTrace();
}
}
#FXML
public void setAddElement()
{
String bufferText = typeToAdd.getText();
element.getItems().addAll(bufferText);
}
}
Console output upto when the scene is shown:
"C:\Program Files\Java\jdk1.8.0_73\bin\java" (cut this short to minimize length)
First Stage now showing
1/3 Now detecting port...
Detected Port:
COM5
How can I get the ComboBox to display the ports? Edit - I'm using the jSSc plugin version 2.8.0
Edit - Corrective changes to the Controller after #RubioRic and #Jose Pereda's suggestion - Port now showing (final code):
package labrat;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.stage.Stage;
import jssc.SerialPort;
import jssc.SerialPortEvent;
import jssc.SerialPortException;
import jssc.SerialPortList;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
public class Controller implements Initializable
{
#FXML
Button changeText;
#FXML
Button showTheCar;
#FXML
Label dynamicText;
#FXML
Button addElement;
#FXML
TextField typeToAdd;
#FXML
ComboBox<String> element;
#FXML
Button nextScene;
#FXML
Button exit;
#FXML
Button version;
#FXML
ImageView imgVw;
#FXML
private Image img;
// for serial com
SerialPort arduinoPort = null;
ObservableList<String> portList;
#FXML
private Label labelValue;
#FXML
ComboBox comboBoxPorts;
#Override
public void initialize(URL location, ResourceBundle resources)
{
detectPort();
}
// port detector method
private void detectPort(){
System.out.println("\n1/3 Now detecting port...");
portList = FXCollections.observableArrayList();
String[] serialPortNames = SerialPortList.getPortNames();
for(String name: serialPortNames){
System.out.println("\nDetected Port: ");
System.out.println(name);
portList.add(name);
}
// No need to create a new combo instance
// No need to add a change listener to refresh ports
comboBoxPorts.setItems(portList);
comboBoxPorts.valueProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue)
{
System.out.println("\nChangeListener executed!");
disconnectArduino();
connectArduino(newValue);
System.out.println("\nOld Value was: " + oldValue);
System.out.println("\nNew Value is: " + newValue);
labelValue.setText(newValue);
}
});
}
// connect the Arduino
public boolean connectArduino(String port)
{
System.out.println("\n2/3 Connect Arduino now running...");
boolean success = false;
SerialPort serialPort = new SerialPort(port);
try {
serialPort.openPort();
serialPort.setParams(
SerialPort.BAUDRATE_9600,
SerialPort.DATABITS_8,
SerialPort.STOPBITS_1,
SerialPort.PARITY_NONE);
serialPort.setEventsMask(SerialPort.MASK_RXCHAR);
serialPort.addEventListener((SerialPortEvent serialPortEvent) -> {
if(serialPortEvent.isRXCHAR()){
try {
String st = serialPort.readString(serialPortEvent
.getEventValue());
System.out.println("\nSPE Listener: ");
System.out.println(st);
//Update label in ui thread
Platform.runLater(() -> {
System.out.println("\nAttempted to update label in ui thread");
labelValue.setText(st);
});
} catch (SerialPortException ex) {
ex.printStackTrace();
}
}
});
arduinoPort = serialPort;
success = true;
} catch (SerialPortException ex) {
ex.printStackTrace();
System.out.println("SerialPortException: " + ex.toString());
}
return success;
}
// disconnect the Arduino
public void disconnectArduino()
{
System.out.println("\n3/3 Now disconnecting Arduino...");
if(arduinoPort != null)
{
try
{
arduinoPort.removeEventListener();
if(arduinoPort.isOpened())
{
arduinoPort.closePort();
}
}
catch (SerialPortException e)
{
e.printStackTrace();
}
}
}
#FXML
public void setChangeText()
{
dynamicText.setText("Text changed successfully!");
}
#FXML
public void setShowTheCar()
{
img = new Image("labrat/images/megane.jpg");
imgVw.setImage(img);
imgVw.isPreserveRatio();
}
#FXML
public void setVersion()
{
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("LabRat Version");
alert.setContentText("Version after the previous one! v0.001");
alert.showAndWait();
}
#FXML
public void setNextScene(ActionEvent ev)
{
try
{
Parent secondParent = FXMLLoader.load(getClass().getResource("secondScene.fxml"));
Scene secondScene = new Scene(secondParent);
Stage ourStage = (Stage) ((Node) ev.getSource()).getScene().getWindow();
ourStage.setTitle("Test Lab Page II");
ourStage.setScene(secondScene);
ourStage.show();
}
catch (IOException e)
{
e.printStackTrace();
}
}
#FXML
public void setAddElement()
{
String bufferText = typeToAdd.getText();
element.getItems().addAll(bufferText);
}
}
I'm no JavaFx expert, but I think that you are mixing two concepts Application and Controller. Change suggested by José Pereda may cause a NullPointerException if controller is not initialized properly.
I've launched successfully a little demo using your fxml file and changing your controller to not invoke arduino. But I've used and extra class for launching the scene. That way the #FMX elements are injected properly and the combobox shows ports.
Main
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class ComboMain extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("combo.fxml"));
primaryStage.setTitle("Combo");
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Controller
public class ComboController implements Initializable {
// #FXML elements here
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
detectPort(); // Include all the code that you need for initializing elements here
}
// port detector method
// Dummy method - Include arduino calls here
private void detectPort(){
System.out.println("\n1/3 Now detecting port...");
portList = FXCollections.observableArrayList();
String[] serialPortNames = {"COM1", "COM2", "COM3"}; // SerialPortList.getPortNames();
for(String name: serialPortNames){
System.out.println("\nDetected Port: ");
System.out.println(name);
portList.add(name);
}
// No need to create a new combo instance
// No need to add a change listener to refresh ports
comboBoxPorts.setItems(portList);
}
// Arduino methods here
}
As I said, I'm no javafx expert, but maybe you can apply some of these changes to your code.
Based on your code:
#FXML
ComboBox comboBoxPorts;
#Override
public void start(Stage primaryStage)
{
...
comboBoxPorts = new ComboBox(portList);
}
Inside the start() method you are creating a second instance of comboBoxPorts. This is the one that get the list of ports, but this is not added to the scene graph.
On the contrary, you don't add anything to the first instance, the one on the scene graph, that is created by the FXMLLoader thanks to the #FXML annotation.
You only need to add the list:
#FXML
ComboBox comboBoxPorts;
#Override
public void start(Stage primaryStage)
{
...
comboBoxPorts.setItems(portList);
}
EDIT
And as #RubioRic has stated in his answer, while it is possible to merge Application and Controller class content like you have done, it won't allow you accessing injected nodes from the start() method, and any attempt to call comboBoxPorts will throw a NPE, since you will be dealing with two non-related instances of the class (one created by the launcher, the other by FXMLLoader).
The usual approach is just creating a proper Controller class, with an initialize method, while loading the fxml from the Application.start() method.
Controller class
#FXML
ComboBox comboBoxPorts;
public void initialize() {
...
comboBoxPorts.setItems(portList);
}
EDIT 2
In case you still want to use your original single class approach, this will work:
Remove the fx:controller tag from the fxml file, and set the controller on the start() method, referring to this, so you will have just one single instance:
#FXML
ComboBox comboBoxPorts;
#Override
public void start(Stage stage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("firstScene.fxml"));
loader.setController(this);
Parent root = loader.load();
...
comboBoxPorts.setItems(portList);
}
How to make that when i click on "Open second window" the main window be hide. And when i close second window, the main window be showed? I read this post, but i don't know how to implement it for my task. Thanks!
I have next code:
Main.java
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("First Stage");
try {
primaryStage.setResizable(false);
Parent root = FXMLLoader.load(getClass().getResource("MainView.fxml"));
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
MainWiew.fxml
<?import javafx.geometry.*?>
<?import javafx.scene.text.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.BorderPane?>
<BorderPane id="rootMain" prefHeight="418.0" prefWidth="691.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.view.MainController">
<top>
</top>
<center>
<AnchorPane prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">
<children>
<VBox fillWidth="false" prefHeight="400.0" prefWidth="394.0" spacing="8.0">
<children>
<Button id="btnMainOne" fx:id="btnMainOne" mnemonicParsing="false" prefHeight="51.0" prefWidth="398.0" text="Open second window" textFill="#4460ff">
<font>
<Font name="Times New Roman Bold" size="20.0" />
</font>
</Button>
</children>
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</padding>
</VBox>
</children>
</AnchorPane>
</center>
</BorderPane>
MainController.java
import java.io.IOException;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.input.MouseEvent;
import javafx.stage.Modality;
import javafx.stage.Stage;
public class MainController {
#FXML
private Button btnMainOne;
#FXML
private void initialize() {
btnMainOne.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
try {
Stage stage = new Stage();
stage.setTitle("Second stage");
stage.setScene(new Scene(root, 450, 450));
stage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
}
You can get the primary window by calling getScene().getWindow(). You can call hide() on it to hide it after you show the new stage, and you can register an event listener with the new stage that shows the primary window when the new stage is hidden:
#FXML
private void initialize() {
btnMainOne.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
Stage primaryStage = (Stage)btnMainOne.getScene().getWindow();
try {
Stage stage = new Stage();
stage.setTitle("Second stage");
stage.setScene(new Scene(root, 450, 450));
stage.setOnHidden(e -> primaryStage.show());
stage.show();
primaryStage.hide();
} catch (IOException e) {
e.printStackTrace();
}
}
});
}