System.out.println for JTextArea
EDIT: I edited the post to clarify my question, now myself, I have more understanding.
I am essentially, as the title says, trying to pipe the console to mine JTextArea
in my GUI while doing application tasks.
Here's what I'm doing now:
public 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')
{
final String text = sb.toString() + "\n";
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
textArea.append(text);
}
});
sb.setLength(0);
}
sb.append((char) b);
}
}
The above will successfully redirect System.out
to my above output stream and hence dispatch an event in EventQueue
to update my GUI ( JTextArea
).
Here's the problem:
The current usage invokeLater()
would be, as the docs say:
Causes runnable to have its run method called in the dispatch thread of the EventQueue. This will happen after all pending events are processed.
So what I really want to do is perform my update in the GUI (call run()
) before handling everything else in the EventQueue.
Is it possible to inject an event into my EventQueue? Or can anyone point me to a decent tutorial on this area?
thank,
The following example creates a frame with a text area and redirects to System.out:
import java.awt.BorderLayout;
import java.awt.Container;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
public class JTextAreaOutputStream extends OutputStream
{
private final JTextArea destination;
public JTextAreaOutputStream (JTextArea destination)
{
if (destination == null)
throw new IllegalArgumentException ("Destination is null");
this.destination = destination;
}
@Override
public void write(byte[] buffer, int offset, int length) throws IOException
{
final String text = new String (buffer, offset, length);
SwingUtilities.invokeLater(new Runnable ()
{
@Override
public void run()
{
destination.append (text);
}
});
}
@Override
public void write(int b) throws IOException
{
write (new byte [] {(byte)b}, 0, 1);
}
public static void main (String[] args) throws Exception
{
JTextArea textArea = new JTextArea (25, 80);
textArea.setEditable (false);
JFrame frame = new JFrame ("stdout");
frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
Container contentPane = frame.getContentPane ();
contentPane.setLayout (new BorderLayout ());
contentPane.add (
new JScrollPane (
textArea,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED),
BorderLayout.CENTER);
frame.pack ();
frame.setVisible (true);
JTextAreaOutputStream out = new JTextAreaOutputStream (textArea);
System.setOut (new PrintStream (out));
while (true)
{
System.out.println ("Current time: " + System.currentTimeMillis ());
Thread.sleep (1000L);
}
}
}
Your mistake must lie elsewhere, which you haven't shown us yet. Here's a very simple demo that works as expected with almost the same code as yours (I've been fixing minor issues):
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public 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') {
final String text = sb.toString() + "\n";
textArea.append(text);
sb.setLength(0);
} else {
sb.append((char) b);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame(TextAreaOutputStream.class.getSimpleName());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JTextArea ta = new JTextArea(24, 80);
System.setOut(new PrintStream(new TextAreaOutputStream(ta)));
frame.add(new JScrollPane(ta));
frame.pack();
frame.setVisible(true);
System.out.println("Textarea console initiated");
Timer t = new Timer(1000, new ActionListener() {
int count = 1;
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Outputting line " + count++ + " to the console. Working properly, no?");
}
});
t.start();
}
});
}
}
You may need to use PipedOutputStream
... see the answers to the question here:
How to redirect all console output to a Swing JTextArea / JTextPane with the correct encoding?
Basically what it is, it redirects System.out
to a buffer from which the program can read the output printed with System.out
. It is calledPiping
If you want to see the scrolling effect in the text area, then instead of adding output, you can put the new text at the beginning. Example:
HttpURLConnection con = (HttpURLConnection) (new URL(url[0]).openConnection());
con.setInstanceFollowRedirects(false);
con.connect();
int responseCode = con.getResponseCode();
String location = con.getHeaderField("Location");
textArea.setText(url[0] +"," +responseCode+"," +location+"\n"+textArea.getText()); //new text is prefixed to the existing text
textArea.update(textArea.getGraphics());