定期更新SWT会导致GUI冻结

时间:2016-10-26 14:48:09

标签: java multithreading swt

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

我希望有一个带文本字段的基于SWT的GUI,值会定期递增。

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

    线程中的异常"线程0" org.eclipse.swt.SWTException:无效   在org.eclipse.swt.SWT.error(SWT.java:4533)的线程访问   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 $ 0(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的runnable。但这导致整个事情冻结。 这是代码:

      打包什么; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; import org.eclipse.swt.SWT;

      公共类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);
      }
      

      }

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

2 个答案:

答案 0 :(得分:2)

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

在您的情况下,text.setText(i.toString());行是SWT UI操作,并且正在另一个线程中运行。

您可以使用asyncExec的{​​{1}}或syncExec方法在UI线程中运行一些代码。所以替换:

Display

text.setText(i.toString());

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

使用final String newText = i.toString(); Display.getDefault().asyncExec(() -> text.setText(newText)); 将异步进行UI更新。如果要在更新完成之前暂停线程,请使用asyncExec

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

syncExec

请注意,您还应该检查正在处理的 final String newText = i.toString(); Display.getDefault().asyncExec(new Runnable() { @Override public void run() { text.setText(newText); } }); 并停止后台主题。如果您不这样做,您将在关闭应用程序时收到错误消息。您的代码递增Shell也是错误的。这个线程有效:

i

答案 1 :(得分:0)

看看您的代码,您的UI冻结的原因是因为您正在Display.sync上执行一个可运行的内容,由于while(true)循环,该内容永远不会返回,因此,其他UI线程没有机会执行,UI冻结。您需要做的是使用Displasy.asyncRunnable,而不是使用while(true)来创建可运行程序,而不是使调度程序或另一个线程每隔x秒休眠一次,执行该可运行程序以进行所需的更新,这样其他UI线程可以运行以防止UI冻结。