4

I have a "ConsoleFrame" which should display my console output in real-time to a JTextArea.

I redirected the output streams:

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));
}

and call the SwingUtilities.invokeAndWait method to append the new text, which works fine

private void updateTextArea(final String text) {
    try {
        SwingUtilities.invokeAndWait(new Runnable() {
            @Override
            public void run() {
                txt_console.append(text);
            }
        });
    } catch (InterruptedException ex) {
    } catch (InvocationTargetException ex) {
    }
}

but it shows me in my new ConsoleFrame this error: java.lang.Error: Cannot call invokeAndWait from the event dispatcher thread and I get that because of the EDT - but why does it work and how can I adapt my code to make it work properly?

mKorbel
  • 109,525
  • 20
  • 134
  • 319
Christian 'fuzi' Orgler
  • 1,682
  • 8
  • 27
  • 51

2 Answers2

2
  • invokeAndWait must be called out of EDT, otherwise caused a.m. exceptions,

  • carefully with invokeAndWait, because can freeze whole Swing GUI, locked by exceptions from RepaintManager (not in all cases only GUI is created, relayout, refreshed some of methods), then applications required restart,

  • for invokeAndWait is required to test if (EventQueue.isDispatchThread()) { / if (SwingUtilities.isEventDispatchThread()) { on true you can to setText("")/append("") without any side effects, output is done on EDT, but is about good practicies to wrap inside invokeLater

  • use SwingWorker, there are implemented methods process, publish, setProcess and done, all mentioned methods notified EDT by default,

  • SwingWorker is designated to run only once time, for repeatly (on some period) to use Executor for SwingWorker or Runnable#Thread as most simple, clear and without any side issues, effects

mKorbel
  • 109,525
  • 20
  • 134
  • 319
  • can y give any example how y mean that part with the Executor? I can't use the SwingWorker in the traditionell way because I have to cover all System.out.... calls in different JPanels. – Christian 'fuzi' Orgler Jul 17 '13 at 18:11
  • is better to use SwingWorker in the case that you have good knowledge from Java Essential Classes, example [Executor & SwingWorker](http://stackoverflow.com/search?q=user%3A714968+[swingworker]+executor), notice Executor is black hole, missing a few important methods, the same with SwingWorker, means Executor doesn't care about invoked Object, SWingWorker is possible to listening only from PropertyChangeListener – mKorbel Jul 17 '13 at 18:23
0

You can use SwingUtilities.invokeLater() from any thread, and for error stream it is likely best to use it even when you are already in EDT:

private void updateTextArea(final String text) {
    SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
            txt_console.append(text);
        }
    });
}

That particular error you get comes from outside the EDT, and then invokeAndWait() may be called so you get the output to your console.

kiheru
  • 6,588
  • 25
  • 31
  • if I try this then I only get the output AFTER the whole calculating process has finished, but I want a realtime output – Christian 'fuzi' Orgler Jul 17 '13 at 17:36
  • 1
    Are you doing the calculation in the EDT? Don't do that, use SwingWorkers for doing heavy tasks so that the EDT does not get blocked. – kiheru Jul 17 '13 at 17:39
  • I can't, I want the ConsoleFrame to be perminant for every single System.out.println call - So i can't do the things in SwingWorker.. – Christian 'fuzi' Orgler Jul 17 '13 at 17:55
  • The console can stay there, and you can initialize it first. Using a SwingWorker does not prevent that in anyway. That's what it's meant for - doing the heavy work while the GUI keeps responsive. – kiheru Jul 17 '13 at 18:25
  • @Christian'fuzi'Orgler: yes you can and should do this with a SwingWorker, and in fact that is the correct solution for this problem. The publish/process method pair would likely work well for your situation. – Hovercraft Full Of Eels Jul 17 '13 at 18:41