我有一个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();
}
}
我是否实施了这个错误?关于如何从EDT外部不断更新JTextPane我是否完全错误?
答案 0 :(得分:4)
setText()
“方法是线程安全的,但大多数Swing方法都不是。请参阅How to Use Threads以获取更多信息。”
附录:作为参考,这里有一些other approaches来更新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();
}
}
答案 1 :(得分:2)
但这似乎只是每秒钟更新一次屏幕。我不确定这花了多长时间。
System.out.println(pIn.available());
我将上述语句添加到Timer的actionPerformed代码中。在缓冲区达到1024字节之前没有任何反应。所以我猜你需要改变缓冲区大小。
此外,您不应该使用setText()。每次进行更改时重新创建文档都是低效的。
您可以使用:
out.replaceSelection(new String(buffer) );
或者更常见的方法是使用:
Document doc = textPane.getDocument();
doc.insertString("...", doc.getLength(), null);
不要认为insertString()方法是线程安全的,但是replaceSelection()方法是。
编辑:
尝试在输入流中使用缓冲区大小为10并刷新输出流并且它没有任何区别,所以我想我不理解管道流。
答案 2 :(得分:2)
你需要刷新printWriter的输出,并且我建议在你的线程中暂停一下,因为它有一个紧密的for循环,让更新线程偶尔启动。
pOut.println(i);
pOut.flush();
try {
sleep(10);
} catch (InterruptedException e) {
}
这将使流程更顺畅。
答案 3 :(得分:0)
并发和Swing的正确教程链接看起来就在这里:Lesson: Concurrency in Swing
@camickr:setText
没有创建新文档,它有效地做到了这一点:
doc.replace(0, doc.getLength(), s, null);
或者这个:
doc.remove(0, doc.getLength());
doc.insertString(0, s, null);
我并没有声称它效率很高......
setText
做
revalidate()
和repaint()
被发出(setDocument
会这样做)。在调用setText
之后添加这两个调用可能是值得的。