简体   繁体   中英

How can I make this method update the GUI within my loop?

I'm making a program that runs a few cmd commands (USMT and file transfer)

It's working fine, but I only get the last line from the cmd in my text box and only after it completes the operation. I want it to print what the cmd is outputting in real time.

 public void load() throws IOException {
    ProcessBuilder builder = new ProcessBuilder(
            "cmd.exe", "/c", "cd \"C:\\usmt\" && loadstate.bat");
    builder.redirectErrorStream(true);
    Process p = builder.start();
    BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream()));
    String line;

    while (true) {

        line = r.readLine();
        if (line == null) { break; }

        cOut.setText(line);
        System.out.println(line);
    }
}

Cause...

The basic cause of your problem is the fact that you are blocking the Event Dispatching Thread, this is preventing the UI from been updated until AFTER the command has executed.

Swing is a single threaded framework, meaning that you shouldn't execute blocking or long running code from within the context of the EDT. Swing is also NOT thread safe, meaning that you should never modify the state of the UI from outside of the context of the EDT.

See Concurrency in Swing for more details

Solution...

To solve this you have two basic options. You could use a Thread , but then you become responsible for ensuring that any and all updates to the UI are synchronised to the context of the EDT, or you could use a SwingWorker , for example...

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutionException;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Runner {

    public static void main(String[] args) {
        new Runner();
    }

    public Runner() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private JTextArea ta;

        public TestPane() {
            setLayout(new BorderLayout());
            ta = new JTextArea(25, 80);
            add(new JScrollPane(ta));

            JButton execute = new JButton("Make it so");
            execute.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    execute.setEnabled(false);
                    CommandWorker worker = new CommandWorker(ta, "cmd.exe", "/c", "cd \"C:\\usmt\" && loadstate.bat");
                    worker.addPropertyChangeListener(new PropertyChangeListener() {
                        @Override
                        public void propertyChange(PropertyChangeEvent evt) {
                            switch (evt.getPropertyName()) {
                                case "state":
                                    SwingWorker work = (SwingWorker) evt.getSource();
                                    switch (worker.getState()) {
                                        case DONE: {
                                            try {
                                                worker.get();
                                            } catch (InterruptedException | ExecutionException ex) {
                                                ex.printStackTrace();;
                                                JOptionPane.showMessageDialog(TestPane.this, "Execution of command failed: " + ex.getMessage());
                                            } finally {
                                                execute.setEnabled(true);
                                            }
                                        }
                                        break;
                                    }
                                    break;
                            }
                        }
                    });
                    worker.execute();
                }
            });

            add(execute, BorderLayout.SOUTH);
        }

    }

    public static class CommandWorker extends SwingWorker<List<String>, String> {

        private JTextArea ta;
        private List<String> commands;

        public CommandWorker(JTextArea ta, List<String> commands) {
            this.ta = ta;
            this.commands = commands;
        }

        public CommandWorker(JTextArea ta, String... commands) {
            this(ta, Arrays.asList(commands));
        }

        @Override
        protected List<String> doInBackground() throws Exception {
            List<String> output = new ArrayList<>(25);
            ProcessBuilder builder = new ProcessBuilder(commands);
            builder.redirectErrorStream(true);

            Process p = builder.start();
            try (BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream()))) {
                String line = null;
                while ((line = r.readLine()) != null) {
                    output.add(line);
                    publish(line);
                }
            }
            return output;
        }

        @Override
        protected void process(List<String> chunks) {
            for (String text : chunks) {
                ta.append(text);
                ta.append("\n");
            }
        }

    }

}

See Worker Threads and SwingWorker for more details

At the moment, you are reading the command output using readLine() , and then you are directly putting it into setText() .

Update in realtime

To make the code update it in real time, we are defining a new Thread and use that thread to read over the OutputStream over the socket:

public void load() throws IOException {

 Thread t = new Thread(() -> {
  try {
   ProcessBuilder builder = new ProcessBuilder(
    "cmd.exe", "/c", "cd \"C:\\usmt\" && loadstate.bat");
   builder.redirectErrorStream(true);
   Process p = builder.start();
   BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream()));
   String line;
   while (true) {
    line = r.readLine();
    if (line == null) {
     break;
    }
    String l = line;
    SwingUtilities.invokeLater(new Runnable() {
     public void run() {
      cOut.setText(l);
     }
    });
    System.out.println(line);
   }
  } catch (IOException ex) {
   ex.printStackTrace(); //Add a better error handling in your app
  }
 });
 t.start();
}

Above, we define a new thread that is used to read the lines.

Outputting all the lines

Sometimes, you need to put all the lines the command is printing on screen, this is easy to do using a StringBuilder:

String line;
StringBuilder total = new StringBuilder();
while (true) {
 line = r.readLine();
 if (line == null) {
  break;
 }
 total.append(line).append('\n');
 cOut.setText(total.toString());
 System.out.println(line);
}

The above uses a StringBuilder to temporary store the finished result, before writing it to the screen.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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