簡體   English   中英

僅在過程完成后,過程輸出才可用

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM