PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : SWT-Widgets per Thread immer wieder neu zeichnen?



Technaton
30-05-2005, 15:55
Hallo,

ich bin's mal wieder mit meinem SWT. :o Dieses Mal mit folgendem Problem...

Ich lade einige Dateien per FTP hoch. Weil das ganze zusammen mit einem Logwindow, eine ProgressBar und anderen netten Spielereien läuft, habe ich jeden Upload in einen syncExec gepackt (damit nicht X Dateien gleichzeitig hochgeladen werden).
Wenn die Datei jetzt etwas größer ist, dauert es natürlich länger, bis der Thread fertig ist. Das Ergebnis ist, daß alle meine Widgets nicht mehr da sind, wenn ich das Fenster mal minimiere z.B. Ich dachte, gut, dann mach ich eben noch einen asyncExec-Thread, der alle 250ms meine Widgets neu zeichnet -- aber Pustekuchen, weder mit update() noch mit redraw() kriege ich das hin, das Programm bleibt dann stehen. Der Effekt bleibt derselbe.

Hat jemand eine Idee, wie man Widgets in SWT einfach nur neu zeichnen kann?

Gruß,
das Techl

peschmae
30-05-2005, 19:35
Äh was? Du machst die ganze Arbeit in einem Syncexec?

Kein Wunder kommt er nicht zum neuzeichnen - schliesslich wird das was du mit display-syncexec ausführst im Hauptthread ausgeführt (der deshalb nicht dazu kommt die Nachrichtenschleife abzuarbeiten und entsprechend auch nicht neu zeichent)...

Im syncexec tut nur die Widgetinhalte ändern - nicht mehr. Sonst könntest du genau so gut eine single-thread Anwendung machen ...

MfG Peschmä

Technaton
30-05-2005, 22:11
Das eigentliche Iterieren, Datei öffnen, usw., läuft in einem Extrathread. Aber wie soll ich denn das Kopieren sonst regeln, wenn ich -- je nach Datei -- noch ein Label ändern und den Fortschrittsbalnken zeichnen muß? Sonst blöckt SWT doch wieder mit einer IllegalThreadAccessException...

Technaton
31-05-2005, 10:17
Jetzt, wo ich wieder dran bin, will ich auch noch einen Codeabriß liefern. Zur Erklärung: Die Methoden summon() und die() kommen aus einer Factory (Creature ist die abstrakte Methode dazu) und werden von einer zentralen Stelle aus aufgerufen. summon() bastelt erst das Design und ruft dann den Kopierthread in einer asyncExec-Methode auf. Aus dem heraus wieder sollen die ProgressBar und das "Kopiert gerade Datei: xy"-Label aktualisiert werden.


public class Ftpcopy extends Creature {
private static ProgressBar pbar;
private static Label currentFileLabel;
// TODO replace this with some value read from some options file
private static final String sourceFileName = "source.zip";
private static ZipFile productArchive;

class Copyer extends Thread {
private ZipEntry zipentry;
private int iter = 1;
public Copyer() {
super("Copyer");
}

public synchronized void run () {
// set wait cursor
innerArea.getShell().setCursor
(new Cursor(innerArea.getDisplay(),SWT.CURSOR_WAIT));

try {
productArchive = new ZipFile(sourceFileName);
pbar.setSelection(0);
pbar.setMaximum(productArchive.size());

FtpManager.initConnection();
FtpManager.changeToDestDir();


for(Enumeration e = productArchive.entries();
e.hasMoreElements();) {
iter++;
final ZipEntry zipentry = (ZipEntry)e.nextElement();
pbar.setSelection(iter);
setCurrentFileLabelDesc(zipentry.getName());
if(zipentry.isDirectory())
FtpManager.makeDirectory(zipentry.getName());
else
FtpManager.uploadZipEntry(productArchive,zipentry) ;
}
} catch (Exception e) { // TODO DEBUG
System.err.println("Exception when uploading: "+e.toString()
+"\n- - - - - - - - -\n");
e.printStackTrace();
}

// restore layout
proceedBtn.setEnabled(true);
cancelBtn.setEnabled(true);
innerArea.getShell().setCursor
(new Cursor(innerArea.getDisplay(),SWT.CURSOR_ARROW));
}
}

/**
* Patch label together. Saves typework. :P
* @param desc
**/
private void setCurrentFileLabelDesc(String desc) {
if(desc.length() > 80) {
desc = "..."+desc.substring((desc.length()-77),desc.length());
}
currentFileLabel.setText(
WizardDialogueFactory.getP("Ftpcopy","currentfilelabeldesc")
+" "+desc
);
}

/* * *
* (non-Javadoc)
* @see de.trionic.mpi.gui.creatures.Creature#summon(de.tr ionic.mpi.gui.DialoguePage)
* * */
public void summon(DialoguePage diag) {
/* some init stuff */
innerArea = diag.innerArea;
proceedBtn = diag.submitBtn;
cancelBtn = diag.cancelBtn;
proceedBtn.setEnabled(false);
cancelBtn.setEnabled(false);
/* end init stuf */

/* BEGIN layout creation */
// ... SWT layout blabla...
/* END layout creation */

/* BEGIN copy */
innerArea.getDisplay().asyncExec(new Copyer());
/* END copy */
}

/* (non-Javadoc)
* @see de.trionic.mpi.gui.creatures.Creature#die()
*/
public void die() {
FtpManager.closeConnection();
}
}

Technaton
31-05-2005, 10:47
Ok, ich habe jetzt verstanden, was du mit deiner Aussage meintest. :rolleyes: Ich hatte etwas anderes angenommen.

Ich habe jetzt das
innerArea.getDisplay().asyncExec(new Copyer()); durch
Copyer copy = new Copyer();
copy.start(); ersetzt, und innerhalb der Copyer-Klasse die Methoden, die was am Dialog ändern, in eine syncExec gepackt. Jetzt geht das auch, und das war wahrscheinlich auch, was du meintest.

Kleine Frage zum Schluß: Aufgrund der Architektur des Programmes muß ich häufig mit anderen Threads arbeiten, die am GUI selbst was ändern müssen. Jedes Mal eine anonyme Threadklasse dafür zu schreiben ist etwas mühsam, deswegen dachte ich mir, ich implementiere eine Methode, der ich eine andere Methode als Parameter übergebe. Den Parameter führe ich dann in einem sync oder asyncExec aus. Ich bin jetzt soweit zum Rumpf gekommen:
public void execSwtFromOutside(final Method method) {
innerArea.getDisplay().asyncExec(new Runnable() {
public void run () {
try {
method.invoke(this,null);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}, aber wie übergebe ich eine Methode? Von Perl bin ich etwas a la execSwtFromOutside( { .... } ); gewohnt, aber das geht wohl nicht. execSwtFromOutside(new Method() { ... }); funktioniert auch nicht, also wie heißt der korrekte Aufruf?!

peschmae
31-05-2005, 18:05
copy.start();[/code] ersetzt, und innerhalb der Copyer-Klasse die Methoden, die was am Dialog ändern, in eine syncExec gepackt. Jetzt geht das auch, und das war wahrscheinlich auch, was du meintest.

Genau. Wenn du was mit asyncExec() ausführst ist das nämlich kein Thread. (Der Methodenname ist insofern etwas unklar - aber die Beschreibung hingegen schon).



Kleine Frage zum Schluß: Aufgrund der Architektur des Programmes muß ich häufig mit anderen Threads arbeiten, die am GUI selbst was ändern müssen.

Das kommt mir bekannt vor...


Jedes Mal eine anonyme Threadklasse dafür zu schreiben ist etwas mühsam, deswegen dachte ich mir, ich implementiere eine Methode, der ich eine andere Methode als Parameter übergebe.

Funktionspointer gibts eben nicht in Java. Leider - das wäre ja genau das was du hier brauchen würdest. Darum geht das nicht. Das ist imo einer der Nachteile von Java - das ständige Implementieren von anonymen Klassen als Listener/Thread oder sonstwas ist mühsam und unübersichtlich.

Falls du trotzdem irgend eine brauchbare einfache Lösung finden solltest würde mich das natürlich freuen ;)

MfG Peschmä