Marko Topolnik
Marko Topolnik

Reputation: 200216

Asynchronously control the BusyIndicator

Apparently all Eclipse/SWT has in the way of managing the busy mouse indicator is

BusyIndicator.showWhile(Runnable synchronousStuffToDo)

However, I have a fundamentally event-oriented project where "stuff to do" doesn't happen within a sequential line of execution: an action gets ordered and a continuation-callback is provided to the execution manager. Therefore I have nothing meaningful to put into that synchronousStuffToDo runnable.

Is there another, however low-level and clumsy, but platform-independent way of manipulating the busy indicator asynchronously, which means two separate method calls, "activate it" and "deactivate it"?

I should add ProgressMonitorDialog to this question because it appears to suffer from the same problem. Yes, within the ProgressMonitorDialog#run method an inner event loop will be spinned, but SWT event loop is just one of my execution managers, so the chain will still be broken. Apparently without this class I can't even show a progress monitor except if I reimplement from lower-level primitives.

Upvotes: 3

Views: 2721

Answers (4)

VonC
VonC

Reputation: 1327324

11 years later (and using a more recent version of Java), you might consider, using Eclipse 4.33 (Q3 2024):

Enhanced BusyIndicator API for Futures

The BusyIndicator class defines the following new method:

 static void showWhile(Future> future)

If called from a Display thread, it waits for the given Future to complete and provides busy feedback using the busy indicator.

While waiting for completion, pending UI events are processed to prevent UI freeze.

If there is no Display for the current thread, Future.get() will be called, ignoring any ExecutionException and no busy feedback will be displayed.

In your case:

CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
    // assynchronousStuffToDo 
});

BusyIndicator.showWhile(future);

Upvotes: 1

Mihai Tudor
Mihai Tudor

Reputation: 446

I found a solution (which I don't particularly like, but it works) in an older SWT application I'm working on now. It uses BusyIndicator#showWhile, and the synchronous stuff it does inside is:

  1. Start the asynch task in a background thread
  2. Loop waiting for the background thread to finish up while at the same time spinning the SWT event loop explicitly:
while (!taskDone){
    if (!display.readAndDispatch() && !shell.isDisposed()) {
        display.sleep();
    }
    taskDone = //check for task progress
    //update something on the main window status bar
}

I'm trying to convert this to something cleaner (along the lines of what Marko suggested):

  1. Set the busy icon
  2. Submit background task
  3. Unset the busy icon

but I'm not sure what would be best for updating the status bar (background tasks are actually remote calls so their thread is blocked until they finish up). I'm thinking of having a dedicated thread that detects when background jobs are running and update the status bar accordingly (the update is just an unfolding dotted line, nothing task specific), but using a thread just for this seems a bit of a waste.

Upvotes: 0

Eugene
Eugene

Reputation: 9474

Your runnable should wait for the task completion. E.g. (code written in browser, will not compile - I'm ignoring exceptions):

final Object condition = new Object();
BusyIndicator.showWhile(new Runnable() {
    public void run() {
        synchronized(condition) {
            while (!isMyTaskDoneFlag()) {
                condition.wait();
            }
        }
    }
});
doTask(new MyTask() {
    public void perform() {
        try {
            // Task logic
            ...
        } finally {
            // When done
            setMyTaskDoneFlag();
            synchronized(condition) {
                condition.notify();
            }
        }
    }
});

Make sure all code paths in your tasks do not forget to unblock the runnable. Pretty much the same approach can be used with progress monitors - you may wake your runnable to update progress monitor value.

Note: You need to make sure the waiting runnable is not executed on SWT thread (e.g. set fork to true if running in progress monitor) or else your application will become unresponsive.

Upvotes: 0

Niranjan
Niranjan

Reputation: 1844

There is no way you can manipulate the Cursor using the BusyIndicator class. You can invoke the below util method to show a Busy Icon while running your job on a background Thread

public static void imBusy(final boolean busy){

    Display.getDefault().asyncExec(new Runnable()
    {
        @Override
        public void run()
        {
            Shell shell = Display.getDefault().getActiveShell();
            if(busy){ //show Busy Cursor
                Cursor cursor = Display.getDefault().getSystemCursor(SWT.CURSOR_WAIT);          
                shell.setCursor(cursor);
            }else{  
                shell.setCursor(null);
            }   
        }
    });

}

Upvotes: 6

Related Questions