定期更新 SWT 导致 GUI 冻结

Updating SWT periodically causes GUI to freeze

问题:定期更新 GUI 字段时 SWT 冻结。

我想要一个带有文本字段的基于 SWT 的 GUI,其中的值会定期递增。

  1. 最初我从单独的线程访问 textField 导致抛出异常:

Exception in thread "Thread-0" org.eclipse.swt.SWTException: Invalid thread access at org.eclipse.swt.SWT.error(SWT.java:4533) at org.eclipse.swt.SWT.error(SWT.java:4448) at org.eclipse.swt.SWT.error(SWT.java:4419) at org.eclipse.swt.widgets.Widget.error(Widget.java:482) at org.eclipse.swt.widgets.Widget.checkWidget(Widget.java:373) at org.eclipse.swt.widgets.Text.setText(Text.java:2311) at regreon.Incrementing.lambda[=14=](Incrementing.java:62) at java.lang.Thread.run(Thread.java:745)

  1. 阅读 SWT 文档后(感谢 @marko-topolnik)- 我尝试将 display.SyncExec(Runnable r)display.AsyncExec(Runnable r) 与在循环中调用 Thread.sleep 的可运行对象一起使用。但这导致 整个过程冻结。 这是代码:

    随便打包; 导入 org.eclipse.swt.widgets.Display; 导入 org.eclipse.swt.widgets.Shell; 导入 org.eclipse.swt.widgets.Text; 导入 org.eclipse.swt.SWT;

    public class FreezingGUI {

    protected Shell shell;
    private Text text;
    
    public static void main(String[] args) {
        try {
            FreezingGUI window = new FreezingGUI();
            window.open();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public void open() {
        Display display = Display.getDefault();
        createContents();
        shell.open();
        shell.layout();
    
        // HOW TO DO THAT???
        display.syncExec(() -> {
            while (true) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    Integer i = Integer.parseInt(text.getText()) + 1;
                    text.setText(i.toString());
                }
            }
        }
        );
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
    }
    
    protected void createContents() {
        shell = new Shell();
        shell.setSize(450, 300);
        shell.setText("SWT Application");
    
        text = new Text(shell, SWT.BORDER);
        text.setEditable(false);
        text.setText("0");
        text.setBounds(30, 32, 78, 26);
    }
    

    }

如何避免冻结和抛出异常?

任何更改 UI 对象的 SWT 操作必须在 SWT 用户界面线程上 运行。

在您的例子中,text.setText(i.toString()); 行是一个 SWT UI 操作,并且 运行 在不同的线程中。

您可以使用 DisplayasyncExecsyncExec 方法来 运行 UI 线程中的某些代码。所以替换:

text.setText(i.toString());

final String newText = i.toString();
Display.getDefault().asyncExec(() -> text.setText(newText));

(假设您使用的是 Java 8)。

使用 asyncExec 将异步执行 UI 更新。如果您想暂停线程直到更新完成,请改用 syncExec

如果您使用的是 Java 7 或更早版本,请使用:

 final String newText = i.toString();
 Display.getDefault().asyncExec(new Runnable() {
    @Override
    public void run() {
      text.setText(newText);
    }
 });

请注意,您还应该检查正在处理的 Shell 并停止您的后台线程。如果您不这样做,您将在关闭应用程序时收到错误消息。您的代码递增 i 也是错误的。此线程有效:

new Thread(() -> {
    for (int i = 1; true; i++) {
        try {
            Thread.sleep(1000);
        } catch (final InterruptedException e) {
            return;
        }

        if (shell.isDisposed())  // Stop thread when shell is closed
          break;

        final String newText = Integer.toString(i);
        Display.getDefault().asyncExec(() -> text.setText(newText));
    }
}).start();

查看您的代码,您的 UI 冻结的原因是因为您正在 Display.sync 上执行一个 运行nable,而由于 while(true ) 循环,因此,其他 UI 线程没有机会执行并且 UI 冻结。您需要做的是使用 Displasy.asyncRunnable 而不是使用 while(true) 使 运行nable 成为一个调度程序或另一个每 x 秒休眠一次的线程,该线程执行 运行nable 使您想要的更新,这样其他 UI 线程可以 运行 防止您的 UI 冻结。