0

我是 Swing 的新手。我正在尝试创建一个摆动包装器以允许用户浏览和选择一个文件夹,并且该文件夹路径用作控制台 .exe 程序的命令行参数。在他们选择文件夹并单击“启动程序”按钮后,我希望摆动窗口显示一条消息,告诉他们程序正在处理(并显示时钟的动画 gif),运行外部程序,然后显示另一条消息当该程序完成执行时。我遇到的问题是“处理中”消息直到外部程序完成执行后才会显示。在下面的代码中,单击“启动程序”按钮时会执行 onLaunchProgram 方法。我试过 revalidate() 和 repaint(),但没有任何变化。


    ...

    JTextField txtFolder = new JTextField();
    JLabel lblMessage = new JLabel();
    JLabel lblPic = new JLabel();
    JButton btnLaunchApplication = new JButton("Launch Program");  

    ...  

    btnLaunchApplication.addActionListener(new ActionListener() {  
        public void actionPerformed(ActionEvent evt) {  
            onLaunchProgram(evt);  
        }  
     });  

    ...

    if (returnVal == JFileChooser.APPROVE_OPTION){
        file = fc.getSelectedFile();
        txtFolder.setText(file.getAbsolutePath());
    }

    ...

    private void onLaunchProgram(ActionEvent evt) {
        String strExecutableFilename = "MyExecutableProgam";
        String strSourceFolder = txtFolder.getText();
        String strCommand = strExecutableFilename + " " + strSourceFolder;
        lblMessage.setText("Processing");
        ImageIcon icon = new ImageIcon("clock.gif");
        lblPic.setIcon(icon);
        try {
            Process procCommand = Runtime.getRuntime().exec(strCommand);
            try {
                procCommand.waitFor();
            } catch (InterruptedException exception) {
                exception.printStackTrace();
            } finally {
            }
            lblMessage.setText("Finished");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
        }
    }
4

2 回答 2

3

从您的示例代码中很难确定您是如何执行该onLaunchProgram方法的,但根据您的描述,假设您在事件调度线程的上下文中执行它是一个安全的节拍。

事件调度线程负责(除其他外)调度重绘请求。任何阻塞该线程的东西都会阻止它更新 UI。

因为procCommand.waitFor()是一个阻塞动作,所以这将阻止任何重绘请求(或任何与此相关的事件)被处理,直到它返回。

您应该在单独的线程中执行所有耗时或阻塞的进程。但是,您遇到的问题是 UI 桅杆的所有更新都在 EDT 的上下文中执行(也就是说,您永远不应该从除 EDT 之外的任何线程更改/更新/修改/创建任何 UI 组件)

在 Swing 中,您有很多选择,在您的情况下,我建议使用SwingWorker. 它将允许您在后台线程中执行该进程,但有一些易于使用的方法来重新同步 UI 更新。

public class ProcessWorker extends SwingWorker<Integer, String> {

    private String program;
    private String sourceFolder;

    public ProcessWorker(String program, String sourceFolder) {
        this.program = program;
        this.sourceFolder = sourceFolder;
    }

    @Override
    protected void process(List<String> chunks) {
        // Back on the EDT
        for (String value : chunks) {
            if (value.equalsIgnoreCase("PROCESSING")) {
                lblMessage.setText("Processing");
                ImageIcon icon = new ImageIcon("clock.gif");
                lblPic.setIcon(icon);
            } else if (value.equalsIgnoreCase("FINISHED")) {
                lblMessage.setText("Finished");
            } else {
                // Possible some other message...
            }
        }
    }

    @Override
    protected Integer doInBackground() throws Exception {
        int result = -1;

        String strExecutableFilename = program;
        String strSourceFolder = sourceFolder;
        String strCommand = strExecutableFilename + " " + strSourceFolder;
        publish("PROCESSING");
//        lblMessage.setText("Processing");
//        ImageIcon icon = new ImageIcon("clock.gif");
//        lblPic.setIcon(icon);
        try {
            ProcessBuilder pb = new ProcessBuilder(program);
            pb.redirectError();
            pb.directory(new File(strSourceFolder));
            Process procCommand = pb.start();
//            Process procCommand = Runtime.getRuntime().exec(strCommand);
            try {
                result = procCommand.waitFor();
            } catch (InterruptedException exception) {
                exception.printStackTrace();
            } finally {
            }
//            lblMessage.setText("Finished");
            publish("FINISHED");
        } catch (IOException e) {
            e.printStackTrace();
        }

        return result;
    }
}

您还应该熟悉ProcessBuilder. 它有许多用于构建流程的有用方法,并克服了人们在尝试开始Runtime.getRuntime().exec工作时遇到的一些困难。

您应该查看http://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html了解更多详细信息

于 2013-03-13T23:24:22.837 回答
0

看来您正在一个线程中完成所有操作。

使用事件调度线程来调用你的 gui 代码。

private void onLaunchProgram(ActionEvent evt) {
        String strExecutableFilename = "MyExecutableProgam";
        String strSourceFolder = txtFolder.getText();
        String strCommand = strExecutableFilename + " " + strSourceFolder;
        ImageIcon icon = new ImageIcon("clock.gif");
        javax.swing.SwingUtilities.invokeLater(
            new Runnable() {
                 public void run() {
                     lblMessage.setText("Processing");
                     lblPic.setIcon(icon);
                 }
            });

        try {
            Process procCommand = Runtime.getRuntime().exec(strCommand);
            try {
                procCommand.waitFor();
            } catch (InterruptedException exception) {
                exception.printStackTrace();
            } finally {
            }

            javax.swing.SwingUtilities.invokeLater(
                new Runnable() {
                    public void run() {
                      lblMessage.setText("Finished");
                    }
                 });
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
        }
    }
于 2013-03-13T21:24:38.023 回答