JavaFX & GUI-Threads: Change button text from Thread - java

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

Related

why i can't do anything in my application when Jbutton is pressed and performing its defined function in java?

this is how I'm trying to accomplish this:
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("Hello there");
Thread.sleep(1000);
panel.updateUI();
}
});
I set Enter button as the default button so when I keep pressing it the button press maybe 100 times or more but because I'm using Thread.sleep(1000) it takes some time so I have time to type in my JtextField or even close the window but can't do anything.
also, I tried to put btnNewButton.addActionListener() in the run method of a thread but no difference.
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("Hello there");
Thread.sleep(1000);
panel.updateUI();
}
});
}
});
thread.start();
// I used try catch block in real code
can anyone help me to solve the issue?
**I'm creating this application in eclipse using windowsBuilder.
This is because of something that is called EDT - the Event Dispatch Thread.
This special purpose thread is responsible for all the events in your GUI - including drawing (refreshing) and interactions (button click). This means, that exactly the same thread is used to draw your application as the one that is used to execute actionPerformed code. Since you are literally halting that thread for some amount of time, your application will be non responsive for that exact period of time.
Whatever is being executed on user interactions should be short and execute fast for the reason of not blocking the application. If you need to do some heavy stuff, there is a facility for that purpose called SwingWorker that allows you to easily do some processing in the background thread (pool) and schedule UI updates during execution (like update of progress bar)
In your second "thread" snippet, your thread is not doing anything beside adding actionListener to the button, and then it terminates (probably after less than 1ms ;)) - action callback is still executed by the EDT. If you would start that custom thread from inside of run method - then it would be indeed in parallel and GUI would not be frozen - but again, SwingWorker is the way to go here.
Not entirely sure what your code is supposed to do, but if you want to press the button and then 1000ms later it will check your field you should do something like this:
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
System.out.println("before 1000ms");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("after 1000ms");
System.out.println("Reading the text field ...");
}
});
thread.start();
System.out.println("after the thread");
Output:
after the thread
before 1000ms
after 1000ms
Reading the text field ...

Java FX : Unable to open popup from different FX thread

Hi I have application which runs both on GUI(Java FX) as well as command line.
When run as GUI, i show the status on text area. This works fine.
But the issue is when ever i try to show a error(via popup) from some different (non javafx) class, it shows me Java Fx - thread Exception not on FX thread.
Below is my code
This is my Java FX class where I wish to show popup.
public class DeploymentProcesController implements Initializable {
#FXML
private TextArea statusTextArea;
#Override
public void initialize(URL location, ResourceBundle resources) {
}
public void updateGUIMessage(String message) {
if (Platform.isFxApplicationThread()) {
statusTextArea.appendText(message);
} else {
Platform.runLater(new Runnable() {
#Override
public void run() {
statusTextArea.appendText(message);
}
});
}
}
public void displayAlertMessages(final String message) {
Platform.setImplicitExit(false);
Task<Void> task = new Task<Void>() {
#Override public Void call() {
Platform.runLater(new Runnable() {
public void run() {
Alert alert = new Alert(AlertType.INFORMATION, message, ButtonType.OK);
alert.showAndWait();
}
});
return null;
}
};
new Thread(task).start();
}
}
I have a non FX class which is the entry point. So Based on type of run (command line / GUI ) I update the status.
Here is how I am calling to update the status.
public void promptUser(String message,boolean isCommandLineRun){
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
if(isCommandLineRun) {
System.out.println(simpleDateFormat.format(new Date()) + " - " + message);
} else {
controller.displayAlertMessages(message);
}
}
I have no issues when i call the updateGUIMessage method from non fx class. This is because the statusArea element is on FX thread(member of this fx class).
Also I have no issues to generate a alert box on some button click,
but to display an Alert box from a different class- I am having issues since as soon as I try to generate a alert box , the application crashes, saying not on fx thread.
I understand that the Alert box is a popup and therefore may be unhandled. But can anyone help me, I want to show user a alert box, from different class.
Assuming that you want some long running code to run before the popup is called,
There are two steps that need to be done when dealing with the Fx Buttons. The first thread lets the code run as a thread when the button is pushed. The second is the Platform.runlater() which is telling the Fx Platform to execute the code in that thread from the Platform. Note that the popup will not get executed until the runlater is reached in the outer thread.
Otherwise you can call the popup directly.
public void MyExampleBtn() {
Thread t = new Thread() {
// long running work to be done, threaded so as not to hang the main controls.
// ....
// work is done do the success popup
#Override
public void run() {
Platform.runLater(new Runnable() {
#Override
public void run() {
Alert alert = new Alert(AlertType.INFORMATION);
alert.setTitle("Success!");
alert.setHeaderText(null);
alert.setContentText("This is my popup");
alert.showAndWait();
}
});
}
};
}
Everything in the UI has to be executed from the UI application thread. That is exactly what the error message means.
Fortunately you can simply wrap your call so that it is executed in the UI thread:
if(isCommandLineRun) {
System.out.println(simpleDateFormat.format(new Date()) + " - " + message);
} else {
Platform.runLater(() -> controller.displayAlertMessages(message));
}
Finally found the solution to it,
Since Java fx runs on single thread, everything has to be on same thread. For every task (such as popup) where there needs background to pause, we need to use FutureTask.
I found this article, here :
JavaFX2: Can I pause a background Task / Service?

handle mouse move and click together in javafx

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

Button doesn't disable when function is called

I'm really lost with this:
In this Case the Button should diasable (btnStart.setEnable(false)) when it got clicked.
after this it should call a function, located in an other class.
Everything works, except that the btnStart doesn't disable on click but after the function ist called.
So this:
#Override
public void actionPerformed(ActionEvent buttonKLick) {
if(buttonKLick.getSource() == this.btnStart){
btnStart.setEnabled(false);
try {
Funktionen.fileFinder(Pfad); //The Function
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
Will call the function FileFinder and then disable the button although the button should be disabled before.
You've likely got a threading issue where your other method is taking time and locking up the Swing event thread, preventing Swing from properly painting the button as disabled. What happens if you use a background thread?
i.e.,
if(buttonKLick.getSource() == this.btnStart){
btnStart.setEnabled(false);
new Thread(new Runnable() {
public void run() {
try {
Funktionen.fileFinder(Pfad); //The Function
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}).start();
}
Edit:
Please be sure to read: Lesson: Concurrency in Swing.
No, it will disable the button right when you call setEnabled( false ). Just try calling isEnabled() before you call that function.
What you observe is that the button is only painted in disabled state after the function is executed. That is because your function occupies the one and only thread used to update the UI (the Event Dispatch Thread).
Solution: execute your function on a worker thread, for example using a SwingWorker. Also, read the Swing concurrency guide. This explains in more detail what I mentioned here

Getting and Setting a Flag variable in a thread

I am interfacing with a JNI that allows me to use a fingerprint scanner. The code I have written takes the scanned ByteBuffer parsed back to it by the JNI and turns it into a BufferedImage for saving.
What I can't figure out is how to wait for the scan thread to finish before the the jlabel icon on my GUI tries to update. What would be the easiest way to do this?
What else do I need to add?
Edit:
//Scanner class
Thread thread = new Thread() {
public void run() {
// [...] get ByteBuffer and Create Image code
try {
File out = new File("C:\\Users\\Desktop\\print.png");
ImageIO.write(padded, "png", out);
// [???] set flag here
} catch (IOException e) {
e.printStackTrace();
}
}
};
thread.start();
return true;
//Gui class
private void btnScanPrintActionPerformed(java.awt.event.ActionEvent evt) {
Scanner scanPrint = new Scanner();
boolean x = scanPrint.initDevice();
//Wait for the scanning thread to finish the Update the jLabel here to show
//the fingerprint
}
Not sure if you are using Swing or Android for UI but you would want to notify the main event dispatch thread (in swing it is called just that). You would run the scanning thread, then when complete send a 'message' to the EDT with the action that you want to be done to the button.
Thread thread = new Thread(new Runnable(){
public void run(){
//scan
SwingUtiltilies.invokeLater(new Runnable(){
//here you can update the the jlabel icon
public void run(){
jlabel.setText("Completed");
}
});
}
});
In UI development there is no waiting for an action to complete because you always want the EDT to be responsive.
at the end of a the scan thread, use SwingUtilities.invokeLater() to update the gui with the results of the scan.

Categories

Resources