[英]Process output only becomes available after the process has finished
我有一個Runnable,它從外部調用的exe(請參見下文)讀取控制台輸出,並將其寫入日志文件和JTextArea。
但是我的Runnable在exe完全完成之前不會在JTextArea中顯示控制台輸出。 如何獲得它以在發生時打印控制台輸出?
簡短代碼示例如下:
//主要
import java.awt.*;
import java.io.IOException;
import javax.swing.*;
public class Example extends JFrame {
private static final long serialVersionUID = 1L;
public static int maxX, maxY;
public static JTextArea ta = new JTextArea(20, 60);//For LOG display window
public static void main(String args[] ) throws IOException
{
new Example();
}
public Example() {
this.setTitle("Example");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//MAIN Panel
final JPanel main = new JPanel();
JButton RunButton = button.run(main);
main.add(RunButton);
Container container = getContentPane();
container.add(main);
this.pack();
this.setVisible(true);
}
}
//按鈕動作監聽器
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.*;
public class button {
public static JButton run( final JPanel parent ) {
JButton RunButton = new JButton();
RunButton.setText("Start!");
RunButton.addActionListener(
new ActionListener()
{
public void actionPerformed( ActionEvent event)
{
try
{
//Set up LOG Display
JDialog dialog = new JDialog((JFrame)null, "Working...");
JPanel temp_panel = new JPanel();
temp_panel.add(new JScrollPane(Example.ta));
dialog.getContentPane().add(temp_panel);
dialog.pack();
dialog.setVisible(true);
//Build the Command
ArrayList<String> command = new ArrayList<String>();
command.add("ping");
command.add("127.0.0.1");
//Start the process
Process p = new ProcessBuilder(command).start();
//Starts LOG display capture in separate thread
SwingUtilities.invokeLater(new execute(p));
//Wait for call to complete
p.waitFor();
}
catch(Exception err)
{
JOptionPane.showMessageDialog( parent, "Error Executing Run!", "Warning", JOptionPane.ERROR_MESSAGE );
}
}//end ActionPerformed
});
return RunButton;
}
}
// Runnable接口
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class execute implements Runnable {
String line;
Process p;
public execute ( Process process ) {
p = process;
}
public void run() {
try {
//Read Process Stream Output and write to LOG file
BufferedReader is = new BufferedReader( new InputStreamReader(p.getInputStream()));
while ( (line = is.readLine()) != null ) {
Example.ta.append(line + "\n");
}
System.out.flush();
} catch(Exception ex) { ex.printStackTrace(); }
}
}
也許是因為您不尊重Swing的線程策略 。 所有對swing組件的訪問都必須在事件分發線程中完成。 因此,您的可運行對象應該使用SwingUtilities.invokeLater
更新EDT中的文本區域,而不是單獨的線程中的文本區域。
編輯:正如阿爾法在他的評論中提到的: JTextArea.append
是線程安全的,因此在這里不是絕對需要的。 不過,我仍然會這樣做,因為如果文本區域的追加被任何其他Swing交互所代替或補充,它將不再是線程安全的。
也可能是外部進程沒有發送任何換行符,這使readLine
一直阻塞,直到找到一個換行符或通信結束為止。
只是為了幫助礦工-下面是一個完全簡約的示例(忽略了並非絕對必要的所有內容),該示例確實在我的上下文中起作用:每一行在textArea中顯示為已讀。 它基本上是使用Justin建議的SwingWorker,並為了清楚起見重新安排了一些東西。
public class ProcessExample {
public static class ProcessWorker extends SwingWorker<Void, String> {
private JTextArea ta;
private List<String> process;
public ProcessWorker(List<String> command, JTextArea ta) {
this.process = command;
this.ta = ta;
}
@Override
protected Void doInBackground() throws Exception {
Process p = new ProcessBuilder(process).start();
// Read Process Stream Output and write to LOG file
BufferedReader is = new BufferedReader(new InputStreamReader(
p.getInputStream()));
String line;
while ((line = is.readLine()) != null) {
publish(line);
}
is.close();
return null;
}
@Override
protected void process(List<String> chunks) {
for (String string : chunks) {
ta.append(string + "\n");
}
}
}
private void startProcess(JTextArea ta) {
ArrayList<String> command = new ArrayList<String>();
command.add("ping");
command.add("127.0.0.1");
new ProcessWorker(command, ta).execute();
}
private JComponent getContent() {
JPanel main = new JPanel(new BorderLayout());
final JTextArea ta = new JTextArea(20, 60);
main.add(new JScrollPane(ta));
Action action = new AbstractAction("Start!") {
@Override
public void actionPerformed(ActionEvent e) {
startProcess(ta);
}
};
main.add(new JButton(action), BorderLayout.SOUTH);
return main;
}
public static void main(String args[]) throws IOException {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame("Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new ProcessExample().getContent());
frame.pack();
frame.setVisible(true);
}
});
}
}
您可以改為使用SwingWorker嘗試相同的邏輯。 您可以擴展此類,而不是實現runnable。 它可以將您的Text區域用作參數,並且您可以發布數據,而不必處理SwingUtils.invokeLater,這顯然很容易...
嘗試:
public class execute extends javax.swing.SwingWorker {
String line;
Process p;
JTextArea jta;
File f = new File( properties.prop.getProperty( "LOG_FILE_DIR" ) + "\\PartGen.log");
public execute ( Process process , JTextArea jta ) {
p = process;
this.jta = jta;
}
//implements a method in the swingworker
public void doInBackground() throws Exception {
//Read Process Stream Output and write to LOG file
BufferedReader is = new BufferedReader( new InputStreamReader(p.getInputStream()));
while ( (line = is.readLine()) != null ) {
osfile.writeline(line, f);
publish(new String(line + "\n"));
}
System.out.flush();
return null;
}
//This will happen on the UI Thread.
public void process(List lines){
for(Object o : lines){
jta.append((String)o);
}
}
public void done(){
try{
get();
//You will get here if everything was OK. So show a popup or something to signal done.
}catch(Exception ex){
//this is where your IO Exception will surface, should you have one.
}
}
}
另外,在您的調用代碼中,我認為它位於您的ui中:
Process p = new ProcessBuilder(command).start();
execute ex = new execute( p , yourTextArea);
ex.execute();
我沒有嘗試對此進行編譯,因此您可能必須對照API進行檢查,但希望它能使您大致了解該怎么做。
在這種情況下,問題在於waitFor:
p.waitFor();
這將導致Button Action Listener在該點上等待,直到該過程完成。
問題不是線程沒有捕獲數據,而是JTextArea沒有刷新。 repaint()
, revalidate()
和updateUI()
不會刷新JTextArea,但是以下內容可以刷新:
Example.ta.update(Example.ta.getGraphics());
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.