【问题标题】:How can I make this method update the GUI within my loop?我怎样才能让这个方法在我的循环中更新 GUI?
【发布时间】:2016-01-18 15:31:55
【问题描述】:

我正在制作一个运行几个 cmd 命令(USMT 和文件传输)的程序

它工作正常,但我只从文本框中的 cmd 获取最后一行,并且只有在它完成操作之后。我希望它实时打印 cmd 输出的内容。

 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);
    }
}

【问题讨论】:

  • 您是否尝试过使用revalidate()repaint() 刷新文本字段?
  • 您的循环阻塞了负责绘制的线程(事件调度线程)。您应该在后台线程中执行耗时的任务,例如使用SwingWorker

标签: java swing cmd


【解决方案1】:

原因...

问题的基本原因是您阻塞了事件调度线程,这会阻止 UI 在命令执行后更新。

Swing 是一个单线程框架,这意味着您不应在 EDT 的上下文中执行阻塞或长时间运行的代码。 Swing 也不是线程安全的,这意味着您永远不应该从 EDT 的上下文之外修改 UI 的状态。

详情请见Concurrency in Swing

解决方案...

要解决这个问题,您有两个基本选择。您可以使用Thread,但随后您需要负责确保 UI 的所有更新都与 EDT 的上下文同步,或者您可以使用SwingWorker,例如...

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");
            }
        }

    }

}

Worker Threads and SwingWorker了解更多详情

【讨论】:

  • +1 用于使用实际为任务设计的机制,而不是像我那样粗略地制作线程解决方案。
  • 这个缩进很深......你会成为一个疯狂的程序员;-)
  • @tt 责备 so 及其 4 个空格缩进要求:P
【解决方案2】:

此时,您正在使用readLine() 读取命令输出,然后将其直接放入setText()

实时更新

为了使代码实时更新,我们定义了一个新线程并使用该线程通过套接字读取OutputStream

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();
}

在上面,我们定义了一个新线程,用于读取行。

输出所有行

有时,您需要将命令打印的所有行都显示在屏幕上,这很容易使用 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);
}

上面使用 StringBuilder 来临时存储完成的结果,然后再将其写入屏幕。

【讨论】:

  • 上面的代码可以编译运行,但是直到操作完成后,文本框中仍然没有显示任何内容。
  • 而且...现在你违反了 Swing 的单线程规则,你不应该从 Event Dispatching Thread 的上下文之外修改任何 UI 组件。更多详情请见Concurrency in Swing
  • @MadProgrammer 当我学习 java 时,它是线程安全的。但现在不是因为如果被错误标记为线程安全,请参阅bugs.java.com/view_bug.do?bug_id=476538 我会更新答案
  • @Ferrybig 这从来都不是线程安全的,文档是错误的。 Swing 的一般经验法则,没有什么是线程安全的(好吧,我会给你repaint,因为这很有用) - 这样更容易记住:P
  • @Ferrybig 仅供参考:像这样使用invokeLater 可能会使事件队列饱和,导致它“停止”,在短时间内这可能不是问题,但我在处理时遇到了问题长时间处理大量数据(复制大量文件(GB / TB)和长时间运行的过程,如编码视频,输出大量状态数据,但需要很长时间运行;)
猜你喜欢
  • 2021-02-17
  • 2017-12-20
  • 2020-08-14
  • 1970-01-01
  • 2020-02-20
  • 2017-06-25
  • 2020-06-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多