简体   繁体   English

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

[英]Updating Swing components while blocking the GUI

I was programming a GUI today, which is doing longer calculations when pressing a button. 我今天正在编程一个GUI,按下按钮时它会进行更长的计算。 While the calculations are running, I wanted to use intermediate results of the still running calculation and write them to a JLabel. 在运行计算时,我想使用仍在运行的计算的中间结果并将其写入JLabel。 The GUI however, should not be operable by the user before the calculation has finished. 但是,在计算完成之前,用户不应操作该GUI。 At first I was doing something like this: 起初我在做这样的事情:

(1) (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);
        }
    }
}

This did not work, the JLabel would only update after the calculation has finished. 这不起作用,JLabel仅在计算完成后才更新。 I also tried to .repaint() and .validate() all components involved at the line commented *, but it did nothing. 我还尝试对行中涉及的所有组件进行.repaint()和.validate()注释*,但未执行任何操作。 So, after trying and searching Google/StackoOverflow the whole day I finally have a working solution, but I still do not understand why above does not work. 因此,在一整天尝试搜索Google / StackoOverflow之后,我终于有了一个可行的解决方案,但是我仍然不明白为什么上述方法不起作用。 I wanted the GUI to block, so naturally I ran the calculation in the same thread. 我想阻止GUI,所以自然而然地在同一线程中运行计算。 However, calling any methods to repaint the GUI -inbetween- the calculation (making the calculation stop while the GUI is updated) did not work, and I do not understand why. 但是,在计算之间调用任何方法重新绘制GUI的方法(在GUI更新时停止计算)是行不通的,而且我不明白为什么。 Can someone explain? 有人可以解释吗?

In the end, I used the SwingWorker class to do my calculations, and use it's functions to update the JLabel while calculating. 最后,我使用SwingWorker类进行计算,并使用其功能在计算时更新JLabel。 However, as I need the GUI to block, I now disable -all- the components before excuting the SwingWorker and have the SwingWorker re-enable all the components after finishing the calculation. 但是,由于我需要GUI进行阻止,因此现在在执行SwingWorker之前禁用所有组件,并在完成计算后让SwingWorker重新启用所有组件。 So, I use SwingWorker, to not block the EDT, but then "fake" to block the EDT by disabling everything? 因此,我使用SwingWorker来不阻止EDT,而是通过禁用所有功能来“假”阻止EDT? This seems really paradox to me. 在我看来,这真的很矛盾。

Here is an outline of what I have now: 这是我现在所拥有的概述:

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() {...}
}

This works. 这可行。

I hope someone can clarify. 我希望有人可以澄清。 This solutions feels wrong. 这种解决方案感觉不对。

why wrong? 为什么错了? the gui thread is for responding to user events only - so you should be doing your heavy lifting in the background - which is what youre doing with a SwingWorker. gui线程仅用于响应用户事件-因此您应该在后台进行繁重的工作-这是您对SwingWorker所做的事情。

also, the best way to prevent a user from changing a componenet is to do exactly that - disable the component before starting the heavu lifting, and enable once its done. 同样,阻止用户更改组件网络的最好方法就是精确地做到这一点-在开始举重之前禁用组件,并在完成后启用。

only thing you might want to consider is displaying the results of your calculation in a modal dialog - a JDialog that will pop above the parent window and block it. 您可能要考虑的唯一事情就是在模态对话框中显示计算结果-一个JDialog,它将弹出父窗口上方并将其阻止。 you could display the intermediate results and progress in this dialog and then once the calculation is done the dialog will close and unblock the UI obscured by it. 您可以在此对话框中显示中间结果和进度,然后一旦完成计算,该对话框将关闭并取消阻止被其遮盖的UI。 this will save you fron having to disable all gui components indiviually in a loop and will also give you an option to have a "cancel" button to halt the work immediately. 这将使您不必重新循环地禁用所有gui组件,并且还可以选择使用“取消”按钮立即停止工作。

However, calling any methods to repaint the GUI -inbetween- the calculation (making the calculation stop while the GUI is updated) did not work, and I do not understand why. 但是,在计算之间调用任何方法重新绘制GUI的方法(在GUI更新时停止计算)是行不通的,而且我不明白为什么。 Can someone explain? 有人可以解释吗?

repaint() requests are handled by the RepaintManager and are not done immediately. repaint()请求由RepaintManager处理,不会立即完成。 The RepaintManager basically schedules the repaint. RepaintManager基本上计划了重新绘制。 Since repainting is done on the EDT, it can't be done until the EDT is free. 由于重新粉刷是在EDT上完成的,因此只有在EDT释放后才能进行。

So, I use SwingWorker, to not block the EDT, but then "fake" to block the EDT by disabling everything? 因此,我使用SwingWorker来不阻止EDT,而是通过禁用所有功能来“假”阻止EDT? This seems really paradox to me. 在我看来,这真的很矛盾。

You can always use an indeterminated JProgressBar. 您始终可以使用不确定的JProgressBar。 See How to Use Progress Bars . 请参阅如何使用进度条

Or maybe you would prefer to use the Disabled Glass Pane approach. 或者,您可能更喜欢使用“ 禁用玻璃窗格”方法。

In some cases you can use: 在某些情况下,您可以使用:

label.paintImmediately(...);

to force the repainting of a component. 强制重涂零部件。 But you still have the issue of disabling the GUI so its probably not a solution you should really be using. 但是您仍然存在禁用GUI的问题,因此它可能不是您真正应该使用的解决方案。

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

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