【问题标题】:SwingWorker done() method seemingly not called? SwingWork not cancelled or blocked either though, Not using publish/process methodsSwingWorker done() 方法貌似没有被调用? SwingWork 也没有取消或阻止,不使用发布/处理方法
【发布时间】:2014-10-11 21:17:20
【问题描述】:

我的问题: 为什么是我的 SwingWorker,它似乎只是在它的线程中执行我的工具,然后从 doInBackground() 方法中退出就好了(不使用cancel() SwingWorker 方法),不调用 done() 方法,所以当工具在 doInBackground() 中完成时,我的进度条和标签可能会被清理? p>

我对 SwingWorkers 还很陌生,但我很快就学会了它们,当需要运行线程进程并仍然更新 GUI 时,它们很有意义。当我需要向类似向导的 GUI 添加进度条时,我最终找到了 SwingWork 类。 GUI 从用户那里收集一些数据,然后运行一个我已经测试过的工具,并根据这些数据处理一个文件。

我有调用我的工具的基本 SwingWorker(从它自己的线程调用):

private class DeIDTask extends SwingWorker<Void, Void> {

    private DeIdentifier deidentifier;
    boolean processing = true;

    DeIDTask(DeIdentifier deid) {
        this.deidentifier = deid;
    }

    /*
     * Main task. Executed in background thread.
     */
    @Override
    protected Void doInBackground() throws Exception {
        // Initialize Progress
        setProgress(0);
        //Sleep for at least one second to simulate "startup".
        try {
            Thread.sleep(1000 + new Random().nextInt(2000));
        } catch (InterruptedException ignore) {}
        // Do Processing
        Thread procThread = new Thread(new DeIdentifierProcessThread(deidentifier));
        procThread.start();
        while(procThread.isAlive()) {
            try {
                // try to join every half a second
                procThread.join(500);
                setProgress(deidentifier.getProcessProgress());
            } catch (InterruptedException e1) {
                e1.printStackTrace();
            }               
        }

        processing = false;

        // Do Output Flushing
        Thread outputThread = new Thread(new DeIdentifierOutputThread(deidentifier));
        outputThread.start();
        while(outputThread.isAlive()) {
            try {
                // try to join every quarter second
                procThread.join(250);
                setProgress(deidentifier.getOutputProgress());
            } catch (InterruptedException e1) {
                e1.printStackTrace();
            } 
        }

        return null;
    }

    /*
     * Executed in event dispatching thread
     */
    @Override
    protected void done() {
        lblProgress_deid.setText("Done.");
        progressBar_deid.setStringPainted(false);
        progressBar_deid.setIndeterminate(false);
        button_65.setEnabled(true);
    }
}

请注意,此 SwingWorker 类仅实现 doInBackground() 和 done() 方法。我不以任何方式调用 cancel(),我知道它能够在 doInBackground()之前执行 done() 方法> 方法完成了。

我的想法是我的 SwingWork 可能没有正确执行,因此我的 done() 方法找不到事件调度线程。我找到了这样的答案here,但回答者回答问题的方式对于我更复杂的 GUI 来说真的很简单,并且没有真正解释如何在 main 方法中创建 JFrame 使得 SwingWork 明确成为 Swing 应用程序连接到。在我看来,您不需要做一些特别的事情来将 SwingWorker 连接到 Swing 组件。

我创建了一个正常工作的 GUI,我的 GUI 调用 SwingWorker 的地方来自一个 JButton:

btnStartDeidentification = new JButton("Start De-Identification");
    btnStartDeidentification.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            // Disable clicking of the button
            progressBar_deid.setIndeterminate(true);
            btnStartDeidentification.setEnabled(false);

            task_deid = new DeIDTask(deidentifier);
            task_deid.addPropertyChangeListener(new PropertyChangeListener() {

                @Override
                public void propertyChange(PropertyChangeEvent evt) {
                    int progress = task_deid.getProgress();
                    System.out.println("Progress is: " + progress);
                    progressBar_deid.setIndeterminate(false); 
                    progressBar_deid.setString(null);
                    progressBar_deid.setValue(progress);
                    progressBar_deid.setStringPainted(true);
                    lblProgress_deid.setText("Processing Records (Current/Total): " + deidentifier.getCurrentLineNumber() + "/" + deidentifier.getNumberOfLinesInFile());

                }

            });
            task_deid.execute();
        }
    });

    btnStartDeidentification.setBounds(107, 195, 200, 29);
    panel_runDeIDTool_23.add(btnStartDeidentification);

我的下一个想法是,可能是因为我实际上是从 inside 我的 actionPerformed() 方法初始化并执行了 SwingWork task_deid done() 方法由于某种原因找不到 EDT。但是这个问题here on SO,与我自己的问题非常相似,有一个从 changeListener() 内部调用 SwingWorker 的示例。回答者说 changeListener() 是 EDT 的一部分。我还没有在 GUI 中使用 changeListener,但我认为这意味着我可以从 actionPerformed 方法内部创建和调用 task_deid。

现在,这些代码片段位于我使用 Eclipse Window Builder 工具创建的一个名为 GUI 的整个类中。该课程的要点是:

public class GUI {
private JFrame frmToolWizard;
private JPanel panel_Steps;
...
private JPanel panel_runDeIDTool_23;

...
private JButton btnStartDeidentification;
private JProgressBar progressBar_deid;
private JLabel lblProgress_deid;
...
private DeIDTask task_deid;

/**
 * Launch the application.
 */
public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
        public void run() {
            try {
                GUI window = new GUI();
                window.frmToolWizard.setVisible(true);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    });
}

/**
 * Create the application.
 */
public GUI() {
    initialize();
}

/**
 * Initialize the contents of the frame.
 */
private void initialize() {
    frmToolWizard = new JFrame();
    ...
    panel_Steps = new JPanel();
    frmToolWizard.getContentPane().add(panel_Steps);
    ...
    panel_runDeIDTool_23 = new JPanel();
    panel_Steps.add(panel_runDeIDTool_23, "name_1406678727369229000");
    ...
    // Lot of other components here
    ...
    btnStartDeidentification = new JButton("Start De-Identification");
    ...
}
private class DeIDTask extends SwingWorker<Void, Void> {
...
}

}

调用 done() 方法的全部意义在于我想完成进度条,将其设为空白,并将进度条下的标签设置为“完成”。所以最终用户知道他们可以继续前进。我想做的这一切都在 DeIDTask done() 方法中非常清楚地看到了。

非常感谢任何帮助。如果这是一个明显的答案,我提前道歉,因为我没有看到我需要做些什么来解决我的问题。

【问题讨论】:

    标签: java multithreading user-interface swingworker windowbuilder


    【解决方案1】:

    isDone() 在doInBackground() 之后被调用,但您在isDone() 完成后收到一个属性更改事件,并且由于您的侦听器覆盖了您在isDone() 中修改的几乎所有 UI 属性,因此您得出了错误的结论。

    PropertyChangeEvents 发送给不同的属性,尤其是progress 和state。在调用isDone 之后收到的最后一个事件是告诉您SwingWorker 的state 属性从STARTED 变为DONE。

    您可以检查PropertyChangeEvent 的property name 以区分progress 和state 的变化。

    【讨论】:

    • 抱歉,我在粘贴时愚蠢地忽略了起始行代码。当线程之前运行时,我在阅读您的答案时感到困惑!据我所知,doInBackground() 方法肯定已经完成了。我为这个愚蠢的错误道歉,请不要让它阻止你。
    • 另外,我将工具放在另一个线程中,这样我就可以在线程尝试加入时更新进度,正如您在上面粘贴的代码中看到的那样,如果我只是调用了那个方法线程调用,doInBackground() 将简单地在该方法上运行,而不会停止更新 TaskDeID 进度。
    • 还有大括号不匹配的问题。不要为这个问题拼凑一些东西,发布一个能够重现问题的代码!如果你复制这样一个工作的例子,任意行是不可能消失的。
    • 抱歉,我试图缩短整个工作代码,事后看来这并没有帮助。整个方法已全部重新发布。您会注意到现在在 doInBackground() 方法中调用并运行了另一个线程。感谢您的耐心等待。
    • 剩下一个问题:您是否真的验证了isDone() 是否未被调用,或者您只是从视觉结果中假设?我猜您在调用isDone() 之后收到一个属性更改事件,并且由于您的侦听器覆盖了您在isDone() 中修改的几乎所有UI 属性,因此您得出了错误的结论。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-07-02
    • 2011-09-06
    • 2015-02-07
    • 2012-03-13
    • 2014-06-02
    • 2011-08-28
    相关资源
    最近更新 更多