AnimationTimer - showAndWait is not allowed during animation processing - java

I'm trying to display an alert dialog box when the player wins the game that I created. However, I get an exception:
java.lang.IllegalStateException: showAndWait is not allowed during animation or layout processing
I tried adding stop() in the AnimationTimer but it didn't work, still threw the same exception:
if (ball.getBall().getCenterY() == 0) {
//computer lost!
stop();
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle(null);
alert.setHeaderText(null);
alert.setContentText("Good game. You won! Click OK to exit.");
alert.showAndWait(); //exception thrown here
System.exit(0);
}

You can only call showAndWait() in an event handler, not from within an animation. This is not explicitly documented in the Alert class, though it is documented in the documentation for Stage.
Call show() instead, and use a handler for the onHidden event for the alert to invoke something when the alert is closed:
if (ball.getBall().getCenterY() == 0) {
//computer lost!
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle(null);
alert.setHeaderText(null);
alert.setContentText("Good game. You won! Click OK to exit.");
alert.setOnHidden(evt -> Platform.exit());
alert.show();
}

An alternative is to put your code into a private method and call using a method reference or lambda runnable in a Platform.runLater() call.
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle(null);
alert.setHeaderText(null);
alert.setContentText("Good game. You won! Click OK to exit.");
alert.showAndWait(); //exception thrown here
System.exit(0);
This make the dialog pop up outside of a timer cycle and should fix the problem.

Related

Dialogue in javafx [duplicate]

This question already has answers here:
Alert Box For When User Attempts to close application using setOnCloseRequest in JavaFx
(2 answers)
Closed 4 years ago.
I want to implement a dialogue alert when the user clicks the close button. With the option for yes they do want to leave and no they don't.
Button button = new Button("Exit");
gridPane.add(button, 12, 12);
button.setOnAction(e ->{
primaryStage.close();
});
How would I go about this?
Use the onCloseRequest event of the stage for closing the window using the X button of the window:
private static boolean confirmClose() {
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.setContentText("Do you really want to close the app?");
return alert.showAndWait().orElse(ButtonType.CANCEL) == ButtonType.OK;
}
primaryStage.setOnCloseRequest(event -> {
if (!confirmClose()) {
event.consume();
}
});
Note that this event is not triggered when closing the window programmatically. You need to request user confirmation yourself in such a case:
button.setOnAction(evt -> {
if (confirmClose()) {
primaryStage.close();
}
});
You have a pretty good article which explains et give some examples about Alert
The one you need is a Confirmation-Dialog :
button.setOnAction(e ->
Alert alert = new Alert(AlertType.CONFIRMATION);
alert.setTitle("Exit Application");
alert.setHeaderText("Exit of the App");
alert.setContentText("Do you really want to exit ? ");
Optional<ButtonType> result = alert.showAndWait();
if (result.get() == ButtonType.OK){
primaryStage.close();
Platform.exit();
System.exit(0);
} else {
// ... user chose CANCEL or closed the dialog
}
});
Try something like:
Button button = new Button("Exit");
gridPane.add(button, 12, 12);
button.setOnAction(e ->{
if(confirmDialog(
"Sure you want to quit?",
"Sure you want to quit?",
"We're really closing - click yes to quit, no to stay in the app")
) {
primaryStage.close();
}
});
...
public boolean confirmDialog(String title, String headerText, String message) {
Alert alert = new Alert(AlertType.CONFIRMATION, message, ButtonType.YES, ButtonType.NO);
alert.initModality(Modality.APPLICATION_MODAL);
alert.initOwner(scene); //scene must be accessible as a field
alert.setTitle(title);
alert.setHeaderText(headerText);
ButtonType result = alert.showAndWait().orElse(ButtonType.NO);
return ButtonType.YES==result;
}

Java Swing - Close JDialog from external Thread

I am working with Swing right now and I do not get this to work properly.
What I need is the following:
I've got a class "Client" that is able to connect to a TCP server.
If the connection fails (wrong IP for example), then it will show an error dialog that can be closed by clicking on the "OK" Button.
However if the client connected successfully, a window should popup that runs until my client receives a specific message from the server.
My code looks like this:
if(ip != null) {
Client c = new Client();
try{
c.connect(ip, 56556);
JOptionPane msg = new JOptionPane("Connecting...", JOptionPane.INFORMATION_MESSAGE);
JDialog dlg = msg.createDialog("Connecting...");
dlg.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
dlg.setVisible(true);
c.addIncomingMessageHandler(new IncomingMessageHandler(){
#Override
public void incomingMessage(Connection<?> cnctn, Object o) {
dlg.setVisible(false);
dlg.dispose();
}
});
}catch(Exception e) {
int n = JOptionPane.showOptionDialog(this, "Oops! Something went wrong!",
"Title", JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE,
null, new Object[] {"OK"}, JOptionPane.OK_OPTION);
}
}
So the exception is throws if c.connect() fails.
c.addIncomingMessageHandler() is a listener that listens to any incoming messages to the client. If the server sends something, this method will be called. If that's the case, the JDialog will be closed. But this window can be closed right now by clicking on the OK-Button.
I'd like to rename that button and add a function.
The new text should be "Cancel" and if the button is pressed, the client should be closed (c.disconnect) and the window itself should be closed as well.
How could I do that?
From the Documentation:
Stopping Automatic Dialog Closing
By default, when the user clicks a JOptionPane-created button, the dialog closes. But what if you want to check the user's answer before closing the dialog? In this case, you must implement your own property change listener so that when the user clicks a button, the dialog does not automatically close.
DialogDemo contains two dialogs that implement a property change listener. One of these dialogs is a custom modal dialog, implemented in CustomDialog, that uses JOptionPane both to get the standard icon and to get layout assistance. The other dialog, whose code is below, uses a standard Yes/No JOptionPane. Though this dialog is rather useless as written, its code is simple enough that you can use it as a template for more complex dialogs.
Besides setting the property change listener, the following code also calls the JDialog's setDefaultCloseOperation method and implements a window listener that handles the window close attempt properly. If you do not care to be notified when the user closes the window explicitly, then ignore the bold code.
final JOptionPane optionPane = new JOptionPane(
"The only way to close this dialog is by\n"
+ "pressing one of the following buttons.\n"
+ "Do you understand?",
JOptionPane.QUESTION_MESSAGE,
JOptionPane.YES_NO_OPTION);
final JDialog dialog = new JDialog(frame,
"Click a button",
true);
dialog.setContentPane(optionPane);
dialog.setDefaultCloseOperation(
JDialog.DO_NOTHING_ON_CLOSE);
dialog.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent we) {
setLabel("Thwarted user attempt to close window.");
}
});
optionPane.addPropertyChangeListener(
new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent e) {
String prop = e.getPropertyName();
if (dialog.isVisible()
&& (e.getSource() == optionPane)
&& (prop.equals(JOptionPane.VALUE_PROPERTY))) {
//If you were going to check something
//before closing the window, you'd do
//it here.
dialog.setVisible(false);
}
}
});
dialog.pack();
dialog.setVisible(true);
int value = ((Integer)optionPane.getValue()).intValue();
if (value == JOptionPane.YES_OPTION) {
setLabel("Good.");
} else if (value == JOptionPane.NO_OPTION) {
setLabel("Try using the window decorations "
+ "to close the non-auto-closing dialog. "
+ "You can't!");
}
Click here!
Related question.

Application not responding to Touch-Events after showing custom dialog

like the title implies i've got a problem with my application. The application is supposed to run in fullscreen mode (no intention for switching back to window mode), so i designed a footer-bar holding some images (with a Label, in a VBox) so the user could navigate or exit the program.
So after starting the application all Buttons work just fine with touch. Even the Exit-button in my footer-bar responded correctly by opening my custom Dialog. But here starts my Problem. The Dialog is shown by showAndWait()-Method call, but does not respond to Touch-Events. In contrary mouse-events are still processed (i still can use a mouse to click the Buttons in my Dialog and the Dialog is responding correctly).
I hope someone got an idea what i'm doing wrong.
MyDialog.java:
public static boolean showExitDialog(Window owner, ResourceBundle resources) {
LOGGER.info("Showing exit dialog...");
final Dialog<ButtonType> dialog = new Dialog<ButtonType>();
dialog.getDialogPane().getStylesheets().add(MyDialog.getInstace().getCssPath());
dialog.setContentText(resources.getString("label.exitdialog.text"));
dialog.setHeaderText(resources.getString("label.exitdialog.header"));
dialog.initOwner(owner);
dialog.initStyle(StageStyle.TRANSPARENT);
dialog.initModality(Modality.APPLICATION_MODAL);
dialog.getDialogPane().getButtonTypes().add(new ButtonType(resources.getString("btn.Exitdialog.exit"), ButtonData.OK_DONE););
dialog.getDialogPane().getButtonTypes().add(new ButtonType(resources.getString("btn.Exitdialog.cancel"), ButtonData.FINISH));
Optional<ButtonType> result = dialog.showAndWait();
LOGGER.debug("Result: {}", result.get());
if(result.isPresent() && result.get().getButtonData() == ButtonData.OK_DONE) {
LOGGER.info("Closing exit dialog returning true...");
return true;
} else {
LOGGER.info("Closing exit dialog returning false...");
return false;
}
}
In MainApp.java:
private EventHandler<WindowEvent> confirmCloseEventHandler = event -> {
// close event handling logic.
// consume the event if you wish to cancel the close operation.
if(MyDialog.showExitDialog(primaryStage, rb)) {
event.consume();
System.exit(0);
}
};
...
primaryStage.setOnCloseRequest(confirmCloseEventHandler);
In FooterBar.java:
#FXML
private void exitProgramPressedTouch(TouchEvent event) {
event.consume();
controller.getWindow().fireEvent(new WindowEvent(controller.getWindow(), WindowEvent.WINDOW_CLOSE_REQUEST));
}
*Edit* Oh totally forgot: No Exception or anything else is thrown.
I don't know the reason for the described behavior - maybe a bug. However, you could try to listen for ActionEvent instead of TouchEvent. It handles both touch and mouse events:
#FXML
private void exitProgramPressedTouch(ActionEvent event) {
event.consume();
controller.getWindow().fireEvent(new WindowEvent(controller.getWindow(), WindowEvent.WINDOW_CLOSE_REQUEST));
}
Maybe you need also to change the attribute which binds the event listener (from onTouch to onAction) in your FXML file.
Finally, I think, you could avoid System.exit(0); if you consume the close event only when the cancel button has been clicked:
if(!MyDialog.showExitDialog(primaryStage)) {
event.consume();
}

JavaFX Alert Dialog Cancel Button

I made a JavaFX alert dialog box to prompt the user, asking if they want to save the output from the console before closing the application.
I have the yes and no options taken care of. If the user clicks cancel, I want it to just close the dialog box and leave everything open. As of right now, if I hit cancel it will close the GUI.
Here is my code for overriding the close button on the GUI.
primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>()
{
#Override
public void handle(WindowEvent event)
{
Alert alert = new Alert(AlertType.WARNING);
alert.setTitle("Warning");
alert.setHeaderText("Would You Like To Save Your Console Output?");
alert.setContentText("Please choose an option.");
ButtonType yesButton = new ButtonType("Yes");
ButtonType noButton = new ButtonType("No");
ButtonType cancelButton = new ButtonType("Cancel", ButtonData.CANCEL_CLOSE);
alert.getButtonTypes().setAll(yesButton, noButton, cancelButton);
Optional<ButtonType> result = alert.showAndWait();
if(result.get() == yesButton)
{
Main.setConsoleVisible();
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
}
else if(result.get() == noButton)
{
System.exit(0);
}
else if(result.get() == cancelButton)
{
}
}
});
Both in "yesButton" and "cancelButton" if-blocks consume the CloseRequest WindowEvent:
else if(result.get() == cancelButton)
{
event.consume();
}
Use Platform.exit() instead of System.exit(0).
Use primaryStage.close(); instead of System.exit(0);
From the documentation for onCloseRequest:
Called when there is an external request to close this Window. The installed event handler can prevent window closing by consuming the received event.
Be aware that result.get() will throw an exception if the user closes the alert dialog without pressing any buttons. The Dialog documentation explains this thoroughly.

'Not on FX application thread' error when starting thread from dialog with loaded FXML as content

I currently have the following situation:
I have created a JavaFX application, which contains a screen from which I open a dialog (just by clicking a button on the screen). Then, the user gives input and clicks the apply button. The user input is then sent to a method, which opens some sort of progress dialog (for showing the user the status of the synchronization, which is not important for the problem). I will call this dialog 'MyDialog'. MyDialog is built with the following code:
Dialog<Void> dialog = new Dialog<>();
dialog.initOwner(null);
dialog.initStyle(StageStyle.UNDECORATED);
dialog.setHeaderText("Nieuw product synchroniseren...");
dialog.setResizable(false);
//Load dialog FXML file into the Pane
FXMLLoader fxmlloader = new FXMLLoader();
fxmlloader.setLocation(getClass().getResource("dialogs/MyDialogContent.fxml"));
try {
dialog.getDialogPane().setContent(fxmlloader.load());
} catch (IOException e) {
Functions.createExceptionDialog(e);
}
MyDialogContentController childController = fxmlloader.getController();
final ButtonType canceledButtonType = new ButtonType("Cancel", ButtonData.CANCEL_CLOSE);
dialog.getDialogPane().getButtonTypes().add(canceledButtonType);
This works just fine. The ProgressBar, which is shown in MyDialog, indicated the progress of a task. The task is started from a thread. This thread is started from the controller of the upper screen. From within the task, I would like to show an additional dialog at some point, to get some user validation. I will call this dialog 'AlertDialog'. This is the code for that part (which is placed in the Controller of the upper screen, and not in the Controller of MyDialog):
Task<Object> task = new Task<Object>() {
#Override
protected Object call() {
//Show choice dialog
Alert alert = new Alert(AlertType.CONFIRMATION);
alert.initOwner(null);
alert.initStyle(StageStyle.UNDECORATED);
ButtonType buttonTypeOne = new ButtonType("One");
ButtonType buttonTypeTwo = new ButtonType("Two");
ButtonType buttonTypeThree = new ButtonType("Three");
ButtonType buttonTypeCancel = new ButtonType("Cancel", ButtonData.CANCEL_CLOSE);
alert.getButtonTypes().setAll(buttonTypeOne, buttonTypeTwo, buttonTypeThree, buttonTypeCancel);
Optional<ButtonType> result = alert.showAndWait();
if (result.get() == buttonTypeOne){
//User chose "One";
} else if (result.get() == buttonTypeTwo) {
// ... user chose "Two"
} else if (result.get() == buttonTypeThree) {
// ... user chose "Three"
} else {
// ... user chose CANCEL or closed the dialog
}
}
}
Unfortunately, the AlertDialog is not showing, and I get the following error:
Exception in thread "Thread-10" java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-10
I already tried the following solutions:
Place the AlertDialog code in the MyDialogController, and then call it from the task.
Start the thread from MyDialogController, instead of starting in from the upper screen controller. And then call it by 'childController.thread'.
Both of these solutions did not work. I expect it has something to do with loading the FXML file in the DialogPane, and therefore with the thread and from where it is started.
So the questions in this situation are:
Why do I get this error?
Has the error something to do with the AlertDialog not showing?
Should my approach for this part of the code be different? (e.g. without loading an external FXML file in a dialog)
Any help is greatly appreciated!
I feel like I have written this 100 times on SO.. yet again: JavaFX is a single threaded GUI tookit, thus every thing GUI related has to be done on the main JavaFX Thread.
If you try to do something GUI related out of the JavaFX Thread you will get your IllegalStateException: Not on FX application thread.
Optional<ButtonType> result = alert.showAndWait(); is a GUI action because you initialize and show a Dialog. So you should retrieve your user input somewhere else and only do the long-running computations in background.
Good points for user input are for example the various life-cycle hooks of the Task class (like succeeded() or failed()).

Categories

Resources