hourglass component

For my long-running tasks, I take advantage of the progress bar, which is very useful. Unfortunately, some users complain that it is not visible enough. Is there a way to display an hourglass or a “in progress” component to make it evident that something is happening?
Thanks.

2 Likes

Hi Francis,

You can use all CUBA components to indicate progress, e.g. Labels, Buttons, ProgressBar, etc.

I strongly recommend to use BackgroundTask functionality to perform heavy background operations with progress indication on UI, it is the only way to update UI correctly from background threads.

Let’s see example:


<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<window xmlns="[url=http://schemas.haulmont.com/cuba/window.xsd]http://schemas.haulmont.com/cuba/window.xsd"[/url];
        caption="msg://caption"
        class="com.company.demo.web.screens.Demo"
        messagesPack="com.company.demo.web.screens">
    <layout expand="spacer"
            spacing="true">
        <progressBar id="progressBar"
                     width="300px"></progressBar>
        <label id="statusLabel" stylename="h2" value="Wait"></label>
        <vbox spacing="true">
            <button caption="Start custom progress"
                    invoke="runProgress"></button>
            <button caption="Start Window"
                    invoke="runTask"></button>
            <button caption="Start Progress Window"
                    invoke="runTaskProgress"></button>
        </vbox>
        <label id="spacer"></label>
    </layout>
</window>
  1. Simple progress indication from heavy background process

// obtain handler object of background task
// first type of BackgroundTask is progress unit - Integer, second is result type - String
BackgroundTaskHandler<String> handler = backgroundWorker.handle(new BackgroundTask<Integer, String>(60) {

    // Main method of background thread
    @Override
    public String run(TaskLifeCycle<Integer> taskLifeCycle) throws Exception {
        for (int i = 0; i <= 5; i++) {
            // indicate Integer progress on UI
            taskLifeCycle.publish(i);

            Thread.sleep(1000);
        }

        // Result of operation - simple String
        // also we can use any objects from services or third-party systems
        return "Done";
    }

    @Override
    public void progress(List<Integer> changes) {
        // Handle integer progress from background thread
        // Here we can change UI state
        statusLabel.setValue("In progress");
        progressBar.setValue((float)changes.iterator().next() / 5f);
    }

    @Override
    public void done(String result) {
        // task is completed, update UI
        statusLabel.setValue(result);
        progressBar.setValue(1f);
    }
});
// run background process
handler.execute();
  1. Indeterminate progress indication using built-in BackgroundWorkWindow

BackgroundWorkWindow.show(new BackgroundTask<Object, Object>(60, this) {
    @Override
    public Object run(TaskLifeCycle<Object> taskLifeCycle) throws Exception {
        Thread.sleep(5000);
        return null;
    }

    @Override
    public void done(Object result) {
        showNotification("Operation completed", NotificationType.HUMANIZED);
    }
});
  1. Progress window with number progress using built-in BackgroundWorkProgressWindow

BackgroundWorkProgressWindow.show(new BackgroundTask<Integer, Object>(60, this) {
    @Override
    public Object run(TaskLifeCycle<Integer> taskLifeCycle) throws Exception {
        for (int i = 0; i <= 5; i++) {
            taskLifeCycle.publish(i);
            Thread.sleep(1000);
        }

        return null;
    }

    @Override
    public void done(Object result) {
        showNotification("Operation completed", NotificationType.HUMANIZED);
    }
}, 5);

If you want to show indeterminate activity in your ProgressBar, just set indeterminate=“true” for progressBar from Studio or XML.

I’ve attached demo project, so you can play with BackgroundWorker and progress indication.

progress-demo.zip (21.8K)

custom-progress

indeterminate-progress

number-progress

Thanks Yuriy, it works great. Another question, is it possible to customize the text in the window?

Yes, it is possible.
Please take a look at BackgroundWorkProgressWindow / BackgroundWorkWindow methods:


show(BackgroundTask<T, V> task, @Nullable String title, @Nullable String message,
     boolean cancelAllowed)

show(BackgroundTask<T, V> task, @Nullable String title, @Nullable String message,
     Number total, boolean cancelAllowed, boolean percentProgress)

Thanks. Also, could you please show me an example on how to terminate a process in case of errors. Right now when my code runs and throws an error, the code never reaches the “done” method and thus the screen is stuck in an indefinite loop.

Thanks.

If you want to cancel background process, you can use cancel method of BackgroundTaskHandler, but it can be used only from UI thread (not from run method of a background task).

If your task throws an exception then background task will be stopped immediately and standard BackgroundWorkWindow and BackgroundWorkProgressWindow will be closed.

You can handle exceptions from a background process if you override handleException method of a BackgroundTask.


BackgroundTaskHandler<String> handler = backgroundWorker.handle(new BackgroundTask<Integer, String>(60) {
    @Override
    public String run(TaskLifeCycle<Integer> taskLifeCycle) throws Exception {
        for (int i = 0; i <= 5; i++) {
            taskLifeCycle.publish(i);

            Thread.sleep(1000);

            if (i == 2) {
                throw new RuntimeException();
            }
        }

        return "Done";
    }

    @Override
    public boolean handleException(Exception ex) {        
        // we can show errors in UI here
        showNotification("Exception occurred!", NotificationType.WARNING);
        
        return true;
    }
});
handler.execute();

thanks. What if I’m using BackgroundWorkWindow.show(), can I have a handler inside of this method?

No need to pass BackgroundTaskHandler to BackgroundWorkWindow, just pass new BackgroundTask, it will be started immediately.