I'm developing an Eclipse plugin that will contribute to the GUI with a view.
The view is updated with informations from a versioning system when the user selects a folder or a file in the workspace.
In order to avoid collecting data everytime the user goes through the project subfolders and files, I need to wait for 3 seconds in order to be sure that the file or folder is the one of interest.
I'm currently doing this using a Swing Timer.
This is ok for small amount of data, but for large amount of data the GUI blocks, waiting for the timer to execute the update function.
I know for this kind of task I can use SwingWorker but I can't figure out how to delay the task and to restart the delay when needed.
Can anyone give me a solution on how to correctly solve this problem ?
Here is my current code:
public void resetTimerIfNeeded()
{
if(timer.isRunning())
timer.restart();
else
timer.start();
}
public void timer()
{
selectionTimer = new Timer(3000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
Display.getDefault().syncExec(new Runnable(){
#Override
public void run()
{
updateView();
selectionTimer.stop();
}
});
}
});
}
Since Eclipse uses SWT rather than Swing it is best to avoid using Swing code.
You can run code in the UI thread after a delay using UIJob, something like:
UIJob job = new UIJob("Job title") {
#Override
public IStatus runInUIThread(IProgressMonitor monitor) {
updateView();
return Status.OK_STATUS;
}
};
job.schedule(3000);
Alternatively you can use Display.timerExec:
Display.getDefault().timerExec(3000, new Runnable(){
#Override
public void run()
{
updateView();
}
});
Schedule it as a Job instead: https://eclipse.org/articles/Article-Concurrency/jobs-api.html . Use a UIJob if the entirety of what it's doing is interacting with the UI. The cancel/schedule and sleep/wakeUp methods will be of interest , see http://help.eclipse.org/luna/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/core/runtime/jobs/Job.html for the JavaDoc.
Related
I have a problem with a long running task.
After my dialog is shown I want to scan a ftp directory. This task takes some time so I need to run this task no in the UI thread.
My idea was
#Override
protected void postDialogOpen() {
// if invoked via menu button
if (!scanFtp) {
final Display display = Display.getDefault();
new Thread(new Runnable() {
#Override
public void run() {
//initProgressWaitViewer();
scanFtpServer();
//closeProgressWaitViewer();
display.syncExec(new Runnable() {
#Override
public void run() {
updateTree();
}
});
}
}).run();
}
}
But during the execution of scanFtpServer() my dialog is not movable and if I click on it it becomes "unresponsible".
Is there something I am doing wrong?
When calling method run() in class Thread, you are executing the method on the caller thread, just like calling any other method. If you want to spawn a new thread and execute method run() in that thread, you need to call method start() instead, that will do all the work of setting up the thread and running it.
So replace
}).run();
with
}).start();
Try to do .start() instead of .run() .
I am trying to do the following: click a button, button disappears for 2 seconds, text appears for 2 seconds and after those 2 seconds the visibility is reversed. So far I have done this:
btnScan.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
txtScanning.setVisible(true);
btnScan.setVisible(false);
try {
Thread.sleep(2000); //1000 milliseconds is one second.
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
btnScan.setVisible(true);
}
});
and the result is that as soon as I click the btnScan, the whole program freezes for 2 seconds before doing anything. How do I add the delay at the correct order?
You should not call sleep method in your code that dispatches the event. All the work related UI is handled by EDT(Event Dispatch Thread) and a sleep method will cause it to freeze and hence your Swing application will freeze.
To overcome it you should use a Timer. Run the timer and execute the UI manipulation using SwingUtilities.invokeLater so that it is handled by EDT.
import java.util.Timer;
// make it a member variable
Timer timer = new Timer();
........
public void actionPerformed(java.awt.event.ActionEvent evt) {
button.setVisible(false);
timer.schedule(new TimerTask() {
#Override
public void run() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
button.setVisible(true);
}
});
}
}, 2000);
}
Currently in your code, you are causing the EDT (event dispatcher thread) to pause with the invocation of Thread.sleep
Performing any long running tasks in the EDT will cause your UI to freeze.
To achieve what you desire, use a SwingWorker thread to perform your actions
This might help: http://docs.oracle.com/javase/tutorial/uiswing/concurrency/worker.html
Swing is a single threaded environment, anything that blocks this thread will prevent it from processing new events, including repaint requests.
Swing is also not thread safe, meaning img you should never create or update the UI from outside the context of the EDT.
In this case you can use a Swing Timer to trigger a callback to occur at some time in the future which (the notification) will be executed within the context of the EDT, making it safe to update the UI with
Take a look at Concurrency in Swing and How to us Swing Timers for more details
Making use of Swing timer, you can do something like this:
btnScan.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
txtScanning.setVisible(true);
btnScan.setVisible(false);
Timer timer = new Timer(2000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent acv) {
btnScan.setVisible(true);
txtScanning.setVisible(false);
}
});
// setRepeats(false) to make the timer stop after sending the first event
timer.setRepeats(false);
timer.start();
}
});
I want to run my program where the value of a label changes after the Timer goes off. But whenever the Timer runs I will keep getting the Invalid Thread access error and my label does not get updated.
protected void createContents() {
<--GUI codes -->
//Timer set to go every 10 seconds
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
System.out.println("Timer");
lblState.setText("On");
}
};
new Timer(delay, taskPerformer).start();
}
This link from the SWT FAQ explains the error and how to solve it: any code that modifies GUI components (in your case, setting the text of the label) needs to run on the display thread, otherwise this error will occur.
To run on the display thread, wrap the code inside a Runnable and call Display.getDefault().syncExec( with the provided Runnable:
Display.getDefault().syncExec(new Runnable() {
public void run() {
// code that affects the GUI
}
});
All access to UI objects must be done in the user interface thread. You can do this using Display.asyncExec (or Display.syncExec).
Change your line:
lblState.setText("On");
to
Display.getDefault().asyncExec(() -> lblState.setText("On"));
for Java 8. For Java 7 or earlier use:
Display.getDefault().asyncExec(new Runnable() {
#Override
public void run()
{
lblState.setText("On");
}
});
I need to keep focus on JTextField. Application uses Swing library. I need set focus on that field from time to time in order to avoid user mistakes that would change focus to other comonents. I suppose I need to use SwingWorker. Set focus is an operation on Swing
component so it should be invoked in EDT. My question is how to write SwingWorker to do that?
I know that method done() pass tasks to be invoked in EDT but I need this task to be invoked every let's sey 2 seconds. Method done() is called one time.
So maybe sth like this will be ok?
public class myWorker extends SwingWorker<Void, Void> {
#Override
protected Void doInBackground() throws Exception {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
//here set focus on JTextField
return null;
}
});
}}
Edit:
I noticed that method process() that is a part of SwingWorker may be appropriate beacuse it is invoked in EDT. I'm not sure but this method is probably invoked always when I call publish() metod. So could you tell me if this code is valid to do this task?
private class KeepFocusWorker extends SwingWorker<Void, Void>
{
#Override
protected Void doInBackground() throws Exception
{
while(true)
{
publish();
}
}
#Override
protected void process(List<Void> chunks)
{
codeBar.requestFocusInWindow();
}
}
Use javax.swing.Timer instead of SwingWorker. In this case actionPerformed will be executed in EDT. Also to set focus in a component, you need to call requestFocus. As the name suggests, it is a request only and not guaranteed. So you may change you approach.
Timer timer = new Timer(2000, new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
codeBar.requestFocus();
}
});
timer.setRepeats(true);
timer.start();
Surely it's better to limit the user's ability to take focus away from the textfield in the first place? Personally I don't see why it's an issue but I suppose it's better to keep focus in the one component rather than letting the user shift focus only for it to be shifted back every few seconds.
Therefore you could add a FocusListener to the component, override the focusLost method and basically requestFocus() again.
codeBar.addFocusListener(new FocusAdapter() {
public void focusGained(FocusEvent e) {
}
public void focusLost(FocusEvent e) {
codeBar.requestFocus();
}
});
NB I've not actually tried this myself but can't see why it wouldn't work.
Alternatively you can use an InputVerifier which always returns false to prevent focus being taken away.
I have the following code...
runNow.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
final Search s = (Search) node.getUserObject();
// Swing worker
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
#Override
public Void doInBackground() {
s.run();
return null;
}
#Override
public void done() {
window.updateResultsPanel(s.getResults(), s.getName());
}
};
worker.run();
}
});
This is a popup menu action which should create a new SwingWorker, free up the GUI, do some work, then update the results of the window. However, right now when I click the menu item the GUI becomes locked up until the work completes which is baffling, the whole point of using a SwingWorker is so that won't happen.
Any ideas as to what I'm doing wrong would be greatly appreciated.
-Cody
You should be calling SwingWorker#execute to start the worker, not run
Run is exposed by the Runnable interface, but execute actually schedules the worker to run at some time in the future.
Have to change method SwingWorker.run() to SwingWorker.execute() and it worked like a charm.