casgage
casgage

Reputation: 529

In a SWT program, how to start a thread which is able to update the UI without blocking

I have a working Java application (about 10k loc) which started as a command line program. A few months ago I "enhanced" it to be a very simple unthreaded SWT application, which added very little but was a step towards what I am trying to do now.

I am trying to make the invocation of the worker code to be run in a thread. I have read a good many recommendations on the internet, and have come up with a prototype:

package myapp;

import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;

public class Console {
    Display display;
    Shell shell;
    Text text;

    private void process() {
        display = new Display();
        shell = new Shell(display);
        shell.setLayout(new FillLayout());
        shell.setLocation(new Point(80, 80));

        text = new Text(shell, SWT.MULTI | SWT.V_SCROLL | SWT.READ_ONLY);

        shell.open();

        Display.getDefault().asyncExec(new Runnable() {
            public void run() {
                new Worker(text).work();
            }
        });

        while (!shell.isDisposed()) {
            if (!display.readAndDispatch())
                display.sleep();
        }

        display.dispose();
    }

    class Worker {
        Text text;

        public Worker(Text t) {
            text = t;
        };

        void work() {
            for (int i = 0; i < 30; i++) {
                text.append("hello " + i + "\n");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) {
        new Console().process();
    }
}

The Worker class represents the bulk of my application,

When I run this, the output lines start appearing every second, as expected, but when I try to interact with the window in any way (eg to move it), at first the window is unresponsive, but then the output stops displaying, the window is movable, and nothing further appears in the window until the end of processing, when everything remaining is displayed all at once, and the end result LOOKS correct.

If I leave the window untouched, it displays correctly.

What am I doing wrong?

Upvotes: 1

Views: 4738

Answers (3)

sambi reddy
sambi reddy

Reputation: 3085

You are blocking UI by calling sleep() on UI Thread.

Use below method on Display if you want to update UI periodically

org.eclipse.swt.widgets.Display.timerExec(int, Runnable)

Upvotes: 1

Domi
Domi

Reputation: 31

You need to start your worker in its own thread. As soon you get a result you can insert it into your gui by calling display.asyncExec from the worker-thread.

there is a snippet doing something similar here: Snippet7

In this Snippet they are doing it by syncExec, which blocks your worker until the display-thread executed the runnable. This can be easier to synchronize. Or see here

Upvotes: 3

SnakeDoc
SnakeDoc

Reputation: 14361

This looks to be the trouble spot. Worker sleeps the thread.

Display.getDefault().asyncExec(new Runnable() {
        public void run() {
            new Worker(text).work();
        }
    });

It appears to me that you are creating a new Worker which looks like it calls Thread.sleep() which will Block the thread. In this case, it's your GUI thread since it's being executed from inside it!

Fix this by removing that offending Thread.sleep(). I try to steer clear of Thread.sleep() in production code.

To keep your timing, you can try moving the asyncExec into your Worker (untested)

class Worker {
    Text text;

    public Worker(Text t) {
        text = t;
    };

    void work() {
        for (int i = 0; i < 30; i++) {
            Display.getDefault().asyncExec(new Runnable() {
                public void run() {
                    text.append("hello " + i + "\n");
                }
            });
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

Upvotes: 1

Related Questions