Monday, May 14, 2012

How to Access SWT UI Components from Non-UI Thread

Accessing SWT components in a non-UI thread will cause an SWTException (invalid thread access) to be thrown. This example shows how an SWT application is started in a non-main thread and the main thread manipulates the SWT application by updating the text in the button and stopping the SWT application.
import org.eclipse.jface.window.ApplicationWindow;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;

public class SWTTest extends ApplicationWindow {
    private Thread uiThread;
    private Button button;
    
    public SWTTest() {
        super(null);
    }
    
    @Override
    protected Control createContents(Composite parent) {
        getShell().setText("SWTTest");
        getShell().setSize(400, 100);
        
        Composite c = new Composite(parent, SWT.NONE);
        c.setLayout(new FillLayout());
        
        button = new Button(c, SWT.NONE);
        button.setText("Hello");
        
        return parent;
    }
    
    public Display getDisplay() {
        // Calling Display.getCurrent() will most likely throw
        // a NUllPointerException if it's called from a main thread.
        // The correct way to get the Display instance is by passing the
        // UI thread
        return Display.findDisplay(uiThread);
    }
    
    public void start() {
        // We store the UI thread for the getDisplay() method
        uiThread= Thread.currentThread();
        setBlockOnOpen(true);
        open();
    }
    
    public void updateText(final String text) {
        // Any SWT operations must be done in a UI thread, so we must
        // use getDisplay().syncExec() or getDisplay().asyncExec().
        getDisplay().syncExec(new Runnable() {
            @Override
            public void run() {
                button.setText(text);
            }
        });
    }
    
    public void stop() {
        // Any SWT operations must be done in a UI thread, so we must
        // use getDisplay().syncExec() or getDisplay().asyncExec().
        getDisplay().syncExec(new Runnable() {
            @Override
            public void run() {
                close();
                getDisplay().dispose();
            }
        });
    }
    
    public static void main(String[] args) throws Exception {
        final SWTTest app = new SWTTest();
        // app.start() blocks, so we need to start it in a new thread.
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("Starting the GUI");
                app.start();
            }
        }).start();
        
        System.out.println("Sleeping for 3 secs");
        Thread.sleep(3000);
        
        System.out.println("Updating the GUI");
        app.updateText("Bye");
        
        System.out.println("Sleeping for 3 secs");
        Thread.sleep(3000);
        
        System.out.println("Stopping the GUI");
        app.stop();
    }
}

1 comment:

  1. Nice; short, clear and concise answer to an annoying problem.

    ReplyDelete