Redirecting System.out to a TextArea in JavaFX

Dreen picture Dreen · Dec 12, 2012 · Viewed 28.1k times · Source

Update:

Still having the same issue, revised source of main app code: http://pastebin.com/fLCwuMVq

There must be something in CoreTest that blocks the UI, but its doing all sorts of stuff (async xmlrpc requests, async http requests, file io etc), i tried putting it all into runLater but it doesnt help.

Update 2:

I verified the code runs and produces output correctly but the UI component cant manage to display it for ages

Update 3:

OK I fixed it. I don't know why, but no guide about JavaFX said this, and its very important:

Always put your program logic in a separate thread from the Java FX thread


I had this working with Swing's JTextArea but for some reason it doesn't work with JavaFX.

I tried debugging and it and doing .getText() after each write returns what seems to be characters written to it properly, but the actual TextArea in GUI shows no text.

Did I forgot to somehow refresh it or something?

TextArea ta = TextAreaBuilder.create()
    .prefWidth(800)
    .prefHeight(600)
    .wrapText(true)
    .build();

Console console = new Console(ta);
PrintStream ps = new PrintStream(console, true);
System.setOut(ps);
System.setErr(ps);

Scene app = new Scene(ta);
primaryStage.setScene(app);
primaryStage.show();

And the Console class:

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

import javafx.scene.control.TextArea;

public class Console extends OutputStream
{
    private TextArea    output;

    public Console(TextArea ta)
    {
        this.output = ta;
    }

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

}

Note: this is based on a solution from this answer, I removed bits I didn't care about but unmodified (apart from changing from Swing to JavaFX), it had the same result: data written to the UI element, no data showing on the screen.

Answer

assylias picture assylias · Dec 12, 2012

Have you tried running it on the UI Thread?

public void write(final int i) throws IOException {
    Platform.runLater(new Runnable() {
        public void run() {
            output.appendText(String.valueOf((char) i));
        }
    });
}

EDIT

I think your problem is that your run some long tasks in the GUI thread, which is going to freeze everything until it completes. I don't know what

CoreTest t = new CoreTest(installPath);
t.perform();

does, but if it takes a few seconds, your GUI won't update during those few seconds. You need to run those tasks in a separate thread.

For the record, this works fine (I have removed the file and CoreTest bits):

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) throws IOException {

        TextArea ta = TextAreaBuilder.create().prefWidth(800).prefHeight(600).wrapText(true).build();
        Console console = new Console(ta);
        PrintStream ps = new PrintStream(console, true);
        System.setOut(ps);
        System.setErr(ps);
        Scene app = new Scene(ta);

        primaryStage.setScene(app);
        primaryStage.show();

        for (char c : "some text".toCharArray()) {
            console.write(c);
        }
        ps.close();
    }

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

    public static class Console extends OutputStream {

        private TextArea output;

        public Console(TextArea ta) {
            this.output = ta;
        }

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