在 GUI 面板内创建 Java 控制台

发布于 2024-07-10 00:18:20 字数 32 浏览 9 评论 0原文

如何在 GUI 面板内创建 Java 控制台实例?

How can I create an instance of the Java console inside of a GUI panel?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

浅听莫相离 2024-07-17 00:18:20

这是一个正在运行的类。 您可以将其实例安装到系统中并使用以下方法进行错误:

PrintStream con=new PrintStream(new TextAreaOutputStream(...));
System.setOut(con);
System.setErr(con);

更新于 2014-02-19:使用 EventQueue.invokeLater() 来避免 GUI 线程问题,这些问题在原始版本中很少会出现。

2014-02-27 更新:更好的实施

2014-03-25 更新:正确记录和修改 在 run() 方法中删除文本区域中的行,以避免追加和删除之间的竞争条件,如果控制台充满输出,则可能会发生这种情况。 最终结果对我来说似乎也更干净。

2022-11-07更新:进一步完善实施,彻底消除产出泛滥问题。 请注意,当输出过多时,此版本将彻底抑制输出(带有一行注释)。 以前的版本最终会开始 GC 抖动,并且虚拟机响应会很大程度上冻结(实际上不会崩溃),直到它最终设法赶上。

import java.awt.*;
import java.io.*;
import java.util.*;
import java.util.regex.*;
import java.util.List;
import javax.swing.*;

public class TextAreaOutputStream
extends OutputStream
{

// *************************************************************************************************
// INSTANCE PROPERTIES
// *************************************************************************************************

private byte[]                          oneByte;                                                    // array for write(int val);
private Appender                        appender;                                                   // most recent action

// *************************************************************************************************
// INSTANCE CONSTRUCTORS/INIT/CLOSE/FINALIZE
// *************************************************************************************************

public TextAreaOutputStream(JTextArea txtara) {
    this(txtara,1000);
    }

public TextAreaOutputStream(JTextArea txtara, int maxlin) {
    this(txtara,maxlin,null);
    }

public TextAreaOutputStream(JTextArea txtara, int maxlin, Pattern rmvptn) {
    if(maxlin<1) { throw new IllegalArgumentException("TextAreaOutputStream maximum lines must be positive (value="+maxlin+")"); }
    oneByte=new byte[1];
    appender=new Appender(txtara,maxlin,rmvptn);
    }

// *************************************************************************************************
// INSTANCE METHODS - ACCESSORS
// *************************************************************************************************

/** Clear the current console text area. */
public synchronized void clear() {
    if(appender!=null) { appender.clear(); }
    }

// *************************************************************************************************
// INSTANCE METHODS - OUTPUT STREAM IMPLEMENTATION
// *************************************************************************************************

public synchronized void close() {
    appender=null;
    }

public synchronized void flush() {
    }

public synchronized void write(int val) {
    oneByte[0]=(byte)val;
    write(oneByte,0,1);
    }

public synchronized void write(byte[] ba) {
    write(ba,0,ba.length);
    }

public synchronized void write(byte[] ba,int str,int len) {
    if(appender!=null) { appender.append(bytesToString(ba,str,len)); }
    }

// *************************************************************************************************
// INSTANCE METHODS - UTILITY
// *************************************************************************************************

@edu.umd.cs.findbugs.annotations.SuppressWarnings("DM_DEFAULT_ENCODING")
static private String bytesToString(byte[] ba, int str, int len) {
    try { return new String(ba,str,len,"UTF-8"); } catch(UnsupportedEncodingException thr) { return new String(ba,str,len); } // all JVMs are required to support UTF-8
    }

// *************************************************************************************************
// STATIC NESTED CLASSES
// *************************************************************************************************

    static class Appender
    implements Runnable
    {
    private final StringBuilder         line    = new StringBuilder(1000);                                              // current line being assembled
    private final List<String>          lines   = new ArrayList<String>();                                              // lines waiting to be appended
    private final LinkedList<Integer>   lengths = new LinkedList<Integer>();                                            // lengths of each line within text area

    private final JTextArea             textArea;
    private final int                   maxLines;                                                                       // maximum lines allowed in text area
    private final Pattern               rmvPattern;

    private boolean                     clear;
    private boolean                     queue;
    private boolean                     wrapped;

    Appender(JTextArea txtara, int maxlin, Pattern rmvptn) {
        textArea    = txtara;
        maxLines    = maxlin;
        rmvPattern  = rmvptn;

        clear       = false;
        queue       = true;
        wrapped     = false;
        }

    synchronized void append(String val) {
        boolean eol                     = val.endsWith(EOL1) || val.endsWith(EOL2);

        line.append(val);
        while(line.length()>LINE_MAX) {
            emitLine(line.substring(0,LINE_MAX)+EOL1);
            line.replace(0,LINE_MAX,"[>>] ");
            }
        if(eol) {
            emitLine(line.toString());
            line.setLength(0);
            }
        }

    private void emitLine(String lin) {
        if(lines.size()>10_000) {
            lines.clear();
            lines.add("<console-overflowed>\n");
            }
        else {
            if(rmvPattern!=null) { lin = rmvPattern.matcher(lin).replaceAll(""); }
            lines.add(lin);
            }
        if(queue) {
            queue=false;
            EventQueue.invokeLater(this);
            }
        }

    synchronized void clear() {
        clear = true;
        if(queue) { queue = false; EventQueue.invokeLater(this); }
        wrapped = false;
        }

    // MUST BE THE ONLY METHOD THAT TOUCHES textArea!
    public synchronized void run() {
        int                             don = 0;

        if(clear) {
            lengths     . clear();
            lines       . clear();
            textArea    . setText("");
            clear       = false;
            }

        for(String lin: lines) {
            don += 1;
            lengths.addLast(lin.length());
            if(lengths.size()>=maxLines) { textArea.replaceRange("",0,lengths.removeFirst()); }
            textArea.append(lin);
            if(don>=100) { break; }
            }
        if(don==lines.size()) {
            lines.clear();
            queue = true;
            }
        else {
            lines.subList(0,don).clear();
            EventQueue.invokeLater(this);
            }
        }

    static private final String         EOL1        = "\n";
    static private final String         EOL2        = System.getProperty("line.separator",EOL1);
    static private final int            LINE_MAX    = 1000;
}

这是它的实际截图:

在此处输入图像描述

Here's a functioning class. You can install an instance of this into the system out and err using:

PrintStream con=new PrintStream(new TextAreaOutputStream(...));
System.setOut(con);
System.setErr(con);

Updated 2014-02-19: To use EventQueue.invokeLater() to avoid GUI threading issues which can crop up very rarely with the original.

Updated 2014-02-27: Better implementation

Updated 2014-03-25: Correct recording & deletion of lines in text area to be within the run() method to avoid race-condition between appending and deleting which can happen if the console is flooded with output. The end result seems cleaner to me, as well.

Updated 2022-11-07: Further improve implementation to completely eliminate problems from flooding with output. Note that this version will outright suppress output (with a one-line note) when it is overwhelmed with output. Previous version would eventually begin GC thrashing and the VM response would largely freeze (without actually crashing) until it finally managed to catch up.

import java.awt.*;
import java.io.*;
import java.util.*;
import java.util.regex.*;
import java.util.List;
import javax.swing.*;

public class TextAreaOutputStream
extends OutputStream
{

// *************************************************************************************************
// INSTANCE PROPERTIES
// *************************************************************************************************

private byte[]                          oneByte;                                                    // array for write(int val);
private Appender                        appender;                                                   // most recent action

// *************************************************************************************************
// INSTANCE CONSTRUCTORS/INIT/CLOSE/FINALIZE
// *************************************************************************************************

public TextAreaOutputStream(JTextArea txtara) {
    this(txtara,1000);
    }

public TextAreaOutputStream(JTextArea txtara, int maxlin) {
    this(txtara,maxlin,null);
    }

public TextAreaOutputStream(JTextArea txtara, int maxlin, Pattern rmvptn) {
    if(maxlin<1) { throw new IllegalArgumentException("TextAreaOutputStream maximum lines must be positive (value="+maxlin+")"); }
    oneByte=new byte[1];
    appender=new Appender(txtara,maxlin,rmvptn);
    }

// *************************************************************************************************
// INSTANCE METHODS - ACCESSORS
// *************************************************************************************************

/** Clear the current console text area. */
public synchronized void clear() {
    if(appender!=null) { appender.clear(); }
    }

// *************************************************************************************************
// INSTANCE METHODS - OUTPUT STREAM IMPLEMENTATION
// *************************************************************************************************

public synchronized void close() {
    appender=null;
    }

public synchronized void flush() {
    }

public synchronized void write(int val) {
    oneByte[0]=(byte)val;
    write(oneByte,0,1);
    }

public synchronized void write(byte[] ba) {
    write(ba,0,ba.length);
    }

public synchronized void write(byte[] ba,int str,int len) {
    if(appender!=null) { appender.append(bytesToString(ba,str,len)); }
    }

// *************************************************************************************************
// INSTANCE METHODS - UTILITY
// *************************************************************************************************

@edu.umd.cs.findbugs.annotations.SuppressWarnings("DM_DEFAULT_ENCODING")
static private String bytesToString(byte[] ba, int str, int len) {
    try { return new String(ba,str,len,"UTF-8"); } catch(UnsupportedEncodingException thr) { return new String(ba,str,len); } // all JVMs are required to support UTF-8
    }

// *************************************************************************************************
// STATIC NESTED CLASSES
// *************************************************************************************************

    static class Appender
    implements Runnable
    {
    private final StringBuilder         line    = new StringBuilder(1000);                                              // current line being assembled
    private final List<String>          lines   = new ArrayList<String>();                                              // lines waiting to be appended
    private final LinkedList<Integer>   lengths = new LinkedList<Integer>();                                            // lengths of each line within text area

    private final JTextArea             textArea;
    private final int                   maxLines;                                                                       // maximum lines allowed in text area
    private final Pattern               rmvPattern;

    private boolean                     clear;
    private boolean                     queue;
    private boolean                     wrapped;

    Appender(JTextArea txtara, int maxlin, Pattern rmvptn) {
        textArea    = txtara;
        maxLines    = maxlin;
        rmvPattern  = rmvptn;

        clear       = false;
        queue       = true;
        wrapped     = false;
        }

    synchronized void append(String val) {
        boolean eol                     = val.endsWith(EOL1) || val.endsWith(EOL2);

        line.append(val);
        while(line.length()>LINE_MAX) {
            emitLine(line.substring(0,LINE_MAX)+EOL1);
            line.replace(0,LINE_MAX,"[>>] ");
            }
        if(eol) {
            emitLine(line.toString());
            line.setLength(0);
            }
        }

    private void emitLine(String lin) {
        if(lines.size()>10_000) {
            lines.clear();
            lines.add("<console-overflowed>\n");
            }
        else {
            if(rmvPattern!=null) { lin = rmvPattern.matcher(lin).replaceAll(""); }
            lines.add(lin);
            }
        if(queue) {
            queue=false;
            EventQueue.invokeLater(this);
            }
        }

    synchronized void clear() {
        clear = true;
        if(queue) { queue = false; EventQueue.invokeLater(this); }
        wrapped = false;
        }

    // MUST BE THE ONLY METHOD THAT TOUCHES textArea!
    public synchronized void run() {
        int                             don = 0;

        if(clear) {
            lengths     . clear();
            lines       . clear();
            textArea    . setText("");
            clear       = false;
            }

        for(String lin: lines) {
            don += 1;
            lengths.addLast(lin.length());
            if(lengths.size()>=maxLines) { textArea.replaceRange("",0,lengths.removeFirst()); }
            textArea.append(lin);
            if(don>=100) { break; }
            }
        if(don==lines.size()) {
            lines.clear();
            queue = true;
            }
        else {
            lines.subList(0,don).clear();
            EventQueue.invokeLater(this);
            }
        }

    static private final String         EOL1        = "\n";
    static private final String         EOL2        = System.getProperty("line.separator",EOL1);
    static private final int            LINE_MAX    = 1000;
}

And here's a screenshot of it in action:

enter image description here

月棠 2024-07-17 00:18:20

@软件猴子:

有效! :)

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;

public class Main{
    public static void main( String [] args ) throws InterruptedException  {
        JFrame frame = new JFrame();
        frame.add( new JLabel(" Outout" ), BorderLayout.NORTH );

        JTextArea ta = new JTextArea();
        TextAreaOutputStream taos = new TextAreaOutputStream( ta, 60 );
        PrintStream ps = new PrintStream( taos );
        System.setOut( ps );
        System.setErr( ps );


        frame.add( new JScrollPane( ta )  );

        frame.pack();
        frame.setVisible( true );
        frame.setSize(800,600);

        for( int i = 0 ; i < 100 ; i++ ) {
            System.out.println( i );
            Thread.sleep( 500 );
        }
    }
}

@Sofware Monkey:

It works! :)

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;

public class Main{
    public static void main( String [] args ) throws InterruptedException  {
        JFrame frame = new JFrame();
        frame.add( new JLabel(" Outout" ), BorderLayout.NORTH );

        JTextArea ta = new JTextArea();
        TextAreaOutputStream taos = new TextAreaOutputStream( ta, 60 );
        PrintStream ps = new PrintStream( taos );
        System.setOut( ps );
        System.setErr( ps );


        frame.add( new JScrollPane( ta )  );

        frame.pack();
        frame.setVisible( true );
        frame.setSize(800,600);

        for( int i = 0 ; i < 100 ; i++ ) {
            System.out.println( i );
            Thread.sleep( 500 );
        }
    }
}
我不是你的备胎 2024-07-17 00:18:20

我知道这是一个旧线程,但事实上,我在尝试找出一种好方法时发现了它,这意味着其他人也可能会这样做。

这是一种(可能)更干净的方法来完成软件猴子发布的内容:

import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;

import javax.swing.JTextArea;

/**
 * Represents a console viewable through a <code>JTextArea</code>.
 * 
 * <p>
 *  Implementation:
 *  <code>
 *      System.setOut(new PrintStream(new Console( ... )));
 *  </code>
 *  </p>
 * 
 * @author Derive McNeill
 *
 */
public class Console extends OutputStream {

    /**
     * Represents the data written to the stream.
     */
    ArrayList <Byte> data = new ArrayList <Byte> ();

    /**
     * Represents the text area that will be showing the written data.
     */
    private JTextArea output;

    /**
     * Creates a console context.
     * @param output
     *      The text area to output the consoles text.
     */
    public Console(JTextArea output) {
        this.output = output;
    }

    /**
     * Called when data has been written to the console.
     */
    private void fireDataWritten() {

        // First we loop through our written data counting the lines.
        int lines = 0;
        for (int i = 0; i < data.size(); i++) {
            byte b = data.get(i);

            // Specifically we look for 10 which represents "\n".
            if (b == 10) {
                lines++;
            }

            // If the line count exceeds 250 we remove older lines.
            if (lines >= 250) {
                data = (ArrayList<Byte>) data.subList(i, data.size());
            }
        }

        // We then create a string builder to append our text data.
        StringBuilder bldr = new StringBuilder();

        // We loop through the text data appending it to the string builder.
        for (byte b : data) {
            bldr.append((char) b);
        }

        // Finally we set the outputs text to our built string.
        output.setText(bldr.toString());
    }

    @Override
    public void write(int i) throws IOException {

        // Append the piece of data to our array of data.
        data.add((byte) i);

        // Indicate that data has just been written.
        fireDataWritten();
    }

}

I know that this is an old thread but the fact that I found it while trying to figure out a good way of doing this means others probably will too.

Here's a (Probably) cleaner way of doing what software monkey posted:

import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;

import javax.swing.JTextArea;

/**
 * Represents a console viewable through a <code>JTextArea</code>.
 * 
 * <p>
 *  Implementation:
 *  <code>
 *      System.setOut(new PrintStream(new Console( ... )));
 *  </code>
 *  </p>
 * 
 * @author Derive McNeill
 *
 */
public class Console extends OutputStream {

    /**
     * Represents the data written to the stream.
     */
    ArrayList <Byte> data = new ArrayList <Byte> ();

    /**
     * Represents the text area that will be showing the written data.
     */
    private JTextArea output;

    /**
     * Creates a console context.
     * @param output
     *      The text area to output the consoles text.
     */
    public Console(JTextArea output) {
        this.output = output;
    }

    /**
     * Called when data has been written to the console.
     */
    private void fireDataWritten() {

        // First we loop through our written data counting the lines.
        int lines = 0;
        for (int i = 0; i < data.size(); i++) {
            byte b = data.get(i);

            // Specifically we look for 10 which represents "\n".
            if (b == 10) {
                lines++;
            }

            // If the line count exceeds 250 we remove older lines.
            if (lines >= 250) {
                data = (ArrayList<Byte>) data.subList(i, data.size());
            }
        }

        // We then create a string builder to append our text data.
        StringBuilder bldr = new StringBuilder();

        // We loop through the text data appending it to the string builder.
        for (byte b : data) {
            bldr.append((char) b);
        }

        // Finally we set the outputs text to our built string.
        output.setText(bldr.toString());
    }

    @Override
    public void write(int i) throws IOException {

        // Append the piece of data to our array of data.
        data.add((byte) i);

        // Indicate that data has just been written.
        fireDataWritten();
    }

}
热情消退 2024-07-17 00:18:20

ByteArrayOutputStream 可用于省略缓冲内容。

private void redirectConsoleTo(final JTextArea textarea) {
    PrintStream out = new PrintStream(new ByteArrayOutputStream() {
        public synchronized void flush() throws IOException {
            textarea.setText(toString());
        }
    }, true);

    System.setErr(out);
    System.setOut(out);
}

您可以绑定 ByteArrayOutputStream#reset() 到某个按钮。

private void redirectConsoleWithClearButton(final JTextArea textarea, JButton clearButton) {
    final ByteArrayOutputStream bytes = new ByteArrayOutputStream() {
        public synchronized void flush() throws IOException {
            textarea.setText(toString());
        }
    };

    clearButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            bytes.reset();
        }
    });

    PrintStream out = new PrintStream(bytes, true);

    System.setErr(out);
    System.setOut(out);
}

ByteArrayOutputStream can be used to omit buffering stuff.

private void redirectConsoleTo(final JTextArea textarea) {
    PrintStream out = new PrintStream(new ByteArrayOutputStream() {
        public synchronized void flush() throws IOException {
            textarea.setText(toString());
        }
    }, true);

    System.setErr(out);
    System.setOut(out);
}

Rather than limiting line number, you can bind ByteArrayOutputStream#reset() to some button.

private void redirectConsoleWithClearButton(final JTextArea textarea, JButton clearButton) {
    final ByteArrayOutputStream bytes = new ByteArrayOutputStream() {
        public synchronized void flush() throws IOException {
            textarea.setText(toString());
        }
    };

    clearButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            bytes.reset();
        }
    });

    PrintStream out = new PrintStream(bytes, true);

    System.setErr(out);
    System.setOut(out);
}
兰花执着 2024-07-17 00:18:20

我最近使用优秀代码提供的劳伦斯·多尔在我的一个项目中。

然而,就我而言,代码消耗了太多内存。 我通过用 JLabel 替换 JTextarea 成功地大幅减少了内存消耗。

我的内存节省搜索表明,JTextarea 内部代码往往会占用太多时间发送的实际文本。 因此,所有这些文本都无法被垃圾收集。

这是初始代码的灵活版本(用锁代替线程同步)。

JComponentOutputStream.java

import java.awt.EventQueue;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import javax.swing.JComponent;

public class JComponentOutputStream extends OutputStream {

    // *************************************************************************************************
    // INSTANCE MEMBERS
    // *************************************************************************************************

    private byte[] oneByte; // array for write(int val);
    private Appender appender; // most recent action

    private Lock jcosLock = new ReentrantLock();

    public JComponentOutputStream(JComponent txtara, JComponentHandler handler) {
        this(txtara, 1000, handler);
    }

    public JComponentOutputStream(JComponent txtara, int maxlin, JComponentHandler handler) {
        if (maxlin < 1) {
            throw new IllegalArgumentException("JComponentOutputStream maximum lines must be positive (value=" + maxlin + ")");
        }
        oneByte = new byte[1];
        appender = new Appender(txtara, maxlin, handler);
    }

    /** Clear the current console text area. */
    public void clear() {
        jcosLock.lock();
        try {
            if (appender != null) {
                appender.clear();
            }
        } finally {
            jcosLock.unlock();
        }
    }

    public void close() {
        jcosLock.lock();
        try {
            appender = null;
        } finally {
            jcosLock.unlock();
        }
    }

    public void flush() {
        // sstosLock.lock();
        // try {
        // // TODO: Add necessary code here...
        // } finally {
        // sstosLock.unlock();
        // }
    }

    public void write(int val) {
        jcosLock.lock();
        try {
            oneByte[0] = (byte) val;
            write(oneByte, 0, 1);
        } finally {
            jcosLock.unlock();
        }
    }

    public void write(byte[] ba) {
        jcosLock.lock();
        try {
            write(ba, 0, ba.length);
        } finally {
            jcosLock.unlock();
        }
    }

    public void write(byte[] ba, int str, int len) {
        jcosLock.lock();
        try {
            if (appender != null) {
                appender.append(bytesToString(ba, str, len));
            }
        } finally {
            jcosLock.unlock();
        }
    }

    static private String bytesToString(byte[] ba, int str, int len) {
        try {
            return new String(ba, str, len, "UTF-8");
        } catch (UnsupportedEncodingException thr) {
            return new String(ba, str, len);
        } // all JVMs are required to support UTF-8
    }

    // *************************************************************************************************
    // STATIC MEMBERS
    // *************************************************************************************************

    static class Appender implements Runnable {
        private final JComponent swingComponent;
        private final int maxLines; // maximum lines allowed in text area
        private final LinkedList<Integer> lengths; // length of lines within
                                                    // text area
        private final List<String> values; // values waiting to be appended

        private int curLength; // length of current line
        private boolean clear;
        private boolean queue;

        private Lock appenderLock;

        private JComponentHandler handler;

        Appender(JComponent cpt, int maxlin, JComponentHandler hndlr) {
            appenderLock = new ReentrantLock();

            swingComponent = cpt;
            maxLines = maxlin;
            lengths = new LinkedList<Integer>();
            values = new ArrayList<String>();

            curLength = 0;
            clear = false;
            queue = true;

            handler = hndlr;
        }

        void append(String val) {
            appenderLock.lock();
            try {
                values.add(val);
                if (queue) {
                    queue = false;
                    EventQueue.invokeLater(this);
                }
            } finally {
                appenderLock.unlock();
            }
        }

        void clear() {
            appenderLock.lock();
            try {

                clear = true;
                curLength = 0;
                lengths.clear();
                values.clear();
                if (queue) {
                    queue = false;
                    EventQueue.invokeLater(this);
                }
            } finally {
                appenderLock.unlock();
            }
        }

        // MUST BE THE ONLY METHOD THAT TOUCHES the JComponent!
        public void run() {
            appenderLock.lock();
            try {
                if (clear) {
                    handler.setText(swingComponent, "");
                }
                for (String val : values) {
                    curLength += val.length();
                    if (val.endsWith(EOL1) || val.endsWith(EOL2)) {
                        if (lengths.size() >= maxLines) {
                            handler.replaceRange(swingComponent, "", 0, lengths.removeFirst());
                        }
                        lengths.addLast(curLength);
                        curLength = 0;
                    }
                    handler.append(swingComponent, val);
                }

                values.clear();
                clear = false;
                queue = true;
            } finally {
                appenderLock.unlock();
            }
        }

        static private final String EOL1 = "\n";
        static private final String EOL2 = System.getProperty("line.separator", EOL1);
    }

    public interface JComponentHandler {
        void setText(JComponent swingComponent, String text);

        void replaceRange(JComponent swingComponent, String text, int start, int end);

        void append(JComponent swingComponent, String text);
    }

} /* END PUBLIC CLASS */

使用示例

JLabel console = new JLabel();
JComponentOutputStream consoleOutputStream = new JComponentOutputStream(console, new JComponentHandler() {
    private StringBuilder sb = new StringBuilder();

    @Override
    public void setText(JComponent swingComponent, String text) {
        sb.delete(0, sb.length());
        append(swingComponent, text);
    }

    @Override
    public void replaceRange(JComponent swingComponent, String text, int start, int end) {
        sb.replace(start, end, text);
        redrawTextOf(swingComponent);
    }

    @Override
    public void append(JComponent swingComponent, String text) {
        sb.append(text);
        redrawTextOf(swingComponent);
    }

    private void redrawTextOf(JComponent swingComponent) {
        ((JLabel)swingComponent).setText("<html><pre>" + sb.toString() + "</pre></html>");
    }
});

PrintStream con = new PrintStream(consoleOutputStream);
System.setOut(con);
System.setErr(con);    

// Optional: add a scrollpane around the console for having scrolling bars
JScrollPane sp = new JScrollPane( //
        console, //
        ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, //
        ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED //
);
myPanel.add(sp);

I recently use the excellent code provided by Lawrence Dol in one of my project.

However, in my case the code consumed too many memory. I managed to reduce drastically the memory comsuption by replacing JTextarea by a JLabel.

My memory saving searches showed that JTextarea internal code tends to hold the actual text sent too much time. Consequently, all this text could not be garbage collected.

Here is the flexible version of initial code (with thread synchronization replaced by locks).

JComponentOutputStream.java

import java.awt.EventQueue;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import javax.swing.JComponent;

public class JComponentOutputStream extends OutputStream {

    // *************************************************************************************************
    // INSTANCE MEMBERS
    // *************************************************************************************************

    private byte[] oneByte; // array for write(int val);
    private Appender appender; // most recent action

    private Lock jcosLock = new ReentrantLock();

    public JComponentOutputStream(JComponent txtara, JComponentHandler handler) {
        this(txtara, 1000, handler);
    }

    public JComponentOutputStream(JComponent txtara, int maxlin, JComponentHandler handler) {
        if (maxlin < 1) {
            throw new IllegalArgumentException("JComponentOutputStream maximum lines must be positive (value=" + maxlin + ")");
        }
        oneByte = new byte[1];
        appender = new Appender(txtara, maxlin, handler);
    }

    /** Clear the current console text area. */
    public void clear() {
        jcosLock.lock();
        try {
            if (appender != null) {
                appender.clear();
            }
        } finally {
            jcosLock.unlock();
        }
    }

    public void close() {
        jcosLock.lock();
        try {
            appender = null;
        } finally {
            jcosLock.unlock();
        }
    }

    public void flush() {
        // sstosLock.lock();
        // try {
        // // TODO: Add necessary code here...
        // } finally {
        // sstosLock.unlock();
        // }
    }

    public void write(int val) {
        jcosLock.lock();
        try {
            oneByte[0] = (byte) val;
            write(oneByte, 0, 1);
        } finally {
            jcosLock.unlock();
        }
    }

    public void write(byte[] ba) {
        jcosLock.lock();
        try {
            write(ba, 0, ba.length);
        } finally {
            jcosLock.unlock();
        }
    }

    public void write(byte[] ba, int str, int len) {
        jcosLock.lock();
        try {
            if (appender != null) {
                appender.append(bytesToString(ba, str, len));
            }
        } finally {
            jcosLock.unlock();
        }
    }

    static private String bytesToString(byte[] ba, int str, int len) {
        try {
            return new String(ba, str, len, "UTF-8");
        } catch (UnsupportedEncodingException thr) {
            return new String(ba, str, len);
        } // all JVMs are required to support UTF-8
    }

    // *************************************************************************************************
    // STATIC MEMBERS
    // *************************************************************************************************

    static class Appender implements Runnable {
        private final JComponent swingComponent;
        private final int maxLines; // maximum lines allowed in text area
        private final LinkedList<Integer> lengths; // length of lines within
                                                    // text area
        private final List<String> values; // values waiting to be appended

        private int curLength; // length of current line
        private boolean clear;
        private boolean queue;

        private Lock appenderLock;

        private JComponentHandler handler;

        Appender(JComponent cpt, int maxlin, JComponentHandler hndlr) {
            appenderLock = new ReentrantLock();

            swingComponent = cpt;
            maxLines = maxlin;
            lengths = new LinkedList<Integer>();
            values = new ArrayList<String>();

            curLength = 0;
            clear = false;
            queue = true;

            handler = hndlr;
        }

        void append(String val) {
            appenderLock.lock();
            try {
                values.add(val);
                if (queue) {
                    queue = false;
                    EventQueue.invokeLater(this);
                }
            } finally {
                appenderLock.unlock();
            }
        }

        void clear() {
            appenderLock.lock();
            try {

                clear = true;
                curLength = 0;
                lengths.clear();
                values.clear();
                if (queue) {
                    queue = false;
                    EventQueue.invokeLater(this);
                }
            } finally {
                appenderLock.unlock();
            }
        }

        // MUST BE THE ONLY METHOD THAT TOUCHES the JComponent!
        public void run() {
            appenderLock.lock();
            try {
                if (clear) {
                    handler.setText(swingComponent, "");
                }
                for (String val : values) {
                    curLength += val.length();
                    if (val.endsWith(EOL1) || val.endsWith(EOL2)) {
                        if (lengths.size() >= maxLines) {
                            handler.replaceRange(swingComponent, "", 0, lengths.removeFirst());
                        }
                        lengths.addLast(curLength);
                        curLength = 0;
                    }
                    handler.append(swingComponent, val);
                }

                values.clear();
                clear = false;
                queue = true;
            } finally {
                appenderLock.unlock();
            }
        }

        static private final String EOL1 = "\n";
        static private final String EOL2 = System.getProperty("line.separator", EOL1);
    }

    public interface JComponentHandler {
        void setText(JComponent swingComponent, String text);

        void replaceRange(JComponent swingComponent, String text, int start, int end);

        void append(JComponent swingComponent, String text);
    }

} /* END PUBLIC CLASS */

Sample usage

JLabel console = new JLabel();
JComponentOutputStream consoleOutputStream = new JComponentOutputStream(console, new JComponentHandler() {
    private StringBuilder sb = new StringBuilder();

    @Override
    public void setText(JComponent swingComponent, String text) {
        sb.delete(0, sb.length());
        append(swingComponent, text);
    }

    @Override
    public void replaceRange(JComponent swingComponent, String text, int start, int end) {
        sb.replace(start, end, text);
        redrawTextOf(swingComponent);
    }

    @Override
    public void append(JComponent swingComponent, String text) {
        sb.append(text);
        redrawTextOf(swingComponent);
    }

    private void redrawTextOf(JComponent swingComponent) {
        ((JLabel)swingComponent).setText("<html><pre>" + sb.toString() + "</pre></html>");
    }
});

PrintStream con = new PrintStream(consoleOutputStream);
System.setOut(con);
System.setErr(con);    

// Optional: add a scrollpane around the console for having scrolling bars
JScrollPane sp = new JScrollPane( //
        console, //
        ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, //
        ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED //
);
myPanel.add(sp);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文