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

后端 未结 2 1007
有刺的猬
有刺的猬 2020-12-02 03:31

I am new to 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 cons

相关标签:
2条回答
  • 2020-12-02 03:37

    It seems you're doing it all in one thread.

    Use event dispatch thread to call your gui code.

    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 {
            }
        }
    
    0 讨论(0)
  • 2020-12-02 04:00

    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.

    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.

    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.

    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)

    In Swing you have a number of options, in your case, I would suggest using a 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.

    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. 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.

    You should take a look at http://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html for more details

    0 讨论(0)
提交回复
热议问题