我今天正在编程GUI,按下按钮时会进行更长时间的计算。在计算运行时,我想使用仍在运行的计算的中间结果并将它们写入JLabel。然而,GUI在计算完成之前不应由用户操作。 起初我做的是这样的事情:
(1)
public class GUI extends JFrame {
JLabel label = new JLabel("Status: ");
public GUI(){...}
public void calculate() {
for(int i = 0; i < 10; i++) {
String one = calculationPartOne(i);
label.setText("Status: " + one);
label.repaint(); //*
calculationPartTwo(i);
}
}
}
这不起作用,JLabel只会在计算结束后更新。我还尝试了.repaint()和.validate()注释行*中涉及的所有组件,但它没有做任何事情。 所以,在尝试和搜索谷歌/ StackoOverflow一整天后,我终于有了一个有效的解决方案,但我仍然不明白为什么上面不起作用。我希望GUI能够阻止,所以我自然而然地在同一个线程中运行了计算。但是,调用任何方法重新绘制GUI-计算之间(在GUI更新时停止计算)不起作用,我不明白为什么。有人可以解释一下吗?
最后,我使用SwingWorker类进行计算,并使用它的函数在计算时更新JLabel。但是,由于我需要阻止GUI,我现在在排除SwingWorker之前禁用所有组件,并在完成计算后让SwingWorker重新启用所有组件。 所以,我使用SwingWorker,不阻止EDT,但是然后&#34;假&#34;通过禁用一切来阻止EDT?这对我来说似乎很悖论。
以下是我现在所拥有的概要:
public class GUI extends JFrame {
JLabel label = new JLabel("Status: ");
//I didn't use a list, but it works to illustrate it here
List<Component> GUIComponents = ...;
public GUI() {...}
public void calculate() {
SwingWorker<Void, String> worker = new SwingWorker<Void, String>() {
protected Void doInBackground() throws Exception {
for(int i = 0; i < 10; i++) {
String one = calculationPartOne(i);
publish(one);
calculationPartTwo(i); //**
}
}
protected void done() {
setEnableGUI(true);
}
protected void process(List<String> chunk) {
label.setText(chunk.get(chunk.size() - 1));
}
};
setEnableGUI(false);
worker.execute();
}
public void setEnableGUI(boolean e) {
for(Component c : GUIComponents) {
c.setEnabled(e);
}
}
//**
public void calculationPartTwo() {...}
}
这很有效。
我希望有人能澄清一下。这个解决方案感觉不对。
答案 0 :(得分:1)
为什么错? gui线程仅用于响应用户事件 - 因此您应该在后台执行繁重的工作 - 这就是您使用SwingWorker所做的事情。
另外,防止用户更改组件的最佳方法是执行此操作 - 在开始重组之前禁用组件,并在完成后启用。
你可能只想考虑在模态对话框中显示计算结果 - 一个JDialog将弹出父窗口并阻止它。您可以在此对话框中显示中间结果和进度,然后在完成计算后,对话框将关闭并取消阻止由其遮挡的UI。这样可以省去你必须在循环中单独禁用所有gui组件,并且还可以选择让“取消”按钮立即停止工作。
答案 1 :(得分:1)
然而,调用任何方法重新绘制GUI-计算之间(在GUI更新时停止计算)不起作用,我不明白为什么。有人可以解释一下吗?
repaint()请求由RepaintManager处理,不会立即完成。 RepaintManager基本上安排重绘。由于重新绘制是在EDT上完成的,所以在EDT空闲之前无法完成。
所以,我使用SwingWorker,不阻止EDT,但是“假”通过禁用所有内容来阻止EDT?这对我来说似乎很悖论。
您始终可以使用不确定的JProgressBar。请参阅How to Use Progress Bars。
或者您可能更愿意使用Disabled Glass Pane方法。
在某些情况下,您可以使用:
label.paintImmediately(...);
强制重新绘制组件。但是你仍然有禁用GUI的问题,所以它可能不是你真正应该使用的解决方案。