PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : [SWT] non-gui-Thread + Gui wird nur aktualisiert wenn Fenster aktiv



AceTheFace
21-05-2006, 22:28
Hallo,

versucht gerade wieder etwas fitter in Java zu werden und wollte zum warm werden einen kleinen IRC-Client mit SWT schreiben. Soweit so gut. Mein Problem im Moment ist, dass ich nicht genau weiss wie ich am besten das Chatfenster aktualisiere. Im Moment habe ich eine Connection-Klasse, die das Interface Runnable implementiert, und sobald am InputStream etwas anliegt (in.ready()) es eine Variable auf true schält.
In meiner GUI-Klasse überwache ich innerhalb des Eventloops eben jene Variable und sobald sie auf true steht, hole ich mir die neue Nachricht von der Verbindung.
Das klappt eigentlich auch, allerdings erscheinen die neuen Nachrichten erst, wenn ich mit der Maus über meinem Chatfenster bin oder dieses aktiv ist.
Das ist nat. etwas nervig, da der chat auch im Hintergrund aktualisiert werden sollte.

Wo ist mein Denkfehler?

GUI-Klasse:


import org.eclipse.swt.*;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.custom.StyledText;

public class MainWindow{
private Display display;
private Shell shell;
private StyledText textArea;
private List nickList;
private Connection conn;
private Text input;
private Button btnSend;

MainWindow() {
display = new Display();
shell = new Shell(display);
shell.setText("JIRC - SWT-based Java IRC client");
conn = new Connection();
createGUI();
nickList.add("AceTheFace");
shell.open();
while(!shell.isDisposed()) {
if(!display.readAndDispatch())
display.sleep();
if(conn.hasNewMessages()) {
String text = conn.getMessage();
if(text!="")
textArea.append(text+System.getProperty("line.separator"));
}
}


}

private void createGUI() {
GridLayout layout = new GridLayout();
layout.numColumns = 2;
shell.setLayout(layout);
GridData g1 = new GridData(GridData.FILL_BOTH);
GridData g2 = new GridData(SWT.END);
GridData g3 = new GridData(GridData.FILL_HORIZONTAL);
GridData g4 = new GridData();
//create TextArea
textArea = new StyledText(shell,SWT.FULL_SELECTION|SWT.READ_ONLY) ;
textArea.setCursor(null);
textArea.setToolTipText("Gelaber im Channel");
textArea.setLayoutData(g1);
//create NickList
nickList = new List(shell,SWT.SINGLE);
nickList.setToolTipText("Leute im Channel");
nickList.setLayoutData(g2);
//create InputField
input = new Text(shell, SWT.SINGLE);
input.setToolTipText("Gib' hier deinen Text ein...");
input.setLayoutData(g3);
//create sendButton
btnSend = new Button(shell,SWT.PUSH);
btnSend.setText("Senden");
btnSend.setToolTipText("Klicken zum Abschicken");
btnSend.setLayoutData(g4);
}

}


Connection-Klasse:


import java.io.*;
import java.net.*;

public class Connection implements Runnable {

private Socket socket = null;
private PrintWriter out = null;
private BufferedReader in = null;
private boolean newMessages;
private String nick;
private String realName;

Connection() {
nick = "AceTheFklace";
realName = "T.H.";
try {
socket = new Socket("localhost",6667);
out = new PrintWriter(socket.getOutputStream(),true);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
} catch(UnknownHostException e) {
System.err.println("Unknown Host...");
System.exit(1);
} catch(IOException e) {
System.err.println("I/O-Error :(");
System.exit(1);
}
sendMessage("USER "+realName+" * * "+realName);
sendMessage("NICK "+nick);
new Thread(this).start();
}

public boolean hasNewMessages() {
return newMessages;
}

public String getMessage() {
String text;
try{
if(in.ready()) {
text = in.readLine();
newMessages=false;
return text;
}
else return "";
} catch(IOException e) {
System.err.println("IO-Error: "+e);
System.exit(1);
return null;
}
}

public void run() {
try {
while(true) {
if(in.ready())
newMessages = true;
Thread.sleep(100);
}
} catch(IOException e) {
System.err.println("IO-Error: "+e);
System.exit(1);
} catch(InterruptedException e) {
System.err.println("uh oh"+e);
System.exit(1);
}
}

public void sendMessage(String msg) {
out.append(msg+System.getProperty("line.seperator"));
System.out.println(msg);
out.flush();
}

public void close() {
try {
in.close();
out.close();
} catch(IOException e) {
System.err.println("IOException: "+e);
System.exit(1);
}

}

}


Gruß und danke,
Ace

peschmae
22-05-2006, 06:17
Das Problem ist wohl dass



display.sleep();


so lange wartet bis es Events zu verarbeiten gibt. Und das ist dann erst der Fall wenn da was neu gezeichnet werden muss oder so - oder das Fenster wurde aktiviert...

Am besten verwendest du um sowas zu erreichen die Funktion Display.asyncExec() - also in Connection sowas ähnliches:


if(in.ready())
// text lesen
//...
display.asyncExec(new Runnable() {
public void run() {
textArea.append(text+System.getProperty("line.separator"));
}
}
);


Der Code da funktioniert wohl nicht 100%ig - meine SWT-Zeit ist schon etwas her.

MfG Peschmä

AceTheFace
22-05-2006, 07:42
Hm, eigentlich wollte ich alles GUI-Technische aus so Sachen wie der Connection-Klasse raushalten.
Aber ich werds nachher trotzdem mal probieren, danke.

Gruß,
Ace

Demo
23-05-2006, 10:29
Und, klappts ?

AceTheFace
23-05-2006, 14:49
Ich hab' das jetzt so gelöst:



while(!shell.isDisposed()) {
//neue Nachrichten ausspucken
display.asyncExec(new Runnable() {
public void run() {
if(conn.hasNewMessages()) {
String text = conn.getMessage();
if(text!="") {
textArea.append(text+System.getProperty("line.separator"));
textArea.setTopIndex(textArea.getLineCount()-1);
}
}
}
});
if(!display.readAndDispatch())
display.sleep();
}

Also direkt in der Eventloop. So habe ich dann immernoch die Verbindung von der GUI getrennt.

Aber ich hab schon wieder ein Problem (http://www.mrunix.de/forums/showthread.php?p=204410) :)

Gruß,
Ace

peschmae
23-05-2006, 16:35
Ich hab auch nicht mehr ganz 100%ig im Kopf wie das ganze läuft - aber bist du sicher dass das nicht den Eventloop in den Wahnsinn treibt indem es immer neue Events generiert - quasi im Eventloop selber?

MfG Peschmä

AceTheFace
23-05-2006, 17:48
Ich hab auch nicht mehr ganz 100%ig im Kopf wie das ganze läuft - aber bist du sicher dass das nicht den Eventloop in den Wahnsinn treibt indem es immer neue Events generiert - quasi im Eventloop selber?

MfG Peschmä

Na ja, kommt mir auch irgendwie komisch vor...aber es funktioniert bestens, und das ist ja eigentlich die Hauptsache :)
Aber wenn du noch'ne andere (sauberere) Lösung, immer her mit. Nur bitte nichts in der Connection-Klasse ;)

Gruß,
Ace

peschmae
23-05-2006, 18:12
Du könntest - das treibt den Aufwand natürlich etwas in die Höhe - in der Connection Klasse ein Listener-Ding implementieren.
D.h. von deinem Hauptprogramm aus registrierst du dann bei deiner Connection-Klasse einen "ConnectionTextAvailableListener" in dessen Implementierung du dann das asyncExec() reintust.

So Zeugs hab ich zumindest recht oft gemacht. Damals. Allerdings wusste ich nie so sicher wie die "beste" Implementierung für so eine Listenerverwaltung (hab dann jeweils ArrayLists genommen - mit der trimToSize()-Methode einigermassen Speichersparsam) aussehen würde deshalb war die jedes Mal etwas anders

MfG Peschmä