[英]How can I make this method update the GUI within my loop?
我正在制作一個運行一些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);
}
}
問題的根本原因是您阻止了事件調度線程,這將阻止在執行命令后更新UI。
Swing是一個單線程框架,這意味着您不應在EDT上下文中執行阻塞或長時間運行的代碼。 Swing也不是線程安全的,這意味着永遠不要在EDT上下文之外修改UI的狀態。
有關更多詳細信息,請參見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");
}
}
}
}
有關更多詳細信息,請參見工作線程和SwingWorker 。
目前,您正在使用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臨時存儲完成的結果,然后再將其寫入屏幕。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.