我今天正在编写一个 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() 注释 * 行中涉及的所有组件,但它什么也没做。 所以,在尝试和搜索 Google/StackoOverflow 一整天之后,我终于有了一个可行的解决方案,但我仍然不明白为什么上面不起作用。我希望 GUI 能够阻塞,所以我很自然地在同一个线程中运行计算。但是,调用任何方法在计算之间重新绘制 GUI(使计算在 GUI 更新时停止)不起作用,我不明白为什么。有人可以解释一下吗?
最后,我使用了 SwingWorker 类来进行计算,并在计算时使用它的函数来更新 JLabel。但是,由于我需要 GUI 进行阻止,因此我现在在执行 SwingWorker 之前禁用所有组件,并在完成计算后让 SwingWorker 重新启用所有组件。 那么,我使用 SwingWorker 不阻止 EDT,然后“假”通过禁用所有内容来阻止 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() {...}
}
这有效。
希望有人能澄清一下。这个解决方案感觉不对。
最佳答案
为什么错了? gui 线程仅用于响应用户事件 - 因此您应该在后台完成繁重的工作 - 这就是您使用 SwingWorker 所做的事情。
此外,防止用户更改组件的最佳方法就是这样做 - 在开始重大提升之前禁用该组件,并在完成后启用。
您可能需要考虑的唯一一件事是在模式对话框中显示计算结果 - 一个将在父窗口上方弹出并阻止它的 JDialog。您可以在此对话框中显示中间结果和进度,然后一旦计算完成,该对话框将关闭并解锁被它遮挡的 UI。这将使您不必在循环中单独禁用所有 GUI 组件,并且还可以选择使用“取消”按钮来立即停止工作。
关于java - 更新 Swing 组件,同时阻止 GUI,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15907330/