Daniel S
Daniel S

Reputation: 609

JAVA - how to modify SWT UI while looping in thread

I'm trying to implement a Desktop app that loops trough a function and sets a textfield on the UI every 1 sec.

But I either get

org.eclipse.swt.SWTException: Invalid thread access

when I don't use the display

or the UI is really sluggish when I do

display.asyncExec(new Runnable() {

My code looks like this:

public void open() {
    Display display = Display.getDefault();
    shell = new Shell();
    shell.setSize(486, 322);
    shell.setText("SWT Application");

    Button btnStartLoop = new Button(shell, SWT.NONE);
    btnStartLoop.addMouseListener(new MouseAdapter() {
        @Override
        public void mouseDown(MouseEvent e) {
            SwingUtilities.invokeLater(new Runnable() {
                public void run() // updates displayArea
                {
                    while (true) {
                        try {
                            text.setText("Text has been set");
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                }
            });
        }
    });
    btnStartLoop.setBounds(35, 30, 75, 25);
    btnStartLoop.setText("Start Loop");

    text = new Text(shell, SWT.BORDER);
    text.setText("0");
    text.setBounds(116, 32, 177, 21);
    shell.open();
    shell.layout();
    while (!shell.isDisposed()) {
        if (!display.readAndDispatch()) {
            display.sleep();
        }
    }
}

is there any way this can be overcome?

Upvotes: 0

Views: 315

Answers (1)

greg-449
greg-449

Reputation: 111142

You must never sleep in the UI thread. You must use a new Thread for the background activity. You call Display.asyncExec from within the thread to run the UI update code in the UI thread.

btnStartLoop.addMouseListener(new MouseAdapter() {
    @Override
    public void mouseDown(final MouseEvent e) {
        final Thread background = new Thread(new Runnable() {
            public void run()
            {
                while (true) {
                    try {
                        Display.getDefault().asyncExec(() -> text.setText("Text has been set"));
                        Thread.sleep(1000);
                    } catch (final InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        });
        background.start();
    }
});

Note: SwingUtilities is for Swing applications, do not use it in SWT apps.

You can also use the timerExec method of Display to run code after a specific delay which avoids the need for a background thread.

btnStartLoop.addMouseListener(new MouseAdapter() {
    @Override
    public void mouseDown(final MouseEvent e) {
        final Runnable update = new Runnable() {
          public void run()
          {
            text.setText("Text has been set");

            Display.getDefault().timerExec(1000, this);
          }
        };
        Display.getDefault().timerExec(0, update);
    }
});

Upvotes: 2

Related Questions