How to Set Output Stream to Textarea

How to set output stream to TextArea

You need to redirect the print stream to an output stream you can control...

This is an example of concept I developed for an application I'm working on for work. We use this to bring up the output console when it's running at user sites so we can see what's being sent to standard out...until we fixed our logging that is ;)

Basically it puts a custom OutputStream in between the print stream and the console to capture the output, but still allows the content to printed to the console. This is helpful if you're running the program from the command line or IDE. You could put a switch to stop this if you wanted...

Sample Image

public class TestRedirect {

public static void main(String[] args) {
new TestRedirect();
}

public TestRedirect() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}

CapturePane capturePane = new CapturePane();
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(capturePane);
frame.setSize(200, 200);
frame.setLocationRelativeTo(null);
frame.setVisible(true);

PrintStream ps = System.out;
System.setOut(new PrintStream(new StreamCapturer("STDOUT", capturePane, ps)));

System.out.println("Hello, this is a test");
System.out.println("Wave if you can see me");
}
});
}

public class CapturePane extends JPanel implements Consumer {

private JTextArea output;

public CapturePane() {
setLayout(new BorderLayout());
output = new JTextArea();
add(new JScrollPane(output));
}

@Override
public void appendText(final String text) {
if (EventQueue.isDispatchThread()) {
output.append(text);
output.setCaretPosition(output.getText().length());
} else {

EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
appendText(text);
}
});

}
}
}

public interface Consumer {
public void appendText(String text);
}

public class StreamCapturer extends OutputStream {

private StringBuilder buffer;
private String prefix;
private Consumer consumer;
private PrintStream old;

public StreamCapturer(String prefix, Consumer consumer, PrintStream old) {
this.prefix = prefix;
buffer = new StringBuilder(128);
buffer.append("[").append(prefix).append("] ");
this.old = old;
this.consumer = consumer;
}

@Override
public void write(int b) throws IOException {
char c = (char) b;
String value = Character.toString(c);
buffer.append(value);
if (value.equals("\n")) {
consumer.appendText(buffer.toString());
buffer.delete(0, buffer.length());
buffer.append("[").append(prefix).append("] ");
}
old.print(c);
}
}
}

Sample Image
Sample Image

Updated with working example. Test on Windows 7, Java 6 and Mac OS Lion Java 7

From Console ouput to TextArea in GUI Java

If your goal is to catch all output to System.out (what your code snippet suggests to me) you might want to do the following:

  1. Create a subclass of PrintStream which in its write methods also (or exclusively) redirects to the text area.
  2. Set the new PrintStream as System.out

See this example for JavaFX which does the same (except that they are not forwarding to a Swing component, but conceptually it's the same).


Example:

package example;

import java.io.PrintStream;

public class ConsoleToGUI {

public ConsoleToGUI() {
GUI gui = new GUI();
System.setOut(new PrintStream(new RedirectingOutputStream(gui), true));
gui.start();
}

public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("Test program");
}
}

public static void main(String[] args) {
ConsoleToGUI ctg = new ConsoleToGUI();
ctg.run();
}
}

package example;

import java.awt.BorderLayout;

import javax.swing.JFrame;
import javax.swing.JTextArea;

public class GUI {

private JTextArea textArea;
private JFrame frame;

public GUI() {
frame = new JFrame("Example");
frame.setBounds(0, 0, 600, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
textArea = new JTextArea();
frame.getContentPane().add(textArea, BorderLayout.CENTER);
}

public void start() {
frame.setVisible(true);
}

public void appendText(String text) {
textArea.append(text);
}
}

package example;

import java.io.IOException;
import java.io.OutputStream;

public class RedirectingOutputStream extends OutputStream {

private GUI gui;

public RedirectingOutputStream(GUI gui) {
this.gui = gui;
}

@Override
public void write(int b) throws IOException {
gui.appendText(String.valueOf((char) b));
}
}

How to redirect output from System.out to JavaFx TextArea

I understand my issue is of a very specific nature and my question was perhaps too broad to convey that sufficiently.

My objective was to get the System.err messages from the output of the exec function in the JSch library and display it in a JavaFX TextArea. As such, I needed to get the error stream from a remote server, not the error stream of my local program.

I attempted to create an OutputStream to perform this but it didn't solve my issue. As such, I figured out a workaround:

First I wrote the errors from the JSch channel (within which I run my exec fucntion) to a file:

File file = new File("tempOut/errorLog.txt");
FileOutputStream fos = new FileOutputStream(file);
PrintStream ps = new PrintStream(fos);
System.setErr(ps);
((ChannelExec) channel).setErrStream(System.err);

Then I read that file and display its contents in my GUI:

FileReader fileReader = new FileReader(file);    
BufferedReader br = new BufferedReader(fileReader); //creates a buffering character input stream

String line;
while ((line = br.readLine()) != null)
{
terminalOut = textArea_terminalOut.getText();
terminalOut = terminalOut + "\n\n" + line;
}

And that's basically the workaround I figured out for this. If there's a better way, I'd still appreciate it.

Stream Process Output to Java FX Text Area with Auto Scroll

First of all Task.call() method is not executed in JavaFX Application Thread, so changing any state of current view in this method is inappropriate. You are adding "" to consoleTextArea in wrong thread. You should do it like this:

Platform.runLater(() -> {
consoleTextArea.appendText("");
});

Second issue is that invoking consoleTextArea.appendText(""); will not trigger your ChangeListener(In fact, it will do nothing), because you bind consoleTextArea text property to Task message property textProperty.bind(bgTask.messageProperty());. In that case text area will only listen to text change in Task message property. Add your listener to message property:

bgTask.messageProperty().addListener((observable, oldValue, newValue) -> {
// currently:
consoleTextArea.selectPositionCaret(consoleTextArea.getLength());
consoleTextArea.deselect();

// also tried:
// consoleTextArea.setScrollTop(Double.MAX);
});

Redirecting System.out.print(ln) to a textArea

Swing is a single threaded framework, this means that any operation which is run within the context of the Event Dispatching Thread, which is long running in nature or blocks for some reason (like I/O operations), will prevent the EDT from processing new events and update the UI.

See Concurrency in Swing for more details

If you're using something like How to set output stream to TextArea to redirect the System.out, then you could safely wrap the decryption process in some thing like a SwingWorker or other Thread, for example...

public class DecryptWorker extends SwingWorker {

private File[] files;

public DecryptWorker(File[] files) {
this.files = files;
}

@Override
protected Object doInBackground() throws Exception {
if(files[0] != null) {
...
for (int j = 0; j < files.length; j++) {
// SDencryptFiles() has System.out.println()'s in it, but
// no System.out's show in the JScrollPane until after
// SDencryptFiles completes I want then to appear as they
// are executed
SDencryptFiles(String, String, int);
}
}
return null;
}

}

See Worker Threads and SwingWorker for more details...

How can I turn a text area into an input stream in java?

I figured it out. The problem was that Scanner was calling InputStream.read(char[],int,int), which is implemented to read the whole stream or the entire sized buffer. The scanner was attempting to fill a buffer of 8000+ bytes, and the default read(...) implementation stops calls read() only after the buffer is full, or -1 is read (EOF).

This lead the scanner to block forever, because most console input would never be that long. The solution was to override the buffered read. The version I need will block for the first byte, and return if there are no more characters, I thought the BufferedInputStream already implemented this by calling available() on the stream, so I tried wrapping my class in one after overriding available and that did not work. My new implementation uses available() as well as EOF as a stopping case.

This is what the implementation now looks like:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import javax.swing.text.StyledDocument;

/**
*
* @author William Matrix Peckham
*/
public class Console extends JTextPane {

DocOutputStream out;
PrintStream pout;
DocInputStream in;
JFrame frame;
StyledDocument doc;

public Console() {
super();
setPreferredSize(new Dimension(500, 500));
doc = this.getStyledDocument();
out = new DocOutputStream(doc,this);
pout=new PrintStream(out);
in = new DocInputStream();
this.addKeyListener(in);
setFGColor(Color.black);
setBGColor(Color.white);
frame = new JFrame("Console");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JScrollPane(this));
frame.pack();
frame.setVisible(true);
}

public InputStream getIn(){
return in;
}
public PrintStream getOut(){
return pout;
}

public void setFGColor(Color c){
StyleConstants.setForeground(out.cur, c);
}
public void setBGColor(Color c){
StyleConstants.setBackground(out.cur, c);
}

private static class DocOutputStream extends OutputStream {

StyledDocument doc;
MutableAttributeSet cur;
JTextPane pane;

public DocOutputStream(StyledDocument doc, JTextPane pane) {
this.doc = doc;
this.pane=pane;
cur=new SimpleAttributeSet();
}

@Override
public void write(int b) throws IOException {
try {
doc.insertString(doc.getLength(), (char)b+"", cur);
pane.setCaretPosition(doc.getLength());
} catch (BadLocationException ex) {
Logger.getLogger(Console.class.getName()).
log(Level.SEVERE, null, ex);
}

}
}

private static class DocInputStream extends InputStream implements KeyListener {

ArrayBlockingQueue<Integer> queue;

public DocInputStream(){
queue=new ArrayBlockingQueue<Integer>(1024);
}

@Override
public int read() throws IOException {
Integer i=null;
try {
i = queue.take();
} catch (InterruptedException ex) {
Logger.getLogger(Console.class.getName()).
log(Level.SEVERE, null, ex);
}
if(i!=null)
return i;
return -1;
}

@Override
public int read(byte[] b, int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
int c = read();
if (c == -1) {
return -1;
}
b[off] = (byte)c;

int i = 1;
try {
for (; i < len && available() > 0 ; i++) {
c = read();
if (c == -1) {
break;
}
b[off + i] = (byte)c;
}
} catch (IOException ee) {
}
return i;

}

@Override
public int available(){
return queue.size();
}

@Override
public void keyTyped(KeyEvent e) {
int c = e.getKeyChar();
try {
queue.put(c);
} catch (InterruptedException ex) {
Logger.getLogger(Console.class.getName()).
log(Level.SEVERE, null, ex);
}
}

@Override
public void keyPressed(KeyEvent e) {

}

@Override
public void keyReleased(KeyEvent e) {
}

}
}


Related Topics



Leave a reply



Submit