[英]AWTEvent and EventQueue
我有一個外部設備,一次向我發送1個字符的數據。 我將其寫到JTextPane上的StyledDocument中。 此數據是在不是AWT線程的線程上發送給我的,因此我需要創建AWTEvents並將它們推送到EventQueue,以便AWT處理編寫過程,以便不會出現異常。
我現在有一個有趣的問題...
我的文本向后打印到文檔。
這顯然是因為我在收到字符時將它們一次推入事件隊列1。 隊列顯然是最先被推送的。 我正在嘗試一種方法,可以在添加新事件或類似事件之前觸發事件,以便可以按順序觸發事件。
http://www.kauss.org/Stephan/swing/index.html是我用來創建事件的示例。
private class RUMAddTextEvent extends AWTEvent {
public static final int EVENT_ID = AWTEvent.RESERVED_ID_MAX + 1;
private int index;
private String str;
private AttributeSet as;
public RUMAddTextEvent(Component target, int index, String str, AttributeSet as) {
super(target, EVENT_ID);
this.index = index;
this.str = str;
this.as = as;
}
public int getIndex() {
return index;
}
public String getStr() {
return str;
}
public AttributeSet getAs() {
return as;
}
}
private class RUMRemoveTextEvent extends AWTEvent {
public static final int EVENT_ID = AWTEvent.RESERVED_ID_MAX + 1;
int index;
int size;
RUMRemoveTextEvent(Component target, int index, int size) {
super(target, EVENT_ID);
this.index = index;
this.size = size;
}
public int getIndex() {
return index;
}
public int getSize() {
return size;
}
}
/**
* Prints a character at a time to the RUMComm window.
*
* @param c
*/
public void simpleOut(Character c) {
cursor.x++;
if (lines.isEmpty()) {
this.lines.add(c.toString());
} else {
this.lines.add(cursor.y, this.lines.get(cursor.y).concat(c.toString()));
this.lines.remove(cursor.y + 1);
}
try {
//doc.insertString(doc.getLength(), c.toString(), as);
eventQueue = Toolkit.getDefaultToolkit().getSystemEventQueue();
eventQueue.postEvent(new RUMAddTextEvent(this, doc.getLength(), c.toString(), as));
getCaret().setDot(doc.getLength());
} catch (Exception ex) {
//Exceptions.printStackTrace(ex);
}
}
/**
* Creates a new line
*/
public void newLine() {
cursor.y++;
cursor.x = 0;
this.lines.add("");
//doc.insertString(doc.getLength(), "\n", null);
eventQueue = Toolkit.getDefaultToolkit().getSystemEventQueue();
eventQueue.postEvent(new RUMAddTextEvent(this, doc.getLength(), "\n", null));
getCaret().setDot(doc.getLength());
}
/**
* Backspace implementation.
*
*/
public void deleteLast() {
int endPos = doc.getLength();
//doc.remove(endPos - 1, 1);
eventQueue = Toolkit.getDefaultToolkit().getSystemEventQueue();
eventQueue.postEvent(new RUMRemoveTextEvent(this, endPos - 1, 1));
cursor.x--;
}
@Override
protected void processEvent(AWTEvent awte) {
//super.processEvent(awte);
if (awte instanceof RUMAddTextEvent) {
RUMAddTextEvent ev = (RUMAddTextEvent) awte;
try {
doc.insertString(ev.getIndex(), ev.getStr(), ev.getAs());
} catch (BadLocationException ex) {
Exceptions.printStackTrace(ex);
}
} else if (awte instanceof RUMRemoveTextEvent) {
RUMRemoveTextEvent ev = (RUMRemoveTextEvent) awte;
try {
doc.remove(ev.getIndex(), ev.getSize());
} catch (BadLocationException ex) {
Exceptions.printStackTrace(ex);
}
} else {
super.processEvent(awte);
}
}
我只能鼓勵嘗試照顧Swing線程規則的人員。 但是,盡管您努力創建事件並將其推送到EventQueue
上,但仍需要調用后台文件的長度和插入符號的位置來訪問后台Thread
上的Swing組件。
擴展文本組件以在其上設置文本對我來說似乎有點過頭了,當然也不是解決此問題的最佳方法。 就個人而言,我會讓后台Thread
偶爾填充一次緩沖區,並將該緩沖區刷新到文本文檔中(例如,在每個新行中,使用Timer每秒兩次,每次到達1000個字符時,...)。 對於更新,我將只使用SwingUtilities.invokeLater
。
從我的一個舊項目中檢索到的一些示例代碼對此進行了說明。 它不會編譯,但可以說明我的觀點。 該類將String
追加到應在EDT上訪問的ISwingLogger
上。 請注意javax.swing.Timer
的用法,以便在EDT上進行定期更新。
import javax.swing.Timer;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class SwingOutputStream extends ByteArrayOutputStream{
private final ISwingLogger fSwingLogger;
private Timer fTimer;
private final StringBuilder fBuffer = new StringBuilder( 1000 );
public SwingOutputStream( ISwingLogger aSwingLogger ) {
fSwingLogger = aSwingLogger;
fTimer = new Timer( 200, new ActionListener() {
public void actionPerformed( ActionEvent aActionEvent ) {
flushBuffer();
}
} );
fTimer.setRepeats( false );
}
@Override
public void flush() throws IOException {
synchronized( fBuffer ){
fBuffer.append( toString( "UTF-8") );
}
if ( fTimer.isRunning() ){
fTimer.restart();
} else {
fTimer.start();
}
super.flush();
reset();
}
private void flushBuffer(){
synchronized ( fBuffer ){
final String output = fBuffer.toString();
fSwingLogger.appendString( output );
fBuffer.setLength( 0 );
}
}
}
您的實際問題不是事件被無序處理,而是您正在使用即將過期的信息來創建事件:
new RUMAddTextEvent(this, doc.getLength(), ...
提交事件時,文檔的長度可能為0,但在事件處理時可能不正確。 您可以通過使用AppendTextEvent而不是指定索引來解決該問題,但是如果您也有基於位置的插入,則必須考慮這些問題。
整個批次的另一種選擇是使用無效模型:
// in data thread
void receiveData(String newData) {
updateModelText(newData);
invalidateUI();
invokeLater(validateUI);
}
// called in UI thread
void validateUI() {
if (!valid) {
ui.text = model.text;
}
valid = true;
}
恕我直言,您已經采取了一些本來應該簡單的事情,但后來又變得更加復雜(順便說一句,我是這方面的真正主人;)
不知道確切的問題,很難做到100%,但是這個小例子說明了我如何使用SwingUtilities.invokeLater/invokeAndWait
處理相同的問題
public class EventTest {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setLayout(new BorderLayout());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 400);
JTextArea area = new JTextArea();
frame.add(new JScrollPane(area));
frame.setLocationRelativeTo(null);
frame.setVisible(true);
Thread thread = new Thread(new Dispatcher(area));
thread.setDaemon(true);
thread.start();
}
public static class Dispatcher implements Runnable {
private String message = "Hello from the other side\n\nCan you read this\n\nAll done now";
private JTextArea area;
public Dispatcher(JTextArea area) {
this.area = area;
}
@Override
public void run() {
int index = 0;
while (index < message.length()) {
try {
Thread.sleep(250);
} catch (InterruptedException ex) {
}
send(message.charAt(index));
index++;
}
}
public void send(final char text) {
System.out.println("Hello from out side the EDT - " + EventQueue.isDispatchThread());
try {
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
System.out.println("Hello from the EDT - " + EventQueue.isDispatchThread());
area.append(new String(new char[]{text}));
}
});
} catch (Exception exp) {
exp.printStackTrace();
}
}
}
}
在您的情況下,我可能會制作一系列可運行的設備,它們能夠處理您要進行的每個更新。
例如,我可以創建一個InsertRunnable
和DeleteRunnable
(從您的示例代碼中獲得),它們可以在當前位置插入一個字符串,或從當前插入符號位置刪除一個/多個字符串。
對於這些,我將傳遞所需的信息,然后讓他們負責其余的工作。
...當我收到它們時。 隊列顯然是最先被推送的。 我試圖...
您在這里有一些概念上的錯誤。 隊列是FIFO ,意味着首先彈出的隊列。 堆棧將是LIFO ,這意味着最后彈出的堆棧將首先彈出。 我相信這就是您真正的問題所在。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.