I have a task that can take a few seconds to a few minutes, and when I click on the button to execute the task, it runs the task but does not always disable button A and enable button B.
Here is the code that I am using:
#FXML
public void onExecute(ActionEvent event){
btnExecute.setDisable(true);
btnStopExec.setDisable(false);
new Thread(){
#Override
public void run(){
Platform.runLater(() -> {
QueryTable qt = new QueryTable(currentMysqlConn, currentDatabase);
qt.setTabPane(resultsTabPane);
qt.setQuery(queries);
qt.executeQueries();
btnExecute.setDisable(false);
btnStopExec.setDisable(true);
});
}
}.start();
}
If I comment out the button disabling in Platform.runLater() button A gets disabled and button B get enabled, but after Platform.runLater() runs. Why does this work sometimes and not others?
According to the Javadocs for Platform.runLater(...), it
Runs the specified Runnable on the FX Application Thread
So the only thing your background thread does is to schedule all your time-consuming database work to run on the FX Application Thread: your background thread is basically redundant, and your UI will be unresponsive while the database work is running.
If it happens that a frame is rendered between the calls to btnExecute.setDisable(true); and the runnable you define getting executed, then you will see the disabled state change. If not, then all your code gets executed during the same frame rendering(*), so you will never see the disabled state change.
Platform.runLater() should be called from a background thread just to update the UI. So you could make this work as follows:
#FXML
public void onExecute(){
btnExecute.setDisable(true);
btnStopExec.setDisable(false);
new Thread(){
#Override
public void run(){
QueryTable qt = new QueryTable(currentMysqlConn, currentDatabase);
qt.setTabPane(resultsTabPane);
qt.setQuery(queries);
qt.executeQueries();
Platform.runLater(() -> {
btnExecute.setDisable(false);
btnStopExec.setDisable(true);
});
}
}.start();
}
Platform.runLater(...) is a pretty low-level approach for FX work. The javafx.concurrent package defines a higher-level API for this: in particular the Task class encapsulates a background task and provides callbacks that will be executed on the FX Application Thread so you can update the UI. The Javadocs have copious examples, but you could do:
#FXML
public void onExecute(){
btnExecute.setDisable(true);
btnStopExec.setDisable(false);
Task<Void> databaseTask = new Task<Void>() {
#Override
public void call(){
QueryTable qt = new QueryTable(currentMysqlConn, currentDatabase);
qt.setTabPane(resultsTabPane);
qt.setQuery(queries);
qt.executeQueries();
return null ;
}
};
databaseTask.setOnSucceeded( event -> {
btnExecute.setDisable(false);
btnStopExec.setDisable(true);
});
new Thread(databaseTask).start();
}
(*) This is a somewhat imprecise statement, but it's qualitatively correct. Technically, the rendering thread blocks while actions are being executed on the FX Application Thread. (The two are not the same thread, but they have a large amount of synchronization between them.) Thus it's impossible for a frame to be rendered between the call to QueryTable qt = new QueryTable(...); and btnStopExec.setDisable(true);. It is possible for a frame to be rendered between btnStopExec.setDisable(false); and the execution of your runnable (i.e before QueryTable qt = new QueryTable(...);). If such a frame is rendered, you see the disabled state change; if not, you don't. Whether or not that happens is just down to the timing of the calls with respect to the "pulses" (frame renderings), which are targeted to happen every 1/60th second.
Related
My plugin checks if the user is idle for a certain amount of time. With the solution from here, I tried to work my way around. But the eclipse application becomes unresponsive until the loop ends.
Also, the message box is just a plain box with no title and buttons. Can somebody tell me what is wrong with this code?
#Override
public void earlyStartup() {
Display.getDefault().asyncExec(new Runnable() {
public void run() {
//while(true) {
for (stop=System.nanoTime()+TimeUnit.MINUTES.toNanos(1);stop>System.nanoTime();) {
Display.getDefault().addFilter(SWT.KeyUp, new Listener() {
#Override
public void handleEvent(Event event) {
stop=System.nanoTime()+TimeUnit.MINUTES.toNanos(1);
System.out.println("checkpoint 1");
}
});
}
Shell shell = new Shell(Display.getDefault());
MessageBox dialog =
new MessageBox(shell, SWT.ICON_INFORMATION | SWT.OK);
dialog.setText("Alert!");
dialog.setMessage("You have been idle for the last 3 minutes.");
shell.open();
stop=System.nanoTime()+TimeUnit.MINUTES.toNanos(1);
System.out.println("checkpoint 2");
}
});
}
Display.asyncExec does not run code in a separate thread. It runs the code in the main UI thread as soon as it is available. The UI thread will be blocked until the code ends.
Instead you can run code in a normal Java thread. But you must call asyncExec to execute any UI code you want to run from the thread.
In your actual code you should only be calling Display.addFilter once. This adds a listener which will be called every time the key up event occurs from then onwards. Since this is UI code you can't actually run this in a background thread at all.
So you can't use a loop like you have shown. You have to keep track of things in the key listener, updating each time the listener is called.
I have a project that takes time to load everything so I create a splash screen that tells the user through a progressbar how much time it will take to fully load and show the UI, but I'm facing a problem.
When I create my splash, this shows up correctly but then I create and initialize the Principal frame and everything freeze until this has fully load.
So, I try to load my Principal frame in a thread using SwingWorker (and it works) but after unknown NullPointerExceptions and reading a lot I found that this is a terrible idea because I am not creating my UI in the EDT, so here I am stuck.
I know that I must do Swing Calls in the Event Dispatch Thread (EDT) and non-swing heavy work in SwingWorkers but initialize the Swing Components of my Principal Frame are a heavy work too so, what should I do?
I have read some question here, specially this, and I think I get it but I have doubts. Taking that example:
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
new SplashScreen();
}
});
// Code to start system (nothing that touches the GUI)
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
new MainFrame();
}
});
//.. etc
And reading this site that says:
The Swing framework manages component drawing, updates, and event handlers on the EDT.
Is creating a new component a Swing Call? If it is, What should I do if new MainFrame() will take some time because the project has a lot of components to initialize?
How do I tell the Splash something like "Program loaded 50%"?
What does a Swing Call means and how can I do a correct use of invokeLater and SwingWorker? Maybe the solution is too obvious or have already an answer, but I can't see it and I apologize if this is the case.
Thanks!
You're on a right track. But don't use invokeAndWait (if you have to only) - use invokeLater:
invokeAndWait
Causes doRun.run() to be executed synchronously on the AWT event dispatching thread.
invokeLater
Causes doRun.run() to be executed asynchronously on the AWT event dispatching thread.
Consider that block wrapped doLater is run on EDT thread and code wrapped in doOutside is invoked in another thread (and that's why you don't block the UI):
EDIT:
As pointed out in the comments I add the explanations for the concepts I'll use.
doLater {
// here goes the code
}
is a concept for:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// here goes the code
}
});
And
doOutside {
// here goes the code
}
is a concept for:
new Thread(new Runnable() {
#Override
public void run() {
// here goes the code
}
}).start();
doLater {
final MainFrame m = new MainFrame();
doOutside {
// handle heavy operation
final int result = 1;
doLater {
m.setResult(result);
}
}
}
Conclusion: everything that touches Swing in some way must be run on EDT.
If you want to update percentages:
doLater {
final MainFrame m = new MainFrame();
doOutside {
// handle progress
for(int i = 0; i < someSize; ++i) {
final int progress = i;
doLater {
m.getProgressBar().setProgress(progress);
}
}
}
}
I hope you understand the concept now. The SwingWorker just do exectly something as doOutside === doInBackground & doLater === done/progress
Btw. The code above is a real code: lookup Griffon framework in Groovy.
I would like to update a Label in a JavaFX application so that the text changes multiple times as the method runs:
private void analyze(){
labelString.setValue("Analyzing"); // (labelString is bound to the Label during initialization)
// <Some time consuming task here>
labelString.setValue("Analysis complete!");
}
But when I run this, the label does not update until the task finishes, and just displays whatever it was before until the analyze() method returns.
How can I force update the label so that it will show "Analyzing" in the beginning followed by "Analysis complete!" when the task is complete?
Assuming you are invoking your analyze() method on the FX Application Thread (e.g. in an event handler), your time consuming code is blocking that thread and preventing the UI from updating until it is complete. As #glen3b says in the comments, you need to use an external thread to manage this code.
JavaFX provides a Task API which helps you do this. In particular, it provides methods which invoke code on the Java FX Application thread for you, allowing you to update the UI safely from your background Task.
So you can do something like
private void analyze() {
Task<Void> task = new Task<Void>() {
public Void call() {
updateMessage("Analyzing");
// time consuming task here
updateMessage("Analysis complete");
}
};
labelString.bind(task.messageProperty());
new Thread(task).start();
}
If you need to unbind the StringProperty when the task is complete, you can do
task.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
#Override
public void handle(WorkerStateEvent event) {
labelString.unbind();
}
});
Java is not my mother tongue and I've been fighting with this problem for a little while.
Basically, I am finding a behavioural difference between calling method switchApplets() directly from init(), and calling it from within a new thread spawned by init().
The consequence of calling it from inside the new thread is that the new applet whitescreens -- until/unless the user resizes or minimizes their browser. If called at the end of init(), the new UI renders immediately without any input from the user. But that's not an option because it doesn't wait for the thread to finish its prep work.
Trimmed-down code:
public class PreLoader extends Applet implements AppletStub {
static JProgressBar pBar = null;
static JLabel message;
public void switchApplets() {
try {
Class main_class = Class.forName("MainClass");
Applet main_applet = (Applet)main_class.newInstance();
removeAll();
setSize(0,0);
setLayout(new GridLayout(1,0));
add(main_applet);
main_applet.init();
main_applet.start();
main_applet.setStub(this);
}
catch (Exception e) {
}
}
public void init() {
pBar = new JProgressBar(0, 100);
pBar.setValue(0);
pBar.setStringPainted(true);
message = new JLabel("Beginning work!");
add(message);
add(pBar);
FlowLayout flow = new FlowLayout();
setLayout(flow);
Thread t = new Thread ( new Runnable () {
public void run ()
{
longRunningFunction1();
longRunningFunction2();
message.setText("Work complete! Stand by..");
switchApplets(); //does NOT work as intended from here
return;
}
} );
t.start();
//switchApplets(); //works as intended if called HERE
}
public void longRunningFunction1() {
//perform some tasks, advance progress bar
}
public void longRunningFunction2() {
//perform some tasks, advance progress bar
}
public void start() {
return;
}
public void appletResize(int width, int height) {
return;
}
}
I tried making init() wait for the thread to finish so that I could call switchApplets() from there, but that only blocked the EDT and prevented the UI from updating. Also tried playing with SwingUtilities' invokeLater/invokeAndWait, but even though switchApplets() gets run on the EDT, it seems that it MUST be called directly from init() (or at least the thread init is running on) to have the desired effect.
Why does calling switchApplets() from within a new thread result in a slightly different (and unwanted) UI behaviour?
The consequence of calling it from inside the new thread is that the new applet whitescreens -- until/unless the user resizes or minimizes their browser.
It's likely a deadlock caused by trying to do UI code on the wrong thread.
I tried making init() wait for the thread to finish so that I could call switchApplets() from there, but that only blocked the EDT and prevented the UI from updating.
You're on the right track. You need to call switchApplets() only from the EDT, and only after the work is done on the other thread.
Are you sure you tried using invokeLater() or invokeAndWait() from within the spawned thread after the long running functions were done? It's been a long while since I did applets but I'm not aware of any applet-specific reason why it wouldn't work, and it would work in any other case. I.e.,
public void run()
{
longRunningFunction1();
longRunningFunction2();
SwingUtilities.invokeLater(new Runnable() {
public void run() {
message.setText("Work complete! Stand by..");
switchApplets();
}
});
}
However, the most proper way to do this is with a SwingWorker rather than a manually created thread. SwingWorker (which is not nearly as well-known as it should be) is designed exactly for the goal of performing background tasks on a separate thread while still being able to update the GUI with progress updates and the results. E.g.,
new SwingWorker<Void,Void>() {
#Override
protected Void doInBackground() { // is called on a background thread
longRunningFunction1();
longRunningFunction2();
return null;
}
#Override
protected void done() { // is called on the Swing thread
message.setText("Work complete! Stand by..");
switchApplets();
}
}.execute();
The Void stuff is because SwingWorker is also capable of returning results and sending intermediate progress updates, but this example doesn't use those features.
You indicated that your long running functions are also updating a progress bar. That's another thing that should happen only on the Swing thread. In practice you can often get away without it, but it's dodgy. Your progress updates can use one of the SwingUtilities.invoke methods, or the mechanisms of SwingWorker; either should work. (SwingWorker itself provides two different ways to do it: Call addPropertyChangeListener (Swing thread) and setProgress (background thread), or call publish (background thread) and override process (Swing thread).)
Also, a small suggestion: if it's inconvenient to deal with a checked exception (or impossible to usefully do so), rather than catching and ignoring it, you should at least catch & rethrow it as an unchecked exception:
catch (Exception e) {
throw new RuntimeException(e);
}
That way, the stacktrace and error message of any exception will not be lost.
I'm making a chess program for a project. I'm trying to add a move history box to the side of the board. The move history works fine, and the data is properly sent to the text area, but the text inside the JTextArea disappears while the AI is thinking about his move.
public void aiMove(){
if (!playing){ return; }
paintImmediately(0,0,totalX,totalY);
ai = eve.getMove(chess,wtm,aiOut); //text disappears here
chess.makeMove(ai);
wtm = !wtm;
humanMove = true;
writeMove(ai); //updates move history, text reappears here
playing = stillPlaying();
repaint();
}
private void writeMove(Move move){
char c = "abcdefgh".charAt(7-move.fromY);
char h ="abcdefgh".charAt(7-move.toY);
String s = Character.toString(c)+(move.fromX+1)+" - "+Character.toString(h)+(move.toX+1)+" ";
if (!wtm){
String q = chess.getFullMove()+". "+s+" ";
moves.setText(moves.getText()+q);
}
else {
moves.setText(moves.getText()+s+"\n");
}
}
Here's a print screen of what's happening.
http://s13.postimage.org/mh7hltfk7/JText_Area_disappear.png
SOLVED
Thanks to all replies. I changed aiMove() so it creates a thread. Here is what I did.
Attempt #3... swing is still so foreign to me. I didn't want to change writeMove to getMove or I would have to rewrite the human's turn slightly. Since the project is essentially done, I am trying to avoid as much work as possible :)
The GUI is entirely optional anyways, I was just doing it for fun, and to try and learn a bit of swing.
public void aiMove(){
if (!playing){ return; }
if (!aiThread.isAlive()){
aiThread = new Thread(){
public void run(){
ai = eve.getMove(chess,wtm,aiOut);
chess.makeMove(ai);
wtm = !wtm;
humanMove = true;
SwingUtilities.invokeLater(new Runnable(){
public void run(){
writeMove(ai);
}
});
repaint();
playing = stillPlaying();
}
};
aiThread.start();
}
}
It also fixed a problem I had before, in that if I were to hold down the 'a' key (force ai move), it would queue up many forced ai moves. Now that doesn't happen.
The problem is your AI thinking is CPU intensive/time consuming, thus it is considered a long running task. You should not do long running tasks on GUI Event Dispatch Thread as this will cause the UI to seem frozen and thus only show updates after the task has finished.
Fortunately there are 2 different approaches you could use:
Use a Swing Worker which as the tutorial states:
The SwingWorker subclass can define a method, done, which is
automatically invoked on the event dispatch thread when the background
task is finished.
SwingWorker implements java.util.concurrent.Future.
This interface allows the background task to provide a return value to
the other thread. Other methods in this interface allow cancellation
of the background task and discovering whether the background task has
finished or been cancelled.
The background task can provide
intermediate results by invoking SwingWorker.publish, causing
SwingWorker.process to be invoked from the event dispatch thread.
The background task can define bound properties. Changes to these
properties trigger events, causing event-handling methods to be
invoked on the event dispatch thread.
Alternatively create separate Thread for AI thinking and wrap setText call in SwingUtilities.invokeLater(...);
Thread t=new Thread(new Runnable() {
#Override
public void run() {
}
});
t.start();
UPDATE
After reading MadProgrammers comment (+1 to it) please remember to create/manipulate your GUI/Swing components on EDT via the SwingUtilities.invokeLater(..) block. You can read more on it here.
UPDATE 2:
That edit is defeating the point, the only call on EDT in SwingUtilitites block should be the setText or atleast only code that manipulates a Swing component i.e
public void aiMove(){
if (!playing){ return; }
if (!aiThread.isAlive()){ //originally initialized by constructor
aiThread = new Thread(){
public void run(){
ai = eve.getMove(chess,wtm,aiOut);
chess.makeMove(ai);
wtm = !wtm;
humanMove = true;
SwingUtilities.invokeLater(new Runnable(){
public void run(){
writeMove(ai);
}
});
repaint();
playing = stillPlaying();
}
};
aiThread.start();
}
}