简体   繁体   English

只有在Runtime.getRuntime()。exec()完成执行之后,Swing消息才会显示

[英]Swing message doesn't get displayed until after Runtime.getRuntime().exec() finishes execution

I am new to Swing. 我是Swing的新手。 I am trying to create a swing wrapper to allow the user to browse and select a folder, and that folder path is used as a command line parameter to a console .exe program. 我试图创建一个摆动包装器,以允许用户浏览和选择文件夹,并且该文件夹路径用作控制台.exe程序的命令行参数。 After they select the folder and click a "Launch Program" button, I want the swing window to display a message telling them that the program is processing (and display an animated gif of a clock), run the external program, then display another message when that program has finished execution. 他们选择文件夹并单击“启动程序”按钮后,我希望旋转窗口显示一条消息,告诉他们该程序正在处理(并显示时钟的gif动画),运行外部程序,然后显示另一条消息该程序完成执行时。 The problem I'm having is that the "Processing" message doesn't get displayed until after the external program finishes execution. 我遇到的问题是,直到外部程序完成执行后,才会显示“正在处理”消息。 In the code below, the onLaunchProgram method gets executed when the "Launch Program" button is clicked. 在下面的代码中,单击“启动程序”按钮时,将执行onLaunchProgram方法。 I've tried revalidate() and repaint(), but there was no change. 我尝试过revalidate()和repaint(),但没有任何变化。 I have a waitFor() for the "Finished" message, but even when I take that out, the "Processing" message and gif don't get displayed until after the external program finishes execution. 我有一个“已完成”消息的waitFor(),但是即使取出该消息,也不会显示“正在处理”消息和gif,直到外部程序完成执行。


    ...

    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 {
        }
    }

It's difficult from your sample code to determine how you are executing the onLaunchProgram method, but from your description, it would be a safe beat to assume you are executing it within the context of the Event Dispatching Thread. 从示例代码很难确定如何执行onLaunchProgram方法,但是从您的描述来看,假设您是在事件调度线程的上下文中执行它是一个onLaunchProgram选择。

The Event Dispatching Thread is responsible for (amongst other things) dispatching repaint requests. 事件分派线程负责(除其他事项外)分派重画请求。 Any thing that blocks this thread will prevent it from updating the UI. 阻止此线程的任何事情都将阻止它更新UI。

Because procCommand.waitFor() is a blocking action, this will prevent any repaint request (or any events for that matter) from been processed until it returns. 由于procCommand.waitFor()是阻止操作,因此将阻止处理任何重画请求(或与此事件有关的任何事件),直到返回为止。

You should execute all time consuming or blocking processes in a separate thread. 您应该在单独的线程中执行所有耗时或阻塞的过程。 The problem you have though, is all updates to the UI mast be executed within the context of the EDT (that is, you should never change/update/modify/create any UI component from any thread other then the EDT) 但是,您遇到的问题是,对UI桅杆的所有更新都是在EDT的上下文中执行的(也就是说,您不应从EDT之外的任何线程更改/更新/修改/创建任何UI组件)

In Swing you have a number of options, in your case, I would suggest using a SwingWorker . 在Swing中,您有很多选择,我建议您使用SwingWorker It will allow you to execute the process in a background thread, but has some easy to use methods for resyncing updates to the UI. 它将允许您在后台线程中执行该过程,但具有一些易于使用的方法来将更新重新同步到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;
    }
}

You should also familiarise yourself with ProcessBuilder . 您还应该熟悉ProcessBuilder It has a number of useful methods for building process and overcomes some of the difficulties people have when trying to get Runtime.getRuntime().exec to work. 它具有许多用于构建过程的有用方法,并克服了人们在尝试使Runtime.getRuntime().exec工作时遇到的一些困难。

You should take a look at http://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html for more details 您应该查看http://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html了解更多详细信息

It seems you're doing it all in one thread. 似乎您正在一个线程中完成所有操作。

Use event dispatch thread to call your gui code. 使用事件分配线程来调用您的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 {
        }
    }

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

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