We have a ViewerFilter for a TableViewer that is a little slow, so to try to give the impression of awesomeness, we wanted to have the viewer wait 500 milliseconds before refreshing the window (otherwise, it was blocking after every key stroke).
Not having any clue what I was doing, I tried creating a class that would check if System.currentTimeMillis() was greater then the time of the last key stroke + 500 from a different thread. This just caused an Invalid thread access exception to be thrown, so I'm lost.
Edit: I was able to use TableViewer.getTable().getDisplay().asyncExec() to sidestep the invalid thread problem, but I don't like my solution, and would love to hear other suggestions.
You might want to try to turn off redraw while updating the viewer.
Viewer.getControl().setRedraw(false);
// update
Viewer.getControl().setRedraw(true);
It can sometimes give a better user experience. You can also schedule a ui-job that you cancel when the user hits a new key or modifies the text. E.g.
class RefreshJob extends WorkbenchJob
{
public RefreshJob()
{
super("Refresh Job");
setSystem(true); // set to false to show progress to user
}
public IStatus runInUIThread(IProgressMonitor monitor)
{
monitor.beginTask("Refreshing", ProgressMonitor.UNKNOWN);
m_viewer.refresh();
monitor.done();
return Status.OK_STATUS;
};
}
and then reschedule the refresh in a separate job.
private RefreshJob m_refreshJob = new RefreshJob();
private Text m_filterText;
void hookModifyListener()
{
m_filterText.addModifyListener(new ModifyListener()
{
public void modifyText(ModifyEvent e)
{
m_refreshJob.cancel();
m_refreshJob.schedule(500);
}
});
}
If the user hits the Enter key you can schedule a refresh job without the delay,
Just wrap your code in display.syncExec, something like this:
Display.getDefault().asyncExec(new Runnable() {
public void run() {
// check refresh time
// refresh.
}
});
You may want to look in to asyncExec too, if syncExec does not meet your needs.
Related
My program listens to 3 types of events: ADD, DELETE, REFRESH which is triggered by a 3rd party library component on UI. My listener looks like this:
//the timestamp is the time when the event occurs
public void listenToEvent(Event event, long timestamp){
if(event.getName().equals("ADD")){
//handle ADD event
}else if(event.getName().equals("DELETE")){
//handle DELETE event
}else if(event.getName().equals("REFRESH")){
//handle REFRESH event
}
}
The code works fine for each event, except a little problem with REFRESH event:
when refresh happened on UI, the 3rd party lib component fires consecutive 3 events in a short time, that's: ADD->DELETE->REFRESH, in this case my listener thinks there are 3 events, but actually it is only a refresh action on UI.
How can I optimise my code so that when ADD->DELETE->REFRESH happens consecutively very quickly, my listener could be smart enough to know it is only a REFRESH?
(ADD and DELETE events are NOT instances of the REFRESH event)
As I already said in the comments, the piece of code I posted below is working(tested). You will probably need some tweaking of the REFRESH_TIMEOUT and probably make it thread-safe, but I've tested the basic idea:
"If ADD event comes, create a timer for it. When DELETE comes, check if there is a timertask already. If it's not-> process DELETE. If there is -> process REFRESH. If the timer expires-> process ADD"
It's a bit of a hack, but with the information you gave, I think this solution may be the easiest thing to do. You may get into a problems if the proper events are coming faster then is your REFRESH_TIMEOUT. In that case, the logic will get a bit more complicated.
long REFRESH_TIMEOUT=100;
Timer timer = null;
MyTask myTask = null;
public void listenToEvent(Event event, long timestamp){
if(event.getName().equals("ADD")){
timer = new Timer();
timer.schedule(myTask = new MyTask(event), REFRESH_TIMEOUT);
}
if(event.getName().equals("DELETE")){
if (myTask!=null && !myTask.expired){
processRefresh(event);
timer.cancel();
}else{
processDelete(event);
}
}
}
private static class MyTask extends TimerTask {
Event event;
boolean expired;
public MyTask(Event event){
this.event=event;
}
#Override
public void run() {
expired=true;
processAdd(event);
}
}
private void processAdd(Event e){
...
}
private void processDelete(Event e){
...
}
private void processRefrsh(Event e){
...
}
After some thinking, I came up with my own solution:
That's in ADD & DELETE condition, I use Thread.sleep(1000), then get the system time, after which I compare the latest system time get in REFRESH condition, if the difference is within 1sec, then it is a refresh event.
private long timeout = 1000;
private long addEventTime;
private long deleteEventTime;
private long refreshEventTime;
public void listenToEvent(Event event, long timestamp){
if(event.getName().equals("ADD")){
Thread.sleep(timeout);
addEventTime = System.currentTimeMillis();
if((refreshEventTime - addEventTime) >timout){
//handle ADD event
}
}else if(event.getName().equals("DELETE")){
Thread.sleep(timeout);
deleteEventTime = System.currentTimeMillis();
if((refreshEventTime - deleteEventTime) >timout){
//handle DELETE event
}
}else if(event.getName().equals("REFRESH")){
refreshEventTime = System.currentTimeMillis();
//handle REFRESH event
}
}
Any guru has any comment on my solution ?
If the event api is well thought imo, I'd think that an ADD event could be an instance of a REFRESH event.
An example of this would be:
//AccidentAPI provided by FooBar Corp.
public CarAccidentEvent extends AccidentEvent {
private String carMake;
public String getMake() {
return carMake;
}
}
So, your listener would be able to do something like this:
public void listenToAccidents(AccidentEvent e) {
if (e instanceOf CarAccidentEvent) {
doStuff();
} else if (e instanceOf SkyJumpingEvent) {
doOtherStuff();
} else {
blah();
}
}
But again, this is going on the assumption that the ADD and DELETE events are instances of the REFRESH event. Though, perhaps their documentation would reveal something further about the EventAPI that may help answer the problem better.
Otherwise, you could add three attributes for the listener for the System's time in Millis which if the time in Millis is a difference greater than say 1ms, then process it, otherwise, go to the REFRESH case.
As in the title, the user drops a file on the target. This should trigger an operation that is not quite immediate , say it takes 4 seconds.
The problem is that the icon used by the system to represent the dragged item stays on the window till the operation is completed and the void handle(DragEvent d) event returns.
This is perceived by the user as an application "freezing". We know that the application ever takes 4 seconds to process the dropped item, but this would probably not be noticed at all by the user if the icon disappeared immediately after the user releases the item on the target and before the 4 seconds operation started.
Here is the code relevant for dropping:
s.setOnDragDropped(new EventHandler<DragEvent>() {
#Override public void handle(DragEvent t) {
//1. The drop is OK
t.setDropCompleted(true);
//<--I THINK THAT SOMETHING SHOULD BE PUT HERE TO FORCE D&D TO REMOVE THE ICON
//2. let's start an elaboration that involves the files
// that have been dropped!
try { Thread.sleep(4000); } catch (InterruptedException ignore_exception_in_test) {}
}
});
Are there any options to address this problem?
The complete code, if you just want to reproduce the case; just fix imports and drop a file from Explorer into the stage.
public class ShortSelfCompilableExample01 extends Application {
Stage stage;
#Override public void start(Stage stage) throws Exception {
stage.setTitle("Drag&Drop test");
this.stage = stage;
Scene s = new Scene(new BorderPane());
stage.setScene(s);
s.setOnDragDropped(new EventHandler<DragEvent>() {
#Override public void handle(DragEvent t) {
//1. Ok, the drop is OK
t.setDropCompleted(true);
//2. let's start an elaboration that involves the files
// that have been dropped!
try {
Thread.sleep(6000);
} catch (InterruptedException ignore_exception_in_test_environments) {}
}
});
s.setOnDragOver(new EventHandler<DragEvent> () {
#Override public void handle(DragEvent t) {
t.acceptTransferModes(TransferMode.ANY);
}
});
stage.show();
}
}
PS. Why I didn't use a thread
The "first class" solution for responsive UIs is to use a thread.
But this is not a background task with very long execution time. This should last in the worst case 5 seconds, while threading introduces a certain degree of complexity.
The shorter is the lenght of the operation, the less is convenient introduce a thread just to "smooth" the UI.
Why this happens: the problem is in the design of the DnD. It depends on the platform, but in general the OS invokes some method which is that propagates up to JavaFX and ends up in your event handler. This method should return a result of the DnD operation so that the OS could know what to do. So it is synchronous by design.
What to do: your proposal with using another thread is probably the only way to handle this problem. It should be quite simple: get all the info needed for the long operation in the event handler, start a new thread, make the long operation there and update the UI using Platform.runLater
I have a use-case coming from a GUI problem I would like to submit to your sagacity.
Use case
I have a GUI that displays a computation result depending on some parameters the user set in a GUI. For instance, when the user moves a slider, several events are fired, that all trigger a new computation. When the user adjust the slider value from A to B, a dozens of events are fired.
But the computation can take up to several seconds, whereas the slider adjustment can fire an event every few 100 ms.
How to write a proper Thread that would listen to these events, and kind of filter them so that the repaint of the results is lively? Ideally you would like something like
start a new computation as soon as first change event is received;
cancel the first computation if a new event is received, and start a new one with the new parameters;
but ensure that the last event will not be lost, because the last completed computation needs to be the one with last updated parameters.
What I have tried
A friend of mine (A. Cardona) proposed this low level approach of an Updater thread that prevents too many events to trigger a computation. I copy-paste it here (GPL):
He puts this in a class that extends Thread:
public void doUpdate() {
if (isInterrupted())
return;
synchronized (this) {
request++;
notify();
}
}
public void quit() {
interrupt();
synchronized (this) {
notify();
}
}
public void run() {
while (!isInterrupted()) {
try {
final long r;
synchronized (this) {
r = request;
}
// Call refreshable update from this thread
if (r > 0)
refresh(); // Will trigger re-computation
synchronized (this) {
if (r == request) {
request = 0; // reset
wait();
}
// else loop through to update again
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void refresh() {
// Execute computation and paint it
...
}
Every-time an event is sent by the GUI stating that parameters have been changed, we call updater.doUpdate(). This causes the method refresh() to be called much less.
But I have no control on this.
Another way?
I was wondering if there is another way to do that, that would use the jaca.concurrent classes. But I could not sort in the Executors framework what would be the one I should start with.
Does any of you have some experience with a similar use case?
Thanks
If you're using Swing, the SwingWorker provides capabilities for this, and you don't have to deal with the thread pool yourself.
Fire off a SwingWorker for each request. If a new request comes in and the worker is not done, you can cancel() it, and just start a new SwingWorker. Regarding what the other poster said, I don't think publish() and process() are what you are looking for (although they are also very useful), since they are meant for a case where the worker might fire off events faster than the GUI can process it.
ThingyWorker worker;
public void actionPerformed(ActionEvent e) {
if( worker != null ) worker.cancel();
worker = new ThingyWorker();
worker.execute();
}
class ThingyWorker extends SwingWorker<YOURCLASS, Object> {
#Override protected YOURCLASS doInBackground() throws Exception {
return doSomeComputation(); // Should be interruptible
}
#Override protected void done() {
worker = null; // Reset the reference to worker
YOURCLASS data;
try {
data = get();
} catch (Exception e) {
// May be InterruptedException or ExecutionException
e.printStackTrace();
return;
}
// Do something with data
}
}
Both the action and the done() method are executed on the same thread, so they can effectively check the reference to whether there is an existing worker.
Note that effectively this is doing the same thing that allows a GUI to cancel an existing operation, except the cancel is done automatically when a new request is fired.
I would provide a further degree of disconnect between the GUI and the controls by using a queue.
If you use a BlockingQueue between the two processes. Whenever the controls change you can post the new settings to the queue.
Your graphics component can read the queue whenever it likes and act on the arriving events or discard them as necessary.
I would look into SwingWorker.publish() (http://docs.oracle.com/javase/6/docs/api/javax/swing/SwingWorker.html)
Publish allows the background thread of a SwingWorker object to cause calls to the process() method, but not every publish() call results in a process() call. If multiple process calls are made before process() returns and can be called again, SwingWorker concatenates the parameters used for multiple publish calls into one call to process.
I had a progress dialog which displayed files being processed; the files were processed faster than the UI could keep up with them, and I didn't want the processing to slow down to display the file names; I used this and had process display only the final filename sent to process(); all I wanted in this case was to indicate to the user where the current processing was, they weren't going to read all the filenames anyway. My UI worked very smoothly with this.
Take a look at the implementation of javax.swing.SwingWorker (source code in the Java JDK),
with a focus on the handshaking between two methods: publish and process.
These won't be directly applicable, as-is, to your problem - however they demonstrate how you might queue (publish) updates to a worker thread and then service them in your worker thread (process).
Since you only need the last work request, you don't even need a queue for your situation: keep only the last work request. Sample that "last request" over some small period (1 second), to avoid stopping/restarting many many times every 1 second, and if it's changed THEN stop the work and restart.
The reason you don't want to use publish / process as-is is that process always runs on the Swing Event Dispatch Thread - not at all suitable for long running calculations.
The key here is that you want to be able to cancel an ongoing computation. The computation must frequently check a condition to see if it needs to abort.
volatile Param newParam;
Result compute(Param param)
{
loop
compute a small sub problem
if(newParam!=null) // abort
return null;
return result
}
To handover param from event thread to compute thread
synchronized void put(Param param) // invoked by event thread
newParam = param;
notify();
synchronized Param take()
while(newParam==null)
wait();
Param param = newParam;
newParam=null;
return param;
And the compute thread does
public void run()
while(true)
Param param = take();
Result result = compute(param);
if(result!=null)
paint result in event thread
I was building a small test tool with Java Swing using Netbeans IDE.
I am trying to update a label, which is somehow not getting 'repainted'/'refreshed'. I looked into a couple of similar questions on SO but was not able to resolve my problem.
private void excelFileChooserActionPerformed(java.awt.event.ActionEvent evt)
{
if(!JFileChooser.CANCEL_SELECTION.equals(evt.getActionCommand()))
{
String selectedFile = excelFileChooser.getSelectedFile().getAbsolutePath();
loaderLabel.setText("Please Wait..");
try {
//This is sort of a blocking call, i.e. DB calls will be made (in the same thread. It takes about 2-3 seconds)
processFile(selectedFile);
loaderLabel.setText("Done..");
missingTransactionsPanel.setVisible(true);
}
catch(Exception e) {
System.out.println(e.getMessage());
loaderLabel.setText("Failed..");
}
}
}
loaderLabel is a JLabel and the layout used is AbsoluteLayout.
So, my problem is "Please Wait..." is never shown. Although call to the method processFile takes about 2-3 seconds, "Please Wait..." is never shown. However, "Done..."/"Failed..." are shown.
If I add a popup (JOptionPane) before the call to processFile, "Please Wait.." is shown. I am not able to clearly understand why this is happening.
Is there a "good practice" that I should follow before a heavy method call? Do I need to call an explicit repaint/refresh/revalidate?
You need to call
processFile(selectedFile);
in another thread (not in the AWT thread). To do so you can do something like this :
Thread t = new Thread(){
public void run(){
processFile(selectedFile);
// now you need to refresh the UI... it must be done in the UI thread
// to do so use "SwingUtilities.invokeLater"
SwingUtilities.invokeLater(new Runnable(){
public void run(){
loaderLabel.setText("Done..");
missingTransactionsPanel.setVisible(true);
}
}
)
}
};
t.start();
Please not that I didn't work with swing for a long time, so there may be some syntax issues with this code.
Have you tried dispatching the call to the EDT with SwingUtilities.invokeLater() ?
http://www.javamex.com/tutorials/threads/invokelater.shtml
I'm trying to update the tab being displayed, however it seems to wait until the end of the method and then update. Is there a way to make the tab being displayed update immediately?
Here is an example of the code where I'm having this issue:
private static void someButtonMethod()
{
Button = new JButton("My Button");
Button(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
tabs.setSelectedIndex(1);
// Do some other things (In my case run a program that takes several seconds to run).
runProgram();
}
});
}
The reason for this is that the method is being executed in the Event Dispatch thread, and any repaint operations will also occur in this thread. One "solution" is to update the tab index and then schedule the remaining work to be invoked later on the EDT; this should cause the tab state to be updated immediately; e.g.
public void actionPerformed(ActionEvent evt) {
tab.setSelectedIndex(1);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// Do remaining work.
}
});
}
EDIT
Per your comment below an example of how to invoke a SwingWorker in order to call your runProgram method would look something like this:
// Typed using Void because runProgram() has no return value.
new SwingWorker<Void, Void>() {
protectedVoid doInBackground() {
runProgram();
return null; // runProgram() doesn't return anything so return null.
}
protected void done() {
// Called on the EDT when the background computation has completed.
// Could insert code to update UI here.
}
}.execute()
However, I sense a bigger problem here: The fact that you are seeing a significant delay in updating the tab makes me think you are performing long running calculations on the EDT. If this is the case you should consider performing this work on a background thread. Take a look at the SwingWorker class.