Частые вызовы setText () в многопоточной программе Swing - PullRequest
1 голос
/ 10 ноября 2010

У меня есть программа Swing, в которой работа постоянно выполняется в не-Swing потоке.Часто требуется обновить JTextPane - часто много раз в секунду.Я понимаю, что setText () нужно вызывать изнутри потока диспетчеризации событий, но я не могу понять, как это сделать плавно.

Следующий минимальный полный пример настолько близокв состоянии получить его, используя пару PipedInputStream / PipedOutputStream, но это, кажется, обновляет экран только раз в секунду или около того.Я не уверен, что так долго.

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

public class TextTest extends JFrame {
    private JTextPane out = new JTextPane();
    private PipedInputStream pIn = new PipedInputStream();
    private PrintWriter pOut;

    public TextTest() {
        try {
            pOut = new PrintWriter(new PipedOutputStream(pIn));
        }
        catch (IOException e) {System.err.println("can't init stream");}

        add(new JScrollPane(out));
        setSize(500, 300);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setVisible(true);

        // Start a loop to print to the stream continuously
        new Thread() {
            public void run() {
                for (int i = 0; true; i++) {
                    pOut.println(i);
                }
            }
        }.start();

        // Start a timer to display the text in the stream every 10 ms
        new Timer(10, new ActionListener() {
            public void actionPerformed (ActionEvent evt) {
                try {
                    if (pIn.available() > 0) {
                        byte[] buffer = new byte[pIn.available()];
                        pIn.read(buffer);
                        out.setText(out.getText() + new String(buffer));
                    }
                }
                catch (IOException e) {System.err.println("can't read stream");}
            }
        }).start();
    }

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

Я неправильно это реализую?У меня просто совершенно неверное представление о том, как постоянно обновлять JTextPane вне EDT?

Ответы [ 4 ]

4 голосов
/ 10 ноября 2010

Метод setText() "является потокобезопасным, хотя большинство методов Swing - нет. Пожалуйста, смотрите Как использовать потоки для получения дополнительной информации."

Приложение: Для справки, вот некоторые другие подходы к обновлению EDT. Следует также отметить, что обработчик события действия для javax.swing.Timer выполняется в EDT. Вот мой вариант:

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

public class TextTest extends JFrame {

    private JTextArea out = new JTextArea();
    private PipedInputStream pIn = new PipedInputStream();
    private PrintWriter pOut;

    public TextTest() {
        try {
            pOut = new PrintWriter(new PipedOutputStream(pIn));
        } catch (IOException e) {
            System.err.println("can't init stream");
        }

        DefaultCaret caret = (DefaultCaret) out.getCaret();
        caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);

        add(new JScrollPane(out));
        setSize(300, 500);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setVisible(true);

        // Start a loop to print to the stream continuously
        new Thread() {

            public void run() {
                for (int i = 0; true; i++) {
                    pOut.println(i);
                }
            }
        }.start();

        // Start a timer to display the text in the stream every 10 ms
        new Timer(10, new ActionListener() {

            public void actionPerformed(ActionEvent evt) {
                try {
                    out.append(String.valueOf((char) pIn.read()));
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    public static void main(String[] args) {
        new TextTest();
    }
}
2 голосов
/ 22 октября 2012

вам нужно очистить вывод вашего printWriter, и я бы посоветовал сделать небольшую паузу в вашем потоке, учитывая его плотный цикл, чтобы позволить обновляющему потоку запускаться время от времени.

 pOut.println(i); 
 pOut.flush();
 try {
      sleep(10);
 } catch (InterruptedException e) {
 }

Это даст более плавный поток.

2 голосов
/ 10 ноября 2010

но это, кажется, обновляет экран только раз в секунду или около того.Я не уверен, что так долго.

System.out.println(pIn.available());

Я добавил приведенное выше утверждение в код actionPerformed таймера.Ничего не происходит, пока буфер не достигнет 1024 байта.Так что я думаю, вам нужно изменить размер буфера.

Кроме того, вы не должны использовать setText ().Неэффективно воссоздавать Документ каждый раз, когда вы вносите изменения.

Вы можете использовать:

out.replaceSelection(new String(buffer) );

Или более распространенный подход - использовать:

Document doc = textPane.getDocument();
doc.insertString("...", doc.getLength(), null);

Не думайте, что метод insertString () является поточно-ориентированным, но метод replaceSelection ():.

Edit:

Только что попытался поиграть с размером буфера 10 во входном потоке иочистка выходного потока, и это не имело никакого значения, поэтому я не понимаю потоковые потоки.

0 голосов
/ 10 ноября 2010

Надлежащая учебная ссылка для параллелизма и Swing выглядит следующим образом: Урок: параллелизм в Swing

@ camickr: setText не создает новый документ, он эффективно выполняетэто:

doc.replace(0, doc.getLength(), s, null);

или это:

doc.remove(0, doc.getLength());
doc.insertString(0, s, null);

Я не утверждаю, что это эффективно, однако ...

Другая вещь, которую setText делает not do вызывает выдачу revalidate() и repaint() (однако, setDocument делает).Вероятно, стоит добавить эти два вызова после вызова к setText.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...