I use a background-thread that should not stop immediately when JavaFX stops (as it does when no stage is open anymore as configured with setImplicitExit), so I do not use setDaemon for this one. But how can I check if JavaFX is shutting down? (This thread should just finish some things and stop itself)
I know I could put an setOnCloseRequest to all stages, but I'd prefer not doing that.
Runtime.getRuntime().addShutdownHook() does not work in this case as the machine does not go down as long as this thread is running).
Override Application.stop() and set a flag. Your thread will need to periodically check that flag.
SSCCE:
import java.util.concurrent.atomic.AtomicBoolean;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class ExitThreadGracefullyOnExit extends Application {
AtomicBoolean shutdownRequested = new AtomicBoolean();
#Override
public void start(Stage primaryStage) {
Label countLabel = new Label();
Thread thread = new Thread(() -> {
try {
int count = 0 ;
while (! shutdownRequested.get()) {
count++ ;
final String message = "Count = "+count ;
Platform.runLater(() -> countLabel.setText(message));
Thread.sleep(1000);
}
System.out.println("Shutdown... closing resources");
Thread.sleep(1000);
System.out.println("Almost done...");
Thread.sleep(1000);
System.out.println("Exiting thread");
} catch (InterruptedException exc) {
System.err.println("Unexpected Interruption");
}
});
VBox root = new VBox(10, countLabel);
root.setAlignment(Pos.CENTER);
thread.start();
Scene scene = new Scene(root, 350, 100);
primaryStage.setScene(scene);
primaryStage.show();
}
#Override
public void stop() {
shutdownRequested.set(true);
}
public static void main(String[] args) {
launch(args);
}
}
Related
I'm trying to remove all the nodes from my pane sequentially 1 by 1 so I can see each line being removed.To do this I have made a new thread and used the task class and wrapped the method delWalls() in a Platform.runLater() . I then used Thread.sleep thinking it would slow the loop slow so I could see the UI updating as each line is removed However what happens is the whole UI freezes up and then after the loop is done all the nodes have disappeared? Is there a way around this ... thanks
*all nodes are lines btw
//loop calls delWalls() 1458 times to delete all 1458 nodes sequentailly
Task task = new Task<Void>() {
#Override
public Void call() {
Platform.runLater(() -> {
try {
for (int i = 0; i <= 1458 - 1; i++) {
Thread.sleep(2);
delWalls();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
return null;
}
};
new Thread(task).start();
}
//delWalls method deletes one node each time it is called.
public void delWalls() throws InterruptedException {
pane.getChildren().remove(0);
}
As #MadProgrammer said, you need to work with Timeline to get the desired effect.
Below is a quick sample demo of how it can be done. Click "Add" to add nodes sequentially, and once all 10 nodes are added, click "remove" to remove them one by one.
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;
public class RemoveNodes_Demo extends Application {
#Override
public void start(Stage stage) throws Exception {
FlowPane pane = new FlowPane();
pane.setVgap(10);
pane.setHgap(10);
Button button1 = new Button("Add Nodes");
button1.setOnAction(e->{
Timeline timeline = new Timeline(new KeyFrame(Duration.millis(400), x -> {
StackPane sp = new StackPane();
sp.setMinSize(100,100);
sp.setStyle("-fx-background-color:black,red;-fx-background-insets:0,2;");
pane.getChildren().add(sp);
}));
timeline.setCycleCount(10);
timeline.play();
});
Button button2 = new Button("Remove Nodes");
button2.setOnAction(e->{
if(!pane.getChildren().isEmpty()){
int count = pane.getChildren().size();
Timeline timeline = new Timeline(new KeyFrame(Duration.millis(400), x -> {
if(!pane.getChildren().isEmpty()){
pane.getChildren().remove(0);
}
}));
timeline.setCycleCount(count);
timeline.play();
}
});
VBox root = new VBox(button1, button2,pane);
root.setSpacing(10);
Scene sc = new Scene(root, 600, 600);
stage.setScene(sc);
stage.show();
}
public static void main(String... a) {
Application.launch(a);
}
}
I want to immediately after increasing the value of i appear in the label
Example:
-in i=0 show 0
-in i=1 show 01
-in i=2 show 012
Can You Help me
import javafx.application.Application;
import javafx.scene.control.Label;
import javafx.scene.control.Button;
import javafx.scene.layout.Pane;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Example extends Application{
#Override
public void start (Stage primaryStage) {
Pane pane=new Pane();
Label label=new Label();
Button bt=new Button("Start");
pane.getChildren().addAll(bt,label);
bt.setOnAction(e->{
for (int i=0;i<10000000;i++) label.setText(label.getText()+i);
});
Scene scene = new Scene(pane,1000,500);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
The problem is that you update the label's value while you are on the user interface's thread. JavaFX works with a model where the updates are done at each 'tick' (60 fps). All the updates done are only visible once your eventhander's code has finished.
Additionally, given that this is a long running task it will result in an unresponsive user interface.
You should use a Worker to do the long running task. See the tutorial on asynchronous processing. Note that it will not guarantee that you will see all values as the worker can be quicker than the user interface updates and the system will coalesce these updates.
You can use Timeline to accomplish this task.
import java.util.concurrent.atomic.AtomicLong;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;
/**
*
* #author blj0011
*/
public class JavaFXApplication177 extends Application
{
#Override
public void start(Stage primaryStage)
{
AtomicLong i = new AtomicLong();
Label label = new Label();
Button btn = new Button();
Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(.5), (ActionEvent event) -> {//This controls how fast this should run. This example happens every half of a second
label.setText(label.getText() + Long.toString(i.getAndIncrement()));
}));
timeline.setCycleCount(10000000);//This controls the amount of time this should run
timeline.setOnFinished(event -> {//This tells what to do once cycle count is reached
btn.setDisable(false);
});
btn.setText("Start");
btn.setOnAction((ActionEvent event) -> {
btn.setDisable(true);
timeline.play();
});
StackPane stackPane = new StackPane(label);
VBox root = new VBox(stackPane, new StackPane(btn));
VBox.setVgrow(stackPane, Priority.ALWAYS);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args)
{
launch(args);
}
}
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class Test5 extends Application {
private String text = "";
private int i;
#Override // Override the start method in the Application class
public void start(Stage primaryStage) {
StackPane pane = new StackPane();
Label lblText = new Label("");
pane.getChildren().add(lblText);
new Thread(new Runnable() {
#Override
public void run() {
try {
for (i=0;i<10000;i++) {
Platform.runLater(new Runnable() { // Run from JavaFX GUI
#Override
public void run() {
lblText.setText(lblText.getText()+i);
}
});
Thread.sleep(200);
}
}
catch (InterruptedException ex) {
}
}
}).start();
Scene scene = new Scene(pane, 200, 50);
primaryStage.setTitle("FlashText"); // Set the stage title
primaryStage.setScene(scene); // Place the scene in the stage
primaryStage.show(); // Display the stage
}
}
Service<Void> service = new Service<Void>() {
#Override
protected Task<Void> createTask() {
return new Task<Void>() {
#Override
protected Void call() throws Exception {
//Your First Task #1
//Here UI won't be interrupted
Platform.runLater(new Runnable() {
#Override
public void run() {
//Your Second Task After Completion Of First One #2
}
});
return null;
}
};
}
};
service.start();
}
#1. The task that you want to perform in the background ex. loading the data has to be placed here. It's working great for me.
#2. Once the background task is finished this thread will be executed so Ui and background thread will run separately and smoothly.
I know it's too late for this answer but I just wanted to share what I did this might help!
I have a simple login window in JavaFX. When the user inserts his username and password I want to make a simple string "progress bar" in another thread while the main thread processes the inputs.
When the main thread gets to the if statement (let's say the passwords don't match) I want the progress to stop when the alert is thrown. But with this code it continues even after the alert is thrown.
public void validateLogin(ActionEvent actionEvent) throws Exception {
Thread thread = new Thread(() -> {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(loading_txt.getText().length() < 10)loading_txt.setText(loading_txt.getText() + "|");
else loading_txt.setText("|");
}
});
thread.start();
String username = username_field.getText();
String password = password_field.getText();
if (!(BCrypt.checkpw(password_field.getText(), dbHandler.getLoginByUsername(username_field.getText()).getPassword()))) {
throwAlert(Alert.AlertType.ERROR,"Login problem", "Password doesn't match.", "Wrong password. Please, check out and try it again. ");
thread.join();
return;
}
thread.join();
//other code
}
So I made a little change in the if statement and put the thread.join() before the alert. Now the progress can't be even seen.
if (!(BCrypt.checkpw(password_field.getText(), dbHandler.getLoginByUsername(username_field.getText()).getPassword()))) {
thread.join();
throwAlert(Alert.AlertType.ERROR,"Login problem", "Password doesn't match.", "Wrong password. Please, check out and try it again. ");
return;
}
How does this little change cause the progress to be seen or not to be seen? What do I have to change to stop the progressing when the alert is thrown? Could it be caused by some functionality in JavaFX?
Here is an Example, you may take the idea and apply it to your program (Explanation in comments).
import javafx.application.Application;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.TextField;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.scene.control.ProgressBar;
public class ProgressBarExample extends Application{
ProgressBar pb = new ProgressBar(); // your progress bar
#Override
public void start(Stage primaryStage) throws Exception {
// The structure and components are for example only
TextField password = new TextField();
Button test = new Button("Test");
HBox container = new HBox();
container.getChildren().addAll(password, test);
container.setAlignment(Pos.CENTER);
VBox root = new VBox();
root.setAlignment(Pos.CENTER);
root.getChildren().addAll(container);
// add action listener to the button
test.setOnAction(e->{
// when it's pressed add Progress bar and other stuff that are concerned with the GUI!
Platform.runLater(new Runnable(){ // always use this to update GUI components
#Override
public void run() {
root.getChildren().add(pb);
// you can add label to the root...etc
// or update your progress bar ..etc
// in a nutshell: anything needs to be updated in GUI.
}
});
Task<Boolean> validatePassword = new Task<Boolean>(){ // always use Task to do complex-long calculations
#Override
protected Boolean call() throws Exception {
return validatePassword(password); // method to validate password (see later)
}
};
validatePassword.setOnSucceeded(ee->{ // when Task finishes successfully
System.out.println("Finished");
root.getChildren().remove(pb); // remove the progress bar
if(!validatePassword.getValue()){
Alert alert = new Alert(AlertType.ERROR, "Wrong Password", ButtonType.OK);
alert.showAndWait();
}
});
validatePassword.setOnFailed(eee->{ // if it fails
System.out.println("Failed");
root.getChildren().remove(pb); // remove it anyway
});
new Thread(validatePassword).start(); // add the task to a thread and start it
});
Scene scene = new Scene(root, 300,300);
primaryStage.setScene(scene);
primaryStage.show();
}
// validate here in this method
public static boolean validatePassword(TextField password){
for(int i=0; i<99999; i++){ // suppose it is a long process
System.out.println("Processing");
}
if(password.getText().equalsIgnoreCase("Invalid")){ // suppose it's invalid, just for testing
return false
}
return true;
}
public static void main(String[] args) {
launch();
}
}
Test:
I am trying to terminate the thread that runs the JavaFX application when I close the window, without closing any other threads. This is my application class:
package testIt;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import java.io.IOException;
public class MemoryVisualizerApp extends Application{
public static void main(String[] args) {
launch(args);
}
//Setup the scene and launch with given properties
#Override
public void start(Stage primaryStage) throws IOException{
Parent root = FXMLLoader.load(getClass().getResource("/MemoryVisualizer.fxml"));
Scene scene = new Scene(root, 650, 340);
//Set whether the screen should be re-sizable (possibly best size = default)
primaryStage.setResizable(true);
primaryStage.setMinHeight(300);
primaryStage.setMinWidth(550);
primaryStage.setTitle("MINT Performance");
primaryStage.setScene(scene);
scene.getStylesheets().add("testIt/MemoryVisualizer.css");
primaryStage.show();
primaryStage.show();
primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>()
{
public void handle(WindowEvent e){
System.out.println("test");
try {
stop();
} catch (Exception e1) {
e1.printStackTrace();
}
}
});
}
}
For testing purposes, this app is the only thing running when I start the program, and so when I close the window, the entire program should terminate. But I still have the option to terminate the program (I'm using eclipse and the red square is still clickable), meaning the thread is still active.
How can I have it so that this thread terminates after closing the GUI window?
You can use Platform.exit() instead of stop()
primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>()
{
public void handle(WindowEvent e){
System.out.println("test");
try {
Platform.exit();
}
catch (Exception e1) {
e1.printStackTrace();
}
}
});
Take a look at the JavaFX Application life cycle
Try overriding the stop() method:
#Override
public void stop(){
System.exit(0);
}
This will cause the application to stop running in the IDE.
i am trying to influence a UI-Element during an event in javaFX.
void buttonClicked(ActionEvent e) {
labelInfo.setText("restarting - might take a few seconds");
jBoss.restart();
labelInfo.setText("JBoss successfully restarted");
}
The action "jBoss.restart()" waits till the JBoss is restarted.
The problem:
the text "restarting - ..." is not displayed. The application waits till the JBoss is restarted and then it shows the Text "JBoss successfully restarted".
My thoughts:
the scene is refreshed AFTER the event is completed. So the first label-change will not happen.
How can i show a info message during an event?
The problem it's that the FX Thread has no safe operations. So I'm guessing that jBoss.restart() it's taking a lot of time. So you have to put this command in a Service. Also I recommend to you a progress indicator to show to the user you are making a long operation.
Here it is an example but I encourage you to go to Concurrency in JavaFX and take a deep look on it. Maybe there are other things that can help you.
import javafx.application.Application;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Test extends Application {
public static void main(String[] args) {
launch(args);
}
private Label labelInfo;
private Button button;
private ProgressIndicator progressIndicator;
#Override
public void start(Stage stage) throws Exception {
VBox vbox = new VBox(5);
vbox.setAlignment(Pos.CENTER);
labelInfo = new Label();
button = new Button("Restart");
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
buttonClicked(event);
}
});
progressIndicator = new ProgressIndicator(-1);
progressIndicator.setVisible(false);
vbox.getChildren().addAll(labelInfo, progressIndicator, button);
Scene scene = new Scene(vbox, 300, 200);
stage.setScene(scene);
stage.show();
}
void buttonClicked(ActionEvent e) {
Service<Void> service = new Service<Void>() {
#Override
protected Task<Void> createTask() {
return new Task<Void>() {
#Override
protected Void call() throws Exception {
updateMessage("restarting - might take a few seconds");
// Here the blocking operation
// jBoss.restart();
Thread.sleep(10000);
updateMessage("JBoss successfully restarted");
return null;
}
};
}
};
// Make the progress indicator visible while running
progressIndicator.visibleProperty().bind(service.runningProperty());
// Bind the message of the service to text of the label
labelInfo.textProperty().bind(service.messageProperty());
// Disable the button, to prevent more clicks during the execution of
// the service
button.disableProperty().bind(service.runningProperty());
service.start();
}
}