繁体   English   中英

在阻止GUI的同时更新Swing组件

[英]Updating Swing components while blocking the GUI

我今天正在编程一个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组件,并且还可以选择使用“取消”按钮立即停止工作。

但是,在计算之间调用任何方法重新绘制GUI的方法(在GUI更新时停止计算)是行不通的,而且我不明白为什么。 有人可以解释吗?

repaint()请求由RepaintManager处理,不会立即完成。 RepaintManager基本上计划了重新绘制。 由于重新粉刷是在EDT上完成的,因此只有在EDT释放后才能进行。

因此,我使用SwingWorker来不阻止EDT,而是通过禁用所有功能来“假”阻止EDT? 在我看来,这真的很矛盾。

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

或者,您可能更喜欢使用“ 禁用玻璃窗格”方法。

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

label.paintImmediately(...);

强制重涂零部件。 但是您仍然存在禁用GUI的问题,因此它可能不是您真正应该使用的解决方案。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM