Friday, January 02, 2009

Redirecting System.out and System.err to JTextPane or JTextArea

In Swing, if you want to redirect System.err and System.out to a JTextPane or a JTextArea, you only need to override the write() methods of OutputStream to append the text to the text pane instead.

The example below shown how to do it with JTextPane. For JTextArea, it's quite simpler, just call JTextArea.append()

This is useful when you spawn external process from a Swing application and want to see its output in your own text component.

Jan 04 2009: Revised regarding Eric Burke's comment below.
JTextPane version:
  private void updateTextPane(final String text) {
    SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        Document doc = textPane.getDocument();
        try {
          doc.insertString(doc.getLength(), text, null);
        } catch (BadLocationException e) {
          throw new RuntimeException(e);
        }
        textPane.setCaretPosition(doc.getLength() - 1);
      }
    });
  }

  private void redirectSystemStreams() {
    OutputStream out = new OutputStream() {
      @Override
      public void write(final int b) throws IOException {
        updateTextPane(String.valueOf((char) b));
      }

      @Override
      public void write(byte[] b, int off, int len) throws IOException {
        updateTextPane(new String(b, off, len));
      }

      @Override
      public void write(byte[] b) throws IOException {
        write(b, 0, b.length);
      }
    };

    System.setOut(new PrintStream(out, true));
    System.setErr(new PrintStream(out, true));
  }
JTextArea version:
  private void updateTextArea(final String text) {
    SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        textArea.append(text);
      }
    });
  }

  private void redirectSystemStreams() {
    OutputStream out = new OutputStream() {
      @Override
      public void write(int b) throws IOException {
        updateTextArea(String.valueOf((char) b));
      }

      @Override
      public void write(byte[] b, int off, int len) throws IOException {
        updateTextArea(new String(b, off, len));
      }

      @Override
      public void write(byte[] b) throws IOException {
        write(b, 0, b.length);
      }
    };

    System.setOut(new PrintStream(out, true));
    System.setErr(new PrintStream(out, true));
  }

[+] Old versions

27 comments:

Eric Burke said...

Make sure you aren't appending from a non-Event Dispatch Thread. Oftentimes, other threads will be writing to System.err and System.out. So your example needs to check the thread and use SwingUtilities.invokeLater() properly. Also, beware apps that spew large quantities of data to their output streams. Over time, you'll eventually cause performance problems if you don't periodically truncate older text from the components. Finally, consider some buffering. We've seen cases in our apps where log levels were turned up too high, causing text to come in faster than the Swing components were capable of rendering. Without intelligent buffering, the UI can get locked up.

Hung Huynh said...

Eric, those are great points. Thanks for pointing it out. I've revised the code so that it properly handles the GUI update with SwingUtilities.invokeLater()

LightningIsMyName (LIMN) said...

Thanks a lot - this is very useful.
I needed that for printing masses of text, and It's much faster than my way of textpane.setText(textpane.getText() + text)

victorclf said...

Brilliant idea. Useful, elegant and quick to implement.

Inverse Chi said...

Thank you for this excellent tutorial

ognivo777 said...

Nice workaround) thank you!

newbie65 said...

Hi,
Thanks for this code.  It sure works!
It took me a while to figure out how to best use it and where to put it but finally (messing about here and there) I got it to work.  
For all newbies like me, I put this code in the same class of the gui and simply called redirectSystemStreams() from within a method and hey presto, all output is on the gui.  I used the second part to implement a text area.

Andre said...

Great! Thank You !

abhishek said...

hi, i was looking for a code like this.
i used the above code for jTextArea and tried to call the redirectSystemStreams() but nthing appears on my jTextArea.
can somebody help me specify when to call the redirectSystemStreams() method?????
i copied the above code as it is.
do tell me if i have to copy at some specific place.

hhuynh said...

You can call redirectSystemStreams() in your constructor of your JFrame, or anywhere right after you create your JTextPane or JTextArea

abhishek said...

thanx alot!!!!!!!!!!!!
finally got it. great code!!!!!!!!!!!
cheers!!!!!!!!

Aburu2011 said...

Cheers Bro - great help!!!

Krz Ostr said...

lol. I thought it does not work...
... a button starts a long junit test suite and it completely locked the gui... thats why it hasnt worked :P
... just threaded the junit test suite and it worked very well.

Thx

LL_55 said...

hey,
thankx for this code, i have used the JTextArea in my program but it doesn't seem to work for my program, i tried to do it in a separate class and then call  the redirectSystemStreams() in the constructor of other classes and i've tried it in the class where the frame is created and call the redirectSystemStreams() in the constructor and make the JTextArea visibile in the frame method, but still doesn't work. any help or advise would be greatly apprecaited. thank you 

LL_55 said...

ohh i fixed it.. :)!
thankx for the code again!

crmiller135 said...

Very nice stuff!

pranav said...

Thanks for this wonderful article/code. JTextArea version helped a lot thanks again.

Taylor White said...

Is this supposed to update in real time because it isn't for me so far. Thanks! Taylor

Umahatokula said...

how do i use this pls? what did u do to fix it

Guest said...

Please include a skill level disclaimer. I am a beginner and do not understand how this works. Can you include an example that I can compile and run? After spending several hours over the past two weeks googling my fingers off, I find that just about all posts like this leave out details that make it impossible for a beginner to pick up. How do we get to where you are if you don't speak to us like children with clean minds?

Brett Ansite said...

I find much the same thing sadly, but with about 3 hours of programming experience under my belt I give you the fix:
For jTextArea stuff:
1)Paste the code in the same class as your GUI (for ease of use I guess, I don't really know how to access a class other than the one I'm in anyway)
2) Now go to this part of the stuff - look at that last line I changed... You need to change that to the VARIABLE name of the text area you want to have the goods dump out in (to find variable name you can be in the design mode of the editor and right click on the field and ChangeVariableName (2nd down)).  private void updateTextArea(final String text) {  SwingUtilities.invokeLater(new Runnable() {    public void run() {      jTextArea1.append(text);

Then down where you are actually doing something that needs the output paste in the 
redirectSystemStreams();
in it.

Hope it helps :/

Denis said...

short and simple solution! Nice!!!

Nethrill said...

I just tried you code and have been working to find something like this for a long time. I really appreciate it. Very simple, I did not have to override any of my old legacy Write(PrintStream ps) methods. I can just call them as normal as long as I add redirectSystemStreams(); into any of my actionListeners I wish to have the output redirected.

heyzeus said...

Thanks for this, and to the others: Programming is not easy. He did not create this so beginner Java Developers could jump into advanced projects. That's what this is. What you are doing is advanced stuff. Its like skydiving with a trainer or crocodile hunting with Steve Irwin; it's something you can't do alone. Take the time to develop your skills and carefully study over the code. Its the only way you'll be able to write the same way one day.

Inggar said...

is that true that I only need to change textarea.append into the variabel name of my text area? But it still doesn't work for me. In redirectSystemStreams(); which part I need to change? and what is "int b" ?

hhuynh said...

Yes, that's right. You do have to call redirectSystemStreams() in your code some where after you created your text area

Inggar said...

Thank you. I found the example here: http://billwaa.wordpress.com/2011/11/14/java-gui-console-output/#comment-100
That is very clear. And thank you for the code. It has been works for me.