Updating SWT objects from another thread

2020-05-23 15:31发布

问题:

In my Java application, when the main module is invoked, i start my SWT GUI in a separate thread. I need to perform some long opertations in the main thread and update the GUI thread. When I try to update the GUI thread from the main thread i.e. change a label text or something, i get a java.lang.NullPointerException. From what I've read online is because SWT doesn't allow non-UI threads to update UI objects. How can I update the GUI thread from the main thread.

I've found some examples online but they all deal with the scenario in which the GUI runs in the main thread and long operation is in separate thread. My scenario is the total opposite.

Could someone tell me how I can update widgets in the GUI thread?

回答1:

I think you are getting java.lang.NullPointerException because you are trying to access the GUI component before it is created. Ideally you should wait for the gui component to get created... for example...

I create a single GUI in a separate thread... like this

package test;


import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;

public class GUIThread implements Runnable 
{
    private Display display;
    private Label label;
    public Display getDisplay(){
        return display;
    }
    public void run() 
    {
        display = new Display();
        Shell shell = new Shell(display);
        shell.setLayout(new GridLayout());
        shell.setLayoutData(new GridData(SWT.FILL,SWT.FILL,true,false));
        label = new Label(shell,SWT.NONE);
        label.setText(" -- ");
        shell.open();
        shell.pack();

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

    public synchronized void update(final int value)
    {
        if (display == null || display.isDisposed()) 
            return;
        display.asyncExec(new Runnable() {

            public void run() {
                label.setText(""+value);
            }
        });

    }

}

And in my main method i do something like this....

package test;

import org.eclipse.swt.widgets.Display;

public class Main 
{

    public static void main(String[] args) throws Exception
    {
        final GUIThread gui = new GUIThread();
        Thread t = new Thread(gui);
        t.start();

        Thread.sleep(3000); // POINT OF FOCUS
        Display d = gui.getDisplay();

        for(int i = 0; i<100; i++)
        {           
            System.out.println(i + "  " + d);
            gui.update(i);  
            Thread.sleep(500);
        }
    }
}

Now if we comment out the POINT OF FOCUS in the above code then I will always get NullPointerException... But a delay of 3 seconds gives my GUI thread enough time to be in ready state and hence it doesn't through NullPointerException.....

In scenario like this you have to efficiently use the wait and yield methods... otherwise it would result in "Hard to find Bugs"... i.e. wait for UI to properly instantiate and then yield...

Also the actual processing is done in main thread and GUI is running in separate thread... to communicate properly it is good to have some shared and synchronized data structure... or it could be done using socket communication... your main thread populating some port and your GUI thread asynchronously listening on that port....

Hope this will through some light on your problem....



回答2:

To say things short, SWT is a single-threaded UI toolkit. As a consequence, widgets must be updated in SWT event thread, like in Swing. Thus, you'll have to call the refresh using anonymous Runnable classes :

Display.getDefault().asyncExec(new Runnable() {
    public void run() {
        someSwtLabel.setText("Complete!");
    }
});

For a longer explanation, this JavaLobby article is a good introduction to this threading usage model.



标签: java swt