定期更新 SWT 导致 GUI 冻结
Updating SWT periodically causes GUI to freeze
问题:定期更新 GUI 字段时 SWT 冻结。
我想要一个带有文本字段的基于 SWT 的 GUI,其中的值会定期递增。
- 最初我从单独的线程访问 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)
阅读 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 操作,并且 运行 在不同的线程中。
您可以使用 Display
的 asyncExec
或 syncExec
方法来 运行 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 冻结。
问题:定期更新 GUI 字段时 SWT 冻结。
我想要一个带有文本字段的基于 SWT 的 GUI,其中的值会定期递增。
- 最初我从单独的线程访问 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)
阅读 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 操作,并且 运行 在不同的线程中。
您可以使用 Display
的 asyncExec
或 syncExec
方法来 运行 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 冻结。