1

我今天正在编写一个 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 -inbetween-计算(在 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() {...}
}

这行得通。

我希望有人能澄清一下。这个解决方案感觉不对。

4

2 回答 2

1

为什么错了?gui 线程仅用于响应用户事件 - 因此您应该在后台进行繁重的工作 - 这就是您使用 SwingWorker 所做的事情。

此外,防止用户更改组件的最佳方法就是做到这一点 - 在开始举重之前禁用组件,并在完成后启用。

您可能要考虑的唯一一件事是在模式对话框中显示计算结果 - 一个 JDialog 将弹出父窗口上方并阻止它。您可以在此对话框中显示中间结果和进度,然后一旦计算完成,对话框将关闭并解锁被它遮挡的 UI。这将使您不必在循环中单独禁用所有 gui 组件,并且还可以让您选择“取消”按钮来立即停止工作。

于 2013-04-09T16:28:48.247 回答
1

但是,调用任何方法来重新绘制 GUI -inbetween-计算(在 GUI 更新时停止计算)不起作用,我不明白为什么。有人可以解释吗?

repaint() 请求由 RepaintManager 处理,不会立即完成。RepaintManager 基本上会安排重绘。由于重绘是在 EDT 上完成的,因此在 EDT 空闲之前无法完成。

所以,我使用 SwingWorker 来不阻止 EDT,然后“假”通过禁用一切来阻止 EDT?这对我来说似乎真的很矛盾。

您始终可以使用不确定的 JProgressBar。请参阅如何使用进度条

或者,您可能更喜欢使用Disabled Glass Pane方法。

在某些情况下,您可以使用:

label.paintImmediately(...);

强制重新绘制组件。但是您仍然遇到禁用 GUI 的问题,因此它可能不是您真正应该使用的解决方案。

于 2013-04-09T16:35:12.407 回答