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.
Related
startbtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
isRun = true;
while(isRun)
runProgram();
}
});
stopbtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
isRun = false;
}
});
When I click the startbtn Button, the whole program just stopped and I can't handle anything.
What's wrong?
The problem is that when you set the variable isRun to true the program enters in the while loop without exiting. This happens in the same thread the GUI is running, so it becomes irresponsive and it cannot handle any other event (as the click to Stop the running action by setting isRun to false).
So, the solution is to run the while loop task in another thread, leaving the thread where the GUI is running free to handle more events.
To run the task in another thread I recommend you to use any of the classes in the concurrent framework such as ExecutorService
Going even further, if your purpose is to submit a task that may be canceled, you could even use Future.
Also, bear in mind to use some synchronization mechanism to guarantee both threads see the latest value assigned to isRun.
I am using InvokeAndBlock whenever any process based function performed. for example.
If I want to save something and it takes while to save the data then i used below code.
First show process dialog.
initProcessDialog();
progressDialog.showModeless(); // show process dialog
//Actual process
Display.getInstance().invokeAndBlock(new Runnable() {
public void run() {
saveAll("SAVE_ALL",jobData);
FileUtil.removeBackupFile(jobDataDetail.getJobTemplateFileName());
progressDialog.dispose();
}
});
also added InvokeAndBlock while any action performed which is time-consuming.
backButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
initLoadingDialog();
loadingDialog.showModeless();
Display.getInstance().invokeAndBlock(new Runnable() {
public void run() {
}
});
}
});
Any correction require in process ?
Since your calls to invokeAndBlock are mostly related to your own code its hard to tell what exactly you are doing.
The call to dialog.dispose() within invokeAndBlock is wrong. You need to call it after the invokeAndBlock which will work exactly the same without the EDT violation.
Codename One has one UI thread: the EDT.
invokeAndBlock opens a separate thread where you aren't allowed to access any UI related API's.
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.
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 have implemented Conway's Game of Life problem in Java swing. Everything is working fine. As you can see in the screenshot below, whenever the "Tick" button is clicked, the game progresses to the next life form. Now, I am planning to include an "Autoplay" button alongside "Tick" button. The purpose of this autoplay is simple. When I hit it, an automated operation should carry on as if I am pressing tick button at an interval of 1 second.
I tried this. But this seems to block all the other operations. How to do this action in a separate thread? A small code snippet would get me going.
class AutoPlayListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
if(e.getSource() == btnAutoPlay){
while(true){
Thread.sleep(1000); //InterruptedException try catch hidden
btnTick.doClick();
}
}
}
}
Use a javax.swing.Timer. It will be able to work with the existing ActionListener if the while(true) and Thread.sleep() calls are removed.
As #Ranman said you're blocking main UI thread. I believe SwingUtilities.invokeLater is usually used for things like this.
There are two options:
Start a new thread. The thread will contain the while loop, and execute a method that processes the array. In each iteration, call repaint() or invalidate() on your window to tell it that it needs redrawing.
Use a Timer. The GUI thread will call your routine at regular intervals.
Threads:
In actionPerformed method, create a new Thread. and call its start method.
The Runnable of the thread should run a while loop (as you have already done), and then simply exit.
Timer:
Create an object in your class of type Timer. Use the one in java.swing.Timer if you are using swing (there is also java.util.Timer which isn't good for GUI ops). The timer should have an ActionListener that calls your method once, but the Timer has a repeat rate of 1000ms.
Tips
to invoke the action, you should put it in a separate method, rather than directly under the button handler. That way, you aren't calling GUI stuff from outside the GUI thread.
e.g.
tickButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
doTick();
}
});
The mechanism to stop the thread is equally important! In general, don't use a while(true) in a thread as it will get lost; invent a semaphore to terminate it.
use a JToggleButton rather than Button?
Synchronization:
If you use threads, you will need something like this, to prevent new threads being created each time the button is pressed:
Code
Thread autoplayThread = null;
Object lock;
boolean autoplaying = false;
public void actionPerformed(ActionEvent e){
synchronized(lock){ // prevent any race condition here
if(!autoplaying && autoplayThread==null ){
autoplaying = true;
autoplayThread = new Thread(new Runnable(){
public void run(){
try{
while(autoplaying){ .... }
}finally{
synchronized(lock) {
autoplaying=false;
autoplayThread=null;
}
}
}
});
autoplayThread.start();
}else{ // stop the thread!
autoplaying=false;
}
}
}