14 Replies - 12628 Views - Last Post: 31 July 2009 - 12:37 AM Rate Topic: -----

#1 SieGeCrafT   User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 9
  • Joined: 30-July 09

External Program Output To JTextArea

Posted 30 July 2009 - 12:50 AM

I am using the following code to capture the output from an external program and print it into the terminal. The output is captured and printed while the program runs.
			try{
				String [] cmd = {"XXX",StorAge.f.getPath()};
				Process proc = new ProcessBuilder(cmd).start();
				InputStream out = proc.getInputStream();
				InputStreamReader isr = new InputStreamReader(out);
				BufferedReader br = new BufferedReader(isr,100);
				System.out.println("\n<XXX OUTPUT>");
				String line = null;
				while ( (line = br.readLine()) != null)
					System.out.println(line);
				System.out.println("</XXX OUTPUT>\n");
			}catch(NullPointerException e1){
				System.out.println("Datafile not found error. Please open a file or save your current work to one.");
			}catch (IOException e2) {
				System.out.println("XXX not found. Please specify the correct path in your .bashrc file.");
			}



Now I use the following code to create a JTextArea to which output can be written.

TextArea constructor:
		Ps=System.out;
		frame=new JFrame("Text Output");
		frame.setSize(550,391);
		frame.setVisible(true);
		area=new JTextArea();
		area.setEditable(false);
		os=new TextAreaOutputStream(area);
		ps=new PrintStream(os);
		JScrollPane pane=new JScrollPane(area);
		frame.add(pane);



The TextAreaOutputStream class. This one I've borrowed from the net.
import java.io.*;
import javax.swing.JTextArea;
 
public final class TextAreaOutputStream extends OutputStream {

	private final JTextArea textArea;
	private final StringBuilder sb = new StringBuilder();

	public TextAreaOutputStream(final JTextArea textArea) {
		this.textArea = textArea;
	}

	@Override
	public void flush(){}
	
	@Override
	public void close(){ }

	@Override
	public void write(int b) throws IOException {

		if (b == '\r')
			return;
		
		if (b == '\n') {
			textArea.append(sb.toString());
			sb.setLength(0);
		}
		
		sb.append((char)b);
	}
}



Method to change the standard out back and forth inbetween textarea and terminal.
	void setOutput(boolean terminal){
		if(terminal)
			System.setOut(Ps);
		else
			System.setOut(ps);
	}



My problem is that when I print the output to the JTextArea my JTextArea freezes while the external program is running and then prints everything when the external program is done. I want it to print the output as it is generated, just like when I use the terminal for output. How do I do it?
Thanks,
SieGe

This post has been edited by SieGeCrafT: 30 July 2009 - 05:51 AM


Is This A Good Question/Topic? 0
  • +

Replies To: External Program Output To JTextArea

#2 syfran   User is offline

  • D.I.C Lover
  • member icon

Reputation: 83
  • View blog
  • Posts: 1,103
  • Joined: 12-July 09

Re: External Program Output To JTextArea

Posted 30 July 2009 - 01:11 AM

Not exactly sure about this but could it be the buffered reader? Either that or it is something with the process class only returning info once it is finished.

Does it at least print the <OUTPUT> tags?

This post has been edited by syfran: 30 July 2009 - 01:14 AM

Was This Post Helpful? 0
  • +
  • -

#3 SieGeCrafT   User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 9
  • Joined: 30-July 09

Re: External Program Output To JTextArea

Posted 30 July 2009 - 01:14 AM

View Postsyfran, on 30 Jul, 2009 - 12:11 AM, said:

Not exactly sure about this but could it be the buffered reader? Either that or it is something with the process class only returning info once it is finished.

I've had that suspicion too, but I don't think it's that as it works perfectly fine when I print to a terminal - still using the same process and the same buffered reader.

Quote

Does it at least print the <OUTPUT> tags?

Nope, bot tags are printed after the external program is done.

This post has been edited by SieGeCrafT: 30 July 2009 - 01:29 AM

Was This Post Helpful? 0
  • +
  • -

#4 syfran   User is offline

  • D.I.C Lover
  • member icon

Reputation: 83
  • View blog
  • Posts: 1,103
  • Joined: 12-July 09

Re: External Program Output To JTextArea

Posted 30 July 2009 - 01:19 AM

Have you tried to temporarily replace the system.out with a simple setText to see how that works?
Was This Post Helpful? 0
  • +
  • -

#5 SieGeCrafT   User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 9
  • Joined: 30-July 09

Re: External Program Output To JTextArea

Posted 30 July 2009 - 01:45 AM

View Postsyfran, on 30 Jul, 2009 - 12:19 AM, said:

Have you tried to temporarily replace the system.out with a simple setText to see how that works?

Same effect.
Was This Post Helpful? 0
  • +
  • -

#6 syfran   User is offline

  • D.I.C Lover
  • member icon

Reputation: 83
  • View blog
  • Posts: 1,103
  • Joined: 12-July 09

Re: External Program Output To JTextArea

Posted 30 July 2009 - 02:03 AM

The newline codes. That could also easily be the problem.
What operating system are you using?

This post has been edited by syfran: 30 July 2009 - 02:04 AM

Was This Post Helpful? 0
  • +
  • -

#7 SieGeCrafT   User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 9
  • Joined: 30-July 09

Re: External Program Output To JTextArea

Posted 30 July 2009 - 02:28 AM

View Postsyfran, on 30 Jul, 2009 - 01:03 AM, said:

The newline codes. That could also easily be the problem.
What operating system are you using?

Are you suggesting that I use print instead of println? That didn't change anything, please let me know if I misunderstood you.
Also, I'd like to add that it's not only my JTextArea that freezes, but the entire GUI. That actually happens when I print to the terminal too but then at least the output is printed as it is generated.

I'm running an older version of RedHat linux.
Was This Post Helpful? 0
  • +
  • -

#8 syfran   User is offline

  • D.I.C Lover
  • member icon

Reputation: 83
  • View blog
  • Posts: 1,103
  • Joined: 12-July 09

Re: External Program Output To JTextArea

Posted 30 July 2009 - 02:34 AM

The newline codes I was talking about are in the TextAreaOutputStream but first try the below.

Did you launch your gui on a separate thread? If not that is probably why it is freezing and may be your whole problem.
Was This Post Helpful? 0
  • +
  • -

#9 SieGeCrafT   User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 9
  • Joined: 30-July 09

Re: External Program Output To JTextArea

Posted 30 July 2009 - 02:39 AM

View Postsyfran, on 30 Jul, 2009 - 01:34 AM, said:

The newline codes I was talking about are in the TextAreaOutputStream but first try the below.

Did you launch your gui on a separate thread? If not that is probably why it is freezing and may be your whole problem.

I don't think I did. I'm no good with threads. Should I launch the GUI normally and the external program on a new thread, both on new threads or only the GUI on a new thread? Should the JTextArea be on a new thread too?

Edit: I tried implementing Runable on both the GUI main class and the JTextArea class and then creating new threads for both them, didn't change anything. I'm afraid I don't know how to start the external program in a new thread.

This post has been edited by SieGeCrafT: 30 July 2009 - 02:48 AM

Was This Post Helpful? 0
  • +
  • -

#10 syfran   User is offline

  • D.I.C Lover
  • member icon

Reputation: 83
  • View blog
  • Posts: 1,103
  • Joined: 12-July 09

Re: External Program Output To JTextArea

Posted 30 July 2009 - 02:47 AM

Here is how I launch my GUIs
public void createGUI() {
		javax.swing.SwingUtilities.invokeLater(new Runnable() {

			public void run() {
				createAndShow();
			}
		});
	}



Where create and show is the method that initializes everything on the gui
Was This Post Helpful? 0
  • +
  • -

#11 SieGeCrafT   User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 9
  • Joined: 30-July 09

Re: External Program Output To JTextArea

Posted 30 July 2009 - 02:54 AM

View Postsyfran, on 30 Jul, 2009 - 01:47 AM, said:

Here is how I launch my GUIs
public void createGUI() {
		javax.swing.SwingUtilities.invokeLater(new Runnable() {

			public void run() {
				createAndShow();
			}
		});
	}



Where create and show is the method that initializes everything on the gui

I tried implementing your code, like this:
(FirstPage() is the class with the initial JFrame.)
	public static void main(String[] args){
		
		javax.swing.SwingUtilities.invokeLater(new Runnable() {

			public void run() {
				new FirstPage();
			}
		});
	}


Didn't solve the problem though.
Was This Post Helpful? 0
  • +
  • -

#12 syfran   User is offline

  • D.I.C Lover
  • member icon

Reputation: 83
  • View blog
  • Posts: 1,103
  • Joined: 12-July 09

Re: External Program Output To JTextArea

Posted 30 July 2009 - 03:09 AM

Does FirstPage contain all of the code? The key is to separate the gui with the logic control. That way the gui will still be responsive. There are other ways to do it but I believe the easiest would be to do a little restructuring so that they are not so integrated.
Was This Post Helpful? 0
  • +
  • -

#13 SieGeCrafT   User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 9
  • Joined: 30-July 09

Re: External Program Output To JTextArea

Posted 30 July 2009 - 04:10 AM

View Postsyfran, on 30 Jul, 2009 - 02:09 AM, said:

Does FirstPage contain all of the code? The key is to separate the gui with the logic control. That way the gui will still be responsive. There are other ways to do it but I believe the easiest would be to do a little restructuring so that they are not so integrated.

It contains a JFrame, some buttons, an imageicon (and one method to create it) and an actionlistener. The output window is written in a separate class but an object of the class is created within FirsPage's initiation. I'll try moving the actionlistener and creating the output object from the run method instead.

Edit: Above mentioned changes did not solve the problem.


I also made a mini program displaying the problem, here's the full code if someone wants to compile and run. It's a frame with a textarea and two buttons. One which changes the output back and forth inbetween the terminal and the textarea, and another which calls a subroutine to print "Done" once every second for five seconds. But as you anyone trying the program will see it will write "Done" five times after five seconds instead if you set the output to the textarea.

Class 1:
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import javax.swing.*;


class OutputWindow implements ActionListener{
	final JTextArea area;
	JFrame frame;
	boolean terminaL;
	OutputStream os;
	PrintStream ps;
	PrintStream Ps;
	JButton x,y;
	int k=0;
	
	OutputWindow(){	
		//Creating a testarea.
		area=new JTextArea();
		area.setFont(new Font("Times New Roman",Font.PLAIN, 12));
		area.setEditable(false);
		JScrollPane pane=new JScrollPane(area);
		pane.setPreferredSize(new Dimension(550,391));
		
		//Saving the old System.
		Ps=System.out;
		//Creating an output stream to the text area.
		os=new TextAreaOutputStream(area);
		ps=new PrintStream(os);
		
		//Creating a frame.
		frame=new JFrame("Text Output");
		frame.setLayout(new GridBagLayout());
		frame.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);
		frame.setVisible(true);
		
		//Creating two buttons.
		x=new JButton("Change Output");
		y=new JButton("Start");
		x.addActionListener(this);
		y.addActionListener(this);
		
		//adding ecerything to the frame.
		GridBagConstraints c=new GridBagConstraints();
		c.weightx=1;
		c.weighty=1;
		c.fill=GridBagConstraints.BOTH;
		c.gridheight=6;
		c.gridwidth=2;
		c.gridx=1;
		c.gridy=1;
		frame.add(pane,c);
		c.gridheight=1;
		c.gridwidth=1;
		c.gridx=1;
		c.gridy=7;
		frame.add(x,c);
		c.gridx=2;
		frame.add(y,c);
		frame.pack();
		System.setOut(ps);
	}
	
	//Changed outbut back and forth inbetween output window and terminal.
	//Called by the Change Output button.
	void setOutput(){
		if(k%2==0)
			System.setOut(Ps);
		else
			System.setOut(ps);
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		//The Change Output button.
		if(e.getSource()==x){
			System.out.println("Output Changed");
			setOutput();
			k++;
			System.out.println("Output Changed");
		}
		
		//The Start button.
		if(e.getSource()==y){
			  try{
				  String [] cmd = {"java","OutputTeters"};
				  Process proc = new ProcessBuilder(cmd).start();
				InputStream out = proc.getInputStream();
				InputStreamReader isr = new InputStreamReader(out);
				BufferedReader br = new BufferedReader(isr,100);
				String line = null;
				System.out.println("\n<OUTPUT>");
				while ( (line = br.readLine()) != null){
					System.out.println(line);
				}
				System.out.println("</OUTPUT>\n");
			}catch(Exception e1){}
		}
		
	}
	
	public static void main(String[] args){
	 	new OutputWindow();
	}	
}


//External program used to generate output.
class OutputTesters {
	public static void main (String[] args){
		for(int j=1;j<5;j++){
			try{
				  Thread.currentThread();
				  Thread.sleep(1000);
				  System.out.println("Done.");
				}
			catch(InterruptedException ie){ }
		}
	}
}




Class 2:
import java.io.*;
import javax.swing.JTextArea;
 
	class TextAreaOutputStream extends OutputStream {

	private final JTextArea textArea;
	private final StringBuilder sb = new StringBuilder();

	TextAreaOutputStream(final JTextArea textArea) {
		this.textArea = textArea;
	}

	@Override
	public void flush(){ 
		System.out.println("FLUSHED!");
	}
	
	@Override
	public void close(){ }

	@Override
	public void write(int b) throws IOException {

		if (b == '\r')
			return;
		
		if (b == '\n') {
			textArea.append(sb.toString());
			sb.setLength(0);
		}
		
		sb.append((char)b);
	}
}

This post has been edited by SieGeCrafT: 30 July 2009 - 05:59 AM

Was This Post Helpful? 0
  • +
  • -

#14 syfran   User is offline

  • D.I.C Lover
  • member icon

Reputation: 83
  • View blog
  • Posts: 1,103
  • Joined: 12-July 09

Re: External Program Output To JTextArea

Posted 30 July 2009 - 12:07 PM

I've got it. ( I worked my ass off to find this so I hope it really helps)
Most of the code is yours though I rearranged it slightly. The major difference is that I launched the application on the even dispatch thread and put the line reading into a SwingWorker. Those two changes were in the OutputWindow class but I'll post all the code.

I apoligize for the messed up indentation. My tab is different than yours and it messed some things up, if you really need it formatted say so and I can always throw it through eclipse.

Outputwindow.java
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import javax.swing.*;


class OutputWindow implements ActionListener{
	JTextArea area;
	JFrame frame;
	boolean terminaL;
	OutputStream os;
	PrintStream ps;
	PrintStream Ps;
	JButton x,y;
	int k=0;
   
	OutputWindow(){	
	 javax.swing.SwingUtilities.invokeLater(new Runnable() {
		public void run() {
			createAndShowGui();
		}
	 	}   );
	}
	
	void createAndShowGui() {
	//Creating a testarea.
		area=new JTextArea();
		area.setFont(new Font("Times New Roman",Font.PLAIN, 12));
		area.setEditable(false);
		JScrollPane pane=new JScrollPane(area);
		pane.setPreferredSize(new Dimension(550,391));
	   
		//Saving the old System.
		Ps=System.out;
		//Creating an output stream to the text area.
		os=new TextAreaOutputStream(area);
		ps=new PrintStream(os);
	   
		//Creating a frame.
		frame=new JFrame("Text Output");
		frame.setLayout(new GridBagLayout());
		frame.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);
		frame.setVisible(true);
	   
		//Creating two buttons.
		x=new JButton("Change Output");
		y=new JButton("Start");
		x.addActionListener(this);
		y.addActionListener(this);
	   
		//adding ecerything to the frame.
		GridBagConstraints c=new GridBagConstraints();
		c.weightx=1;
		c.weighty=1;
		c.fill=GridBagConstraints.BOTH;
		c.gridheight=6;
		c.gridwidth=2;
		c.gridx=1;
		c.gridy=1;
		frame.add(pane,c);
		c.gridheight=1;
		c.gridwidth=1;
		c.gridx=1;
		c.gridy=7;
		frame.add(x,c);
		c.gridx=2;
		frame.add(y,c);
		frame.pack();
		System.setOut(ps);
	}
   
	//Changed outbut back and forth inbetween output window and terminal.
	//Called by the Change Output button.
	void setOutput(){
		if(k%2==0)
			System.setOut(Ps);
		else
			System.setOut(ps);
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		//The Change Output button.
		if(e.getSource()==x){
			System.out.println("Output Changed");
			setOutput();
			k++;
			System.out.println("Output Changed");
		}
	   
		//The Start button.
		if(e.getSource()==y){
	   SwingWorker sw = new SwingWorker() {
		public Object doInBackground() {
			  try{
				  String [] cmd = {"java","OutputTesters"};
				  Process proc = new ProcessBuilder(cmd).start();
				InputStream out = proc.getInputStream();
				InputStreamReader isr = new InputStreamReader(out);
				BufferedReader br = new BufferedReader(isr,100);
				String line = null;
				System.out.println("\n<OUTPUT>");
				while ( (line = br.readLine()) != null){
					System.out.println(line);
				}
				System.out.println("</OUTPUT>\n");
			}catch(Exception e1){} 
		return null;}};
		sw.execute();
		}
	   
	}
	
	public static void main(String[] args){
		 new OutputWindow();
	}	
}


OutputTesters.java
class OutputTesters {
	public static void main (String[] args){
		for(int j=1;j<5;j++){
			try{
				  Thread.currentThread();
				  Thread.sleep(1000);
				  System.out.println("Done.");
				}
			catch(InterruptedException ie){ }
		}
	}
}



TextAreaOutputStream.java - I changed this but I do not think it had any affect. Your original should work fine.
import java.io.*;
import javax.swing.JTextArea;
 
	class TextAreaOutputStream extends OutputStream {

	private final JTextArea textArea;
	private final StringBuilder sb = new StringBuilder();

	TextAreaOutputStream(final JTextArea textArea) {
		this.textArea = textArea;
	}

	@Override
	public void flush(){
		System.out.println("FLUSHED!");
	}
   
	@Override
	public void close(){ }

	@Override
	public void write(int b) throws IOException {

		
	
	updateTextArea(String.valueOf((char)b));
		
	   
		
	}

private void updateTextArea(final String text) {  
   javax.swing.SwingUtilities.invokeLater(new Runnable() {  
	 public void run() {  
	   textArea.append(text);  
	 }  
   });  
}
}

Was This Post Helpful? 1
  • +
  • -

#15 SieGeCrafT   User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 9
  • Joined: 30-July 09

Re: External Program Output To JTextArea

Posted 31 July 2009 - 12:37 AM

View Postsyfran, on 30 Jul, 2009 - 11:07 AM, said:

I've got it. ( I worked my ass off to find this so I hope it really helps)
Most of the code is yours though I rearranged it slightly. The major difference is that I launched the application on the even dispatch thread and put the line reading into a SwingWorker. Those two changes were in the OutputWindow class but I'll post all the code.

I apoligize for the messed up indentation. My tab is different than yours and it messed some things up, if you really need it formatted say so and I can always throw it through eclipse.

It runs beautifully now. Through trial and error I've isolated which of the changes that are necessary, and it turns your you only need the SwingWorker.

Thanks a lot.

This post has been edited by SieGeCrafT: 31 July 2009 - 12:38 AM

Was This Post Helpful? 0
  • +
  • -

Page 1 of 1