Can I be explained how updating GUI threads really work? It is all messy to me.
For instance I want to update a ProgressBar in a loop. I got it that I need to put the loop into a new Task. I binded the progressBar's progressProperty to the Task's progress.
If I call the Task with
new Thread(task).start();
combined with the Task's updateProgress() method in the call function, it works fine, the progressBar is updated.
Question one : why do I fail at updating the ProgressBar by setting directly its progress inside of the loop (progressProperty being not binded) ? Same occurs if I want to set it (non-)visible inside of the loop.
Question 2 : Let the progressProperty be binded to the Task.progressProperty. Why can't I update the progressBar by calling the Task with Plateform.runLater(task) ? It won't update the GUI thread.
Question 3 : how do I set the visibility of the progressBar inside of the loop?
public class PRogressBar extends Application {
ProgressBar progressBar;
#Override
public void start(Stage stage) {
/**
Task for updating the ProgressBar
*/
Task<Void> task = new Task() {
#Override
protected Object call() throws Exception {
System.out.println("Init Task");
// progressBar.setVisible(false) // Question 3
// progressBar.setProgress(0.75) // question 1
for (int i = 1; i < 5; i++) {
final int update = i;
try {
Thread.sleep(500);
System.out.println("Run later : " + update/5.0);
updateProgress(i, 5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}
};
/*
Initializing the GUI
*/
progressBar = new ProgressBar();
Button button = new Button("blou");
button.setOnAction((event) -> {
// Platform.runLater(task); // Question 2
Thread th = new Thread(task);
// th.setDaemon(true);
th.start();
System.out.println("Thread started");
});
StackPane layout = new StackPane();
layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 10;");
HBox hbox = new HBox(progressBar, button);
layout.getChildren().add(hbox);
progressBar.setProgress(0.1);
// setVisible2(false);
// progressBar.progressProperty().bind(task.progressProperty());
stage.setScene(new Scene(layout));
stage.show();
}
Question one : why do I fail at updating the ProgressBar by setting directly its progress inside of the loop (progressProperty being not binded) ? Same occurs if I want to set it (non-)visible inside of the loop.
Becouse all changes of the UI that effects the current displayed Stage have to executed in JavaFX Application Thread. Your Task is executed in your own Thread, so make sure to call setProgress and setVisible in the JavaFX Application Thread. You have to make this by Platform.runLater(() -> { //change UI});
updateProgress and updateMessage are thread-safe. Maybe these methods effects the UI, e. g. if you have bind the progressProperty to a ProgressBar. You can call it from the worker thread in a safe manner. But updateProgress is the exception not the rule.
Question 2 : Let the progressProperty be binded to the Task.progressProperty. Why can't I update the progressBar by calling the Task with Plateform.runLater(task) ? It won't update the GUI thread.
Do you mean Platform.runLater(() -> myTask.call());? This should work, becouse call get executed in the JavaFX Application Thread, so you can make changes to the UI. But this is not the way you should work with Tasks :)
Question 3 : how do I set the visibility of the progressBar inside of the loop?
You are outside of the JavaFX Application Thread so you have to use Platform.runLater(...) for it.
By the way, Brian Goetz describes in his book Java Concurrency in Practice on Chapter 9.1 Why are GUIs single-threaded?.
Related
Lately I encountered some issues with code that asynchonously updates GUI. I then came across this article, which shined some light on the problem - I was not using Platform.runLater() to update my GUI components, however, consider the original code:
public class Main extends Application {
private TextArea textArea = new TextArea();
private Label statusLabel = new Label("Not Started...");
private Button startButton = new Button("Start");
private Button exitButton = new Button("Exit");
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(final Stage stage) {
startButton.setOnAction(event -> startTask());
exitButton.setOnAction(event -> stage.close());
HBox buttonBox = new HBox(5, startButton, exitButton);
VBox root = new VBox(10, statusLabel, buttonBox, textArea);
root.setStyle("-fx-padding: 10;" +
"-fx-border-style: solid inside;" +
"-fx-border-width: 2;" +
"-fx-border-insets: 5;" +
"-fx-border-radius: 5;" +
"-fx-border-color: blue;");
Scene scene = new Scene(root, 400, 300);
stage.setScene(scene);
stage.setTitle("A simple Concurrency Example");
stage.show();
}
private void startTask() {
Runnable task = this::runTask;
Thread backgroundThread = new Thread(task);
backgroundThread.setDaemon(true);
backgroundThread.start();
}
private void runTask() {
for (int i = 1; i <= 10; i++) {
try {
String status = "Processing " + i + " of " + 10;
statusLabel.setText(status);
textArea.appendText(status + "\n");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
The problematic part is the runTask() method. The article explains that instead of simply using statusLabel.setText(status), I should use Platform.runLater(() -> statusLabel.setText(status));. That makes sense to me.
What doesn't make sense, however, is why I don't have to apply the same logic to textArea updates? Notice the fifth line of runTask() method - the textArea.appendText(status + "\n"); part. Why it doesn't give me an exception (java.lang.IllegalStateException: Not on FX application thread) about modifying an FX component from non-FX thread, since it's clearly a way of updating GUI FX component? What operations should I put inside the Platform.runLater() and what operations do now have to be there?
Run later will interfere more or less with the UI Thread. The execution will be queued in the UI thread. When you have a thread sleep or a long calculation the UI will freeze. You can use it for simple tasks. But in your case I do recommend it, because it is only a "set-operation".
Generally you should use Observable properties and bind them to the UI when possible. Then you don't need to do anything of the "Thread" and "Queuing" and "RunLater" operations.
When the WHOLE operation is needed to executed with the "run later", and the calculation would be time costly, you should look, for what you can run later or use a different Mechanism.
But when you just add something with out run laterform a difficult or longer calculation from another thread, an exception will pop up.
So what you want is to update the UI-Elements when it is needed and the new values are calculated or provided by whatever mechanism.
Here the way to go with Properties:
StringProperty text = new SimpleStringProperty("Hello");
Label label = new Label();
label.getTextProperty().bind(text);
Now whenever you Update text from another Thread, the update will automatically occur in the UI without Run Later.
If there is no way around:
What you could (in the case of more costly operations) do is have a queue that contains all the executions of updates for the view that come asynchronously. And execute the changes only in the JavaFX Thread.
You should use the Animationtimer for that.
//thread safe queue
Queue<Runnable> queue = new LinkedBlockingQueue<Runnable>();
Now the approach with runLater:
Platform.runLater(()->{
while(!queue.isEmpty()) {
queue.remove().run();
}
});
And the approach with the AnimationTimer.
AnimationTimer timer = new AnimationTimer() {
#Override
public void handle(long now) {
//Whatever condition or how many changes you would make at one tick
//In this case we just run all "updates" in that tick.
// I would recommend it, you could update 100 things each tick
while(!queue.isEmpty()) {
queue.remove().run();
}
}
};
timer.start();
Then have a thread that would calculate something and when it's done it adds the new change to the queue. Or sleeps or waits for a response etc.
private void startTask() {
Runnable task = this::runTask;
Thread backgroundThread = new Thread(task);
backgroundThread.setDaemon(true);
backgroundThread.start();
}
private void runTask() {
for (int i = 1; i <= 10; i++) {
try {
String status = "Processing " + i + " of " + 10;
//Add the new change as a Runnable here
//It will be run in the in the next update of the UI
tasks.add(()->{
statusLabel.setText(status);
textArea.appendText(status + "\n");
});
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
NOTE
It still depends for what purpose you are going to use it.
Those are just two approaches.
When you only update declared values, you could also use Property Bindings.
(StringProperty in your case)
And I would personally always go with the AnimationTimer.
RunLater decides when it is "fine" with queuing the Runnable. With the Animationtimer, you can be sure it is executed each tick, when a update is needed.
You could also Store all the updates and then do a "poll" of the new Values each tick.
TASK
When you know exactly where you want to put which value you should use Task and bind it the the UI element, that would also work.
Label statusLabel = new Label("Not Started...");
Task<String> t = new Task<String>() {
#Override
protected String call() throws Exception {
int i = 0;
while(//any condition ... ) {
i++;
Thread.sleep(1000);
updateValue("i is " + i);
}
return "last value!";
}
// now Bind the TextProperty of the Label to the ValueProperty of the
// Task
statusLabel.getTextproperty().bind(task.getValueProperty());
Thread backgroundThread = new Thread(task);
backgroundThread.start();
I'm trying to build a javafx app in which i need to respond to mouse movements and clicks together just like what happens in counter strike when you shoot. But the problem is when i press the mouse button it will not respond to mouse movements anymore until i release the mouse button. I want them both to work together in parallel. I tried to set my listeners in separate threads but it doesn't work.This is an image of a gun pointer.
Image image = new Image("/pointer.png"); // a 25*25 PNG icon
ImageView imageView = new ImageView(image);
scene.setCursor(Cursor.NONE);
and then :
scene.setOnMouseMoved(e -> {
imageView.setX(e.getX());
imageView.setY(e.getY());
});
scene.setOnMousePressed(e -> Toolkit.getDefaultToolkit().beep());
I also tried to put them in separate threads, it doesn't work either but if it does there is another problem, i cannot change the coordinates of a javafx component in another thread and i get this error -even if it doesn't cause an error it will not work:
java.lang.IllegalStateException: Not on FX application thread
scene.setOnMouseMoved(e -> {
Thread thread = new Thread() {
#Override
public void run() {
imageView.setX(e.getX()); // here i cannot do stuff related
imageView.setY(e.getY()); // to javafx components
}
};
thread.start();
});
scene.setOnMousePressed(e -> {
Thread thread = new Thread() {
#Override
public void run() {
Toolkit.getDefaultToolkit().beep());
}
};
thread.start();
});
I also tried this but it doesn't work either
scene.setOnMouseMoved(e -> {
imageView.setX(e.getX());
imageView.setY(e.getY());
scene.setOnMousePressed(event -> Toolkit.getDefaultToolkit().beep());
});
So how i can handle this problem, how i can respond to mouse clicks in parallel with mouse movements with no conflict.
When the mouse is clicked and held, instead of onMouseMoved use onMouseDragged with same method signature. I believe that should satisfy your requirements.
As for the exception, just for your information, in order to run code on JavaFX Application Thread simply call Platform.runLater(some Runnable code); So in your case
Thread thread = new Thread() {
#Override
public void run() {
Platform.runLater(() -> {
imageView.setX(e.getX()); // this will now run fine
imageView.setY(e.getY());
});
}
};
Nevertheless, there is absolutely no need for extra threads, since the capture of events will be propagated only to the JavaFX Application Thread. There are various ways of filtering or handling those events. More information about events can be found here
I have a GUI with a TextArea and a Save Button. When I press the latter the text is saved. This takes around 2 seconds. During the process of saving the buttons should get another caption than before and after saving.
Here is my code:
saveButton.setText("Saving...");
Util.print("Saving...");
Thread saveIt = new Thread(new Runnable() {
#Override public void run() {
Platform.runLater(() -> {
try {
Thread.sleep(2000);
} catch (Exception ex) {
Util.print(ex);
}
saveButton.setText("Saved!");
Util.print("Saved!");
});
}
});
saveIt.setDaemon(true);
saveIt.start();
What happens:
The following output is produced on the command line after pressing the button:
Saving...
Saved!
The command line prints "Saving..." directly after I click on saveButton. 2 seconds after pressing saveButton the command line prints "Saved!" and the button caption changes to "Saved!".
What I would expect:
The command line output and the button caption show "Saving..." directly after I click on the save button. After 2 seconds the caption changes to "Saved!".
How can I achieve the expected behaviour?
Thank you very much in advance for your help.
P.S.: I know so many people have had problems with changing GUI elements from Threads. I already read some articles on StackOverflow & the web about it, but this one's a too hard nut for me. Just for reference, here is some of the things I tried so far, among others Tasks:
Constantly Update UI in Java FX worker thread
Why am I getting java.lang.IllegalStateException "Not on FX application thread" on JavaFX?
javafx, update ui from another thread
http://blog.axxg.de/javafx-ui-thread-update/
I had to put the Thread.sleep() part out of the Platform.runLater() process. Seemingly runLater() must as few workload as possible.
Platform.runLater(() -> {
saveButton.setText("Saving...");
Util.print("Saving...");
});
Thread saveIt = new Thread(new Runnable() {
#Override public void run() {
try {
sleep(2000);
} catch (Exception ex) {
Util.print(ex);
}
Platform.runLater(() -> {
saveButton.setText("Saved!");
Util.print("Saved!");
});
}
});
saveIt.setDaemon(true);
saveIt.start();
try wrapping your first setText into a Platform.runLater like this:
Platform.runLater(() -> {
saveButton.setText("Saving...");
});
Every change made to a JavaFX UI component has to been called from the JavaFX thread using the Platform.runLater
I have a method in Swing to hide and show some buttons, called setScreen(int stage), which will hide or not certain buttons depending on the stage parameter. I want to call the method, then wait a few seconds and then call it again, like in this code:
... //Here stage has been assigned to some value
setScreen(stage);
if (stage != STAGE_TWO) {
sleep(WAIT_TIME * 1000);
stage = WELCOME;
setScreen(stage);
}
The code for setScreen(int stage) is something like this:
void setScreen(int stage) {
switch (stage) {
case WELCOME:
screen.infoLabel.setText("Welcome!");
screen.startButton.setVisible(true);
break;
case STAGE_TWO:
screen.infoLabel.setText("We are in stage two!");
screen.startButton.setVisible(false);
break;
}
screen.validate();
}
Where screen is an instantiation of a class extending JFrame.
The problem here is that the first setScreen(stage) is never displayed, since the thread goes to sleep before the changes have been commited. I have tried substituting the sleep for a while loop checking the time of the system, but the effect is the same.
**EDIT: ** I have found in a recommended StackOverflow thread some information on Swing Timer that may be useful. I'll work with it and upload any useful advances I make.
You are sleeping event dispatch thread. Don't do it and use:
javax.swing.Timer timer = new javax.swing.Timer(WAIT_TIME * 1000, new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
if (stage != STAGE_TWO) {
stage = WELCOME;
setScreen(stage);
}
}
});
timer.start();
The if condition may be for entire timer as per your requirement
My Android App works fine, except the end sequence. Its a game and at the end the screen shows the score:
TextView allscores = new TextView(this);
allscores.setText("Your score: "+ mypoints);
Next I want the GUI to slowdown for a few seconds so the user has time to take in the information. Maybe 2 or 3 secs is fine.
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
layout.removeAllViews();
Bear in mind, I'm new to thread programming. All this is done on the UI thread, I have no other threads running.
Next I put in a final splash screen ending:
AnView animator = new AnView(this);
layout.addView(animator);
where AnView is my own extension of View.
All in all it runs great. Problem is the thread sleeps for 3 seconds without showing my final score. I don't know why. I don't know why it doesn't show my final score before sleeping. It actually goes blank, indicating that it removed all views and then slept.
You're not seeing the score because sleeping on the UI thread prevents the layout and drawing operations that make your text changes visible. Never sleep on the UI thread.
Using a new thread for this is overkill. Use a Handler and the postDelayed method to make something happen after a delay.
private final Handler mHandler = new Handler();
Then when you want to do something later,
mHandler.postDelayed(new Runnable() {
public void run() {
doDelayedThing();
}
}, 3000);
As others have pointed out: don't use sleep in the UI thread.
What you should do instead is described here:
http://developer.android.com/guide/appendix/faq/commontasks.html#threading
in section "Handling Expensive Operations in the UI Thread"
It is not recommended to ever sleep the main UI thread. Try something like this:
new Thread(new Runnable(){
//do work here
});
Or you could try using a Toast popup for the score.
You can also use Timer to wait 3 seconds, which will start a new thread after the time runs out.
Timer timer = new Timer();
timer.schedule(new TimerTask() {
#Override
public void run() {
AnView animator = new AnView(this);
layout.addView(animator);
}
}, 3000);