JavaFX SetOnCloseRequest() closing all screens - java

In my application I have multiple .fxml files. When I set a screen in my application, I also set the SetOnCloseRequest() method. For some screens is the code in that method just to switch to another screen. For example: When I have my Create Bank screen open and I press the close button, I want to switch back to my Manage Banks screen. But when I press the close button, it seems to switch the screens correctly, but very shortly after it closes the Manage Banks screen for some reason and the application dont stops running. So I cant do anything after that, because I don't have any GUI.
Does someone knows how to make sure that the Manage Banks screens does not close?
Here is my main class with theSetOnCloseRequest() method:
public class ClientMain extends Application {
public static String screenCreateBankId = "createBank";
public static String screenCreateBankFile = "Screens/createBank.fxml";
public static String screenCreateBankAccountId = "createBankAccount";
public static String screenCreateBankAccountFile = "Screens/createBankAccount.fxml";
public static String screenLoginId = "login";
public static String screenLoginFile = "Screens/login.fxml";
public static String screenManageBanksId = "manageBanks";
public static String screenManageBanksFile = "Screens/manageBanks.fxml";
private static Stage primaryStage;
private static ScreensController mainContainer;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
ClientMain.primaryStage = primaryStage;
mainContainer = new ScreensController();
mainContainer.loadScreen(ClientMain.screenCreateBankId, ClientMain.screenCreateBankFile);
mainContainer.loadScreen(ClientMain.screenLoginId, ClientMain.screenLoginFile);
mainContainer.loadScreen(ClientMain.screenManageBanksId, ClientMain.screenManageBanksFile);
mainContainer.setScreen(ClientMain.screenLoginId);
Group root = new Group();
root.getChildren().addAll(mainContainer);
primaryStage.setResizable(false);
primaryStage.getIcons().add(new Image("file:assets/ideal_logo.jpg"));
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
public static void setProperties(String name){
primaryStage.sizeToScene();
primaryStage.centerOnScreen();
switch (name){
case "createBank":
primaryStage.setTitle("Create Bank");
primaryStage.setOnCloseRequest(event -> mainContainer.setScreen(screenManageBanksId));
break;
case "login":
primaryStage.setTitle("Login");
primaryStage.setOnCloseRequest(event -> System.exit(0));
break;
case "manageBanks":
primaryStage.setTitle("Manage Banks");
primaryStage.setOnCloseRequest(event -> mainContainer.getManageBanksController().logoutAdmin());
break;
}
}
}
And here is my ScreensController class:
public class ScreensController extends StackPane {
private HashMap<String, Node> screens = new HashMap<>();
private Client client;
private NewTransactionController newTransactionController;
private BankAccountController bankAccountController;
private ManageBanksController manageBanksController;
public Client getClient() {
return this.client;
}
public NewTransactionController getNewTransactionController() {
return this.newTransactionController;
}
public BankAccountController getBankAccountController() {
return this.bankAccountController;
}
public ManageBanksController getManageBanksController() {
return this.manageBanksController;
}
public ScreensController() {
try {
this.client = new Client();
System.out.println("Client: Client created");
} catch (RemoteException e) {
System.out.println("Client: Cannot create Client");
System.out.println("Client: RemoteException: " + e.getMessage());
System.exit(0);
}
}
public void addScreen(String name, Node screen) {
screens.put(name, screen);
}
public void loadScreen(String name, String resource) {
try {
FXMLLoader myLoader = new FXMLLoader(getClass().getResource(resource));
Parent loadScreen = myLoader.load();
IControllers myScreenController = myLoader.getController();
if (myScreenController instanceof NewTransactionController) {
this.newTransactionController = (NewTransactionController) myScreenController;
} else if (myScreenController instanceof BankAccountController) {
this.bankAccountController = (BankAccountController) myScreenController;
} else if (myScreenController instanceof ManageBanksController) {
this.manageBanksController = (ManageBanksController) myScreenController;
}
myScreenController.setScreenParent(this);
addScreen(name, loadScreen);
} catch (Exception e) {
e.printStackTrace();
}
}
public void setScreen(final String name) {
if (screens.get(name) != null) {
if (!getChildren().isEmpty()) {
getChildren().remove(0);
getChildren().add(0, screens.get(name));
ClientMain.setProperties(name);
} else {
//First time start up
getChildren().add(screens.get(name));
ClientMain.setProperties(name);
}
} else {
System.out.println("Screen hasn't been loaded!!!");
}
}
}

try this
primaryStage.setOnCloseRequest(event -> {
event.consume();
mainContainer.setScreen(screenManageBanksId);
});
...
primaryStage.setOnCloseRequest(event -> {
event.consume();
mainContainer.getManageBanksController().logoutAdmin();
});

Related

How to register EventBus only once

I'm writing JavaFX application in order to send from one controller to other controller. I use EventBus which was written by developer. I download it from github.But When I try to recall from one controller to other controller. First time it works once. Second time it works twice. Third time it works three times and so on. What might be reason of behaving this eventbus?
MainController
here Event bus was registered like static
public class Main extends Application
{
public static EventBus eventBus = new FxEventBus();
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
This controller class which was fired event
Controller
private AddClickedEvent addClickedEvent;
#Override
public void initialize(URL location, ResourceBundle resources)
{
id.setOnAction(event ->
{
try
{
FXMLLoader loader = new FXMLLoader();
Parent parent = loader.load(getClass().getResource("ask.fxml").openStream());
Stage stage = new Stage();
stage.setScene(new Scene(parent));
if(addClickedEvent == null){
addClickedEvent = new AddClickedEvent(AddClickedEvent.ANY);
}
Main.eventBus.fireEvent(addClickedEvent);
stage.showAndWait();
} catch (IOException e) {
e.printStackTrace();
}
});
}
Here is Controller other Controller that should show up something after fire
#Override
public void initialize(URL location, ResourceBundle resources)
{
Main.eventBus.addEventHandler(AddClickedEvent.ANY,event -> {
System.out.println("uyondan bosilib galdi");
System.out.println(yes);
yes = true;
});
id1.setOnAction(event -> {
System.out.println(yes);
});
id2.setOnAction(event -> {
Stage stage = (Stage)((Node) (event).getSource()).getScene().getWindow();
stage.close();
});
}
AddClicked Event class
public class AddClickedEvent extends Event
{
public static final EventType<AddClickedEvent> ANY =
new EventType<>(Event.ANY, "ADD_CLIENT_EVENT");
public AddClickedEvent(#NamedArg("eventType") EventType<? extends Event> eventType) {
super(eventType);
}
}
EventHandler should be created only once in process of whole application then it will react only once rather than multiple times. I come up with solutions to it declare for every controller class with static int variable which helps me to register events only once when the value of int is zero.
private static int check;
#Override
public void initialize(URL location, ResourceBundle resources)
{
if(check == 0)
{
Main.eventBus.addEventHandler(AddClickedEvent.ANY,event -> {
System.out.println("uyondan bosilib galdi");
System.out.println(yes);
yes = true;
});
check ++;
// Then it will react only once We registered event here
}
id1.setOnAction(event -> {
System.out.println(yes);
});
id2.setOnAction(event -> {
Stage stage = (Stage)((Node) (event).getSource()).getScene().getWindow();
stage.close();
});
}

JavaFX fireEvent StackOverflowError

I'm currently working on a little game with JavaFX, and i had some problems to catch keyEvents.
Now i can catch them but the program throw a java.lang.StackOverflowError and it didn't do what I expected when a key is pressed.
Here is the main class:
public class WarbladeFX extends Application {
Game root;
public void start(Stage primaryStage) {
root = new Game();
Scene scene = new Scene(root, 800, 600);
scene.setFill(new Color(0,0,0,1));
scene.setOnKeyPressed(new EventHandler<KeyEvent>() {
public void handle(KeyEvent event) {
Event.fireEvent(root, event);
}
});
scene.setOnKeyReleased(new EventHandler<KeyEvent>() {
public void handle(KeyEvent event) {
Event.fireEvent(root, event);
}
});
primaryStage.setTitle("WarbladeFX");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
And the Game class:
public class Game extends Group {
Entity ship;
long delta;
HashSet<String> input = new HashSet<>();
public Game() {
File f = new File("src/ressources/ship.gif");
ship = new Entity(new Image("file:///" + f.getAbsolutePath().replace("\\", "/")) ,300, 300);
getChildren().add(ship);
setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
//System.out.println(".handle()");
String code = event.getCode().toString();
if(!input.contains(code))
input.add(code);
}
});
setOnKeyReleased(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
String code = event.getCode().toString();
input.remove(code);
}
});
new AnimationTimer(){
#Override
public void handle(long now) {
if(input.contains("LEFT"))
ship.setVelX(-1);
if(input.contains("RIGHT"))
ship.setVelX(1);
ship.move(now);
getChildren().clear();
getChildren().add(ship);
}
}.start();
}
}
Some help would be very great.
Events fired on nodes in the scene graph will "bubble up" to the parent, and then the parent of the parent, and eventually all the way up to the scene. So the event handlers that you defined on the scene "refire" the event to the root, which will then bubble up to the scene and get handled again, being refired again to the root, and so on...
If you want to catch the events anywhere in the scene, and then handle them in the Game class, define some methods in Game to process the events and invoke those methods. Don't "refire" the events.
For example:
public class Game extends Group {
Entity ship;
long delta;
HashSet<String> input = new HashSet<>();
public Game() {
File f = new File("src/ressources/ship.gif");
ship = new Entity(new Image("file:///" + f.getAbsolutePath().replace("\\", "/")) ,300, 300);
getChildren().add(ship);
new AnimationTimer(){
#Override
public void handle(long now) {
if(input.contains("LEFT"))
ship.setVelX(-1);
if(input.contains("RIGHT"))
ship.setVelX(1);
ship.move(now);
getChildren().clear();
getChildren().add(ship);
}
}.start();
}
public void keyDown(String key) {
if(!input.contains(key))
input.add(key);
}
public void keyUp(String key) {
input.remove(code);
}
}
and then you can do
public class WarbladeFX extends Application {
Game root;
public void start(Stage primaryStage) {
root = new Game();
Scene scene = new Scene(root, 800, 600);
scene.setFill(new Color(0,0,0,1));
scene.setOnKeyPressed(new EventHandler<KeyEvent>() {
public void handle(KeyEvent event) {
game.keyDown(event.getCode().toString());
}
});
scene.setOnKeyReleased(new EventHandler<KeyEvent>() {
public void handle(KeyEvent event) {
game.keyUp(event.getCode().toString());
}
});
primaryStage.setTitle("WarbladeFX");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
you are generating an infinite loop when you fire the event again in the event-handle method. Try to handle the event in this method, i.e. react to the users input.

JavaFX TableView Issues Adding Rows + Organizing Multi Controller and FXML App

I'm new to JavaFX and I've been at this code for about 8 hours now and I've become a bit delusional with the code. My two main problems are:
Can't add new items to the TableView using my popUp box display().
Feels messy and unorganized. Any tips for better communication between FXML and Controllers? (Again I'm new so it could be that I've stared too long at it)
My main class
public class Main extends Application {
public static Stage primaryStage;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage window) throws Exception {
try {
primaryStage = new Stage();
window = primaryStage;
Parent root = FXMLLoader.load(getClass().getResource("Fissto.fxml"));
Scene scene = new Scene(root);
window.setTitle("Fissto - the File Storage App!");
window.setScene(scene);
window.show();
}catch(Exception e){
e.printStackTrace();
}
// C.setLibraryStage();
}
}
My main Controller class (I have two sub ones that connect in the Fissto.fxml)
public class Controller implements Initializable{
Main main;
#FXML LibraryController libraryController = new LibraryController();
#FXML MergePageController mergePageController = new MergePageController();
private AddImageController addImageController = new AddImageController();
#FXML public void initialize(URL location, ResourceBundle resources){
System.out.println("View is now loaded!");
main = new Main();
libraryController.init(this);
mergePageController.init(this);
addImageController.init(this);
}
//Interface Initialization
public void setMergeStage() throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("Controllers/MergePage.fxml"));
Scene scene = new Scene(root);
main.primaryStage.setScene(scene);
}
public void setLibraryStage() throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("Controllers/LibraryPage.fxml"));
Scene scene = new Scene(root);
main.primaryStage.setScene(scene);
}
//Closing a window
public void closeWindow(){
main.primaryStage.close();
}
}
And finally the controller for the page that holds the TableView
public class LibraryController {
private Controller main;
//Library TableView Controllers
#FXML public TableView<Image> library;
#FXML private TableColumn<Image, String> NameColumn = new TableColumn<>();
#FXML private TableColumn<Image, ArrayList<String>> TagsColumn = new TableColumn<>();
#FXML private TableColumn<Image, String> CommentsColumn = new TableColumn<>();
#FXML private TableColumn<Image, String> FileLocationColumn = new TableColumn<>();
#FXML private TableColumn<Image, Integer> PointsColumn = new TableColumn<>();
public void init(Controller main){
System.out.println("LibraryPage Loading");
this.main = main;
addDataToColumns();
library = new TableView<>();
library.getItems().setAll(getImages());
System.out.println("LibraryPage Loaded");
}
//Initializes the column titles
private void addDataToColumns(){
NameColumn.setCellValueFactory(new PropertyValueFactory<>("name"));
TagsColumn.setCellValueFactory(new PropertyValueFactory<>("tags")); //TODO Convert to String format
CommentsColumn.setCellValueFactory(new PropertyValueFactory<>("comments"));
FileLocationColumn.setCellValueFactory(new PropertyValueFactory<>("filelocation"));
PointsColumn.setCellValueFactory(new PropertyValueFactory<>("points"));
}
//Gets all of the images
private ObservableList<Image> getImages() {
//TODO: Add where to actually get the data from
ObservableList<Image> images = FXCollections.observableArrayList();
String s = "Dog, Cat, Jumping Jack,";
ArrayList<String> list = Image.getTagOrganizer(',', s);
images.add(new Image("Test", list, "Comment", "No File Location, yet!", 10));
String k = "Calculus, Complex Numbers, Hard dude,";
ArrayList<String> list2 = Image.getTagOrganizer(',', k);
images.add(new Image("Number2", list2, "I love MathClub", "No File Location, yet!", -10));
return images;
}
This last class is the popup menu that takes in input to put in the GridPane
public class AddImageController {
private Controller main;
public void init(Controller main){
System.out.println("ImagePage Loaded");
this.main = main;
}
//Submitting an image to the library from the AddImagePage
public TextField nameInput;
public TextField tagsInput;
public TextField commentInput;
public TextField pointsInput;
public Label errorMessage;
/** TODO: Make it so that it writes to file then theoretically, the main controller should read from file every so often
* Main functionality for adding the information from the form to the database */
public void submitImage(){
if(!(nameInput.getText().trim().isEmpty()) && !(tagsInput.getText().trim().isEmpty()) && !(pointsInput.getText().trim().isEmpty())) {
if (isInt(pointsInput)) {
// System.out.print("Sent to database, *whoosh!*");
LibraryController c = new LibraryController();
ArrayList<String> s = Image.getTagOrganizer(',', tagsInput.getText());
Image image = new Image(nameInput.getText(), s, commentInput.getText(),"Location Needed", Integer.parseInt(pointsInput.getText()));
c.library.getItems().add(image);
clearInputs();
}
}else {
errorMessage.setText("Fill every field");
}
}
//Clears the input fields in the AddImagePage
public void clearInputs(){
nameInput.clear();
tagsInput.clear();
commentInput.clear();
pointsInput.clear();
errorMessage.setText("");
}
//Submission format verifiers
private boolean isInt(TextField input){
try{
int i = Integer.parseInt(input.getText());
errorMessage.setText("");
return true;
}catch (NumberFormatException e){
System.out.println("Oh no: " + input.getText() + " is not an integer");
errorMessage.setText("Points must be a number");
return false;
}
}
//Image Selection Handler
public void imageSelectionHandler(){
}
}
I understand it may be hard to read, so any feedback on how to make it easier to read in the future is much appreciated.

JavaFX : Keep listening to keyboard-input and on input perform action

I have JavaFX project in which I have to listen to keyboard-input as our barcode scanner is configured that way.
Are there any libraries in JavaFX where I can keep a listener active and perform suitable action upon reception of a String by barcode-scanner.
I searched on net, but didn't find any good solution unfortunately.
Here is my code :
public class Main extends Application {
private Scene scene;
MyBrowser myBrowser;
#Override
public void start(Stage primaryStage) throws Exception{
primaryStage.setTitle("Our Application");
java.net.CookieManager manager = new java.net.CookieManager();
java.net.CookieHandler.setDefault(manager);
myBrowser = new MyBrowser();
scene = new Scene(myBrowser, 1080, 1920);
primaryStage.setScene(scene);
primaryStage.setFullScreen(true);
primaryStage.show();
// # being the escape character
scene.setOnKeyTyped(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
String text = event.getCharacter();
if (text.equals("#")) {
String tempText = completeText;
completeText = "";
processText(tempText);
}else {
completeText = completeText+text;
}
}
});
}
private void processText(String text){
System.out.println("I will process "+text);
}
public static void main(String[] args) {
launch(args);
}
public class MyBrowser extends Region {
final String hellohtml = "index.html";
WebView webView = new WebView();
WebEngine webEngine = webView.getEngine();
public MyBrowser() {
webEngine.getLoadWorker().stateProperty().addListener((observable, oldValue, newValue) -> {
if (newValue == Worker.State.SUCCEEDED) {
JSObject window = (JSObject) webEngine.executeScript("window");
window.setMember("app", this);
}
});
URL urlHello = getClass().getResource(hellohtml);
webEngine.load(urlHello.toExternalForm());
webView.setPrefSize(1080, 1920);
webView.setContextMenuEnabled(false);
getChildren().add(webView);
}
Kindly let me know. Thank you.
So I think I understand what your problem is, you have a barcode scanner that sends in barcodes as keyevents to your application, And then you need to respond to the whole bar code when you're done receiving the code?
If that's the case you can use a KeyListener to intercept the key press events. Then you just need to implement the logic to put the individual key events together.
class MyListener implements KeyListener{
#Override
public void keyPressed(KeyEvent e) {
// Logic goes here
}
#Override
public void keyReleased(KeyEvent e) {
// Logic goes here
}
#Override
public void keyTyped(KeyEvent e) {
// Logic goes here
}
}

JavaFX Switch Scene in a SplitPane?

At the Base I have an AnchorPane then a SplitPane. On the left pane I have a listView and depending on the list element selected, the right pane displays the appropriate content. The way I have done this is by overlapping AnchorPanes and setting them to .setVisible(false) initially and as they are selected I set them to .setVisible(true) like so :
public void listSelection() {
String selection = listView.getSelectionModel().getSelectedItem();
switch(selection) {
case "Speed of sound":
disableOld(); // disables old AnchorePane
response.setText("Speed of sound conversion");
AnchorPane1.setVisible(true);
break;
case "Temperature conversion":
disableOld();
response.setText("Temperature conversion");
AnchorPane2.setVisible(true);
break;
}
}
I would like to know how to produce the same effect visually but with different scenes as I would like for each new AnchorPane to have it's own FXML and ControllerClass.
You can implement something like this :
Your main class :
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;
}
public static void main(String[] args) {launch(args); }
Then you can create a separate class call Navigator 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";}
In your main FxmlController(which is the controller of the permanent layer of your application , rest of the stack-panes-{p1 and p2} will load on your permanent layer )
This is how you load layers on the main FxmlController :
#FXML
private StackPane stackPaneHolder;
...
public void setPane(Node node) {
if (stackPaneHolder.getChildren().isEmpty()) {
//if stackPaneHolder is empty
stackPaneHolder.getChildren().add(node);
} else {
if (stackPaneHolder.getClip() != node) {
//if stackPaneHolder is not empty then remove existing layer and add new layer
stackPaneHolder.getChildren().remove(0);
stackPaneHolder.getChildren().add(0, node);
}
}
}
Then you can load panes by pressing a button like below :
#FXML
private void btnAction(ActionEvent event) throws IOException {
Navigator.load(new Navigator().getP1());
..
This is how it works :

Categories