Обновление SWT-объектов из другого потока

В моем приложении Java, когда вызывается основной модуль, я запускаю свой SWT-интерфейс в отдельном потоке. Мне нужно выполнить некоторые длинные операции в основном потоке и обновить поток графического интерфейса. Когда я пытаюсь обновить поток GUI из основного потока, то есть изменить текст ярлыка или что-то еще, я получаю java.lang.NullPointerException. Из того, что я читал в Интернете, это потому, что SWT не разрешает потокам не-UI обновлять объекты пользовательского интерфейса. Как обновить поток GUI из основного потока.

Я нашел несколько примеров в Интернете, но все они касаются сценария, в котором графический интерфейс работает в основном потоке, а длительная операция - в отдельном потоке. Мой сценарий - полная противоположность.

Может ли кто-нибудь сказать мне, как я могу обновлять виджеты в потоке графического интерфейса?

Ответы

Ответ 1

Я думаю, что вы получаете java.lang.NullPointerException, потому что пытаетесь получить доступ к компоненту GUI перед его созданием. В идеале вам нужно подождать, пока компонент gui будет создан... например...

Я создаю один графический интерфейс в отдельном потоке... вроде этого

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);
            }
        });

    }

}

И в моем основном методе я делаю что-то вроде этого....

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);
        }
    }
}

Теперь, если мы прокомментируем POINT OF FOCUS в приведенном выше коде, я всегда получаю NullPointerException... Но задержка в 3 секунды дает моему потоку графического интерфейса достаточно времени, чтобы быть в состоянии готовности, и, следовательно, он не через NullPointerException.....

В сценарии, подобном этому, вам необходимо эффективно использовать методы wait и yield... в противном случае это приведет к "трудно найти ошибки"... т.е. подождать, пока пользовательский интерфейс будет правильно создавать экземпляр, а затем вывести...

Также фактическая обработка выполняется в основном потоке, а графический интерфейс работает в отдельном потоке... для правильной связи, хорошо иметь некоторую общую и синхронизированную структуру данных... или это можно сделать, используя связь сокетов... ваш основной поток, заполняющий некоторые port, и ваш поток GUI asynchronously, прослушивающий этот порт....

Надеюсь, что это прояснит вашу проблему....

Ответ 2

Говоря кратко, SWT представляет собой однопоточный набор инструментов пользовательского интерфейса. Как следствие, виджеты должны обновляться в потоке событий SWT, как в Swing. Таким образом, вам нужно вызвать обновление с помощью анонимных классов Runnable:

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

Для более подробного объяснения эта статья JavaLobby - хорошее введение в эту модель использования потоков.