PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Java Console in SWT Text Widget



teamasta
24-01-2007, 15:43
Hallo,
ich brauche eine SWT Klasse, die nicht blockierend ist (Grund folgt).
Der Output von System.out und System.err soll in ein Text Widget in dieser Klasse angezeigt werden. Die Klasse darf nicht blockieren, weil das Hauptprogramm weiterlaufen und die Outputs per SWT ausgeben soll.

D.h. mit

OutputWindow owindow = new OutputWindow(); soll eine SWT Shell mit Text Widget erzeugt werden, was dann den Output anzeigt.

Mit googlen habe ich bisher keine Lösungen finden können.
http://www.comweb.nl/java/Console/Console.html benutzt Swing, aber ich habe es nicht auf SWT ändern können. Kennt jemand eine schöne Lösung mit SWT Widgets?

Wenn ich

Display display = new Display ();
Shell shell = new Shell(display);
shell.open ();
while (!shell.isDisposed ()) {
if (!display.readAndDispatch ()) display.sleep ();
}
display.dispose ();

in einen Thread packe (wegen nicht blockieren der Shell), funktioniert es auch nicht.

Vielleicht hat jemand einen kleinen Hinweis.

Danke
Jens

peschmae
24-01-2007, 16:37
Also du willst ein externes Programm ausführen und dessen Output in SWT ausgeben, hab ich das richtig verstanden?

In dem Fall Packst du einfach alles was das externe Programm betrifft in einen eigenen Thread. Wenn du aus diesem Thread dann etwas an der Gui ändern musst (z.B. Text im Widget anfügen) dann benutzt du dazu Display.asyncExec() bzw. Display.syncExec(), aufgerufen von deinem Thread aus.
Deren Funktion hab ich schon ein paar mal anderen Leuten hier im Forum zu erklären versucht. ;)

MfG Peschmä

teamasta
24-01-2007, 16:50
also, ich habe z.b. eine main Funktion


public static void main()
{
...hier läuft viel ab
... noch mehr Code

OutputWindow owindow = new OutputWindow();

System.out.println("Beispielausgabe"); <- erscheint im OutputWindow
.. mehr Code
}


es soll quasi die gesamte Standardausgabe umgelenkt werden.

peschmae
24-01-2007, 17:21
Also die Ausgabe deines eigenen Programms? Wieso benutzt du dann System.out.println und schreibst das Zeugs nicht einfach direkt in das outputwindow? Was genau darf nicht blockieren?

MfG Peschmä

Waxolunist
25-01-2007, 13:37
Jetzt habe ich es gefunden. Ich denke, ich habe schon mal so etwas geschrieben, was du suchst.

Es ist ein sehr einfacher StatusMessenger, aus meinem SWTUtils-Projekt. Es ist nicht sehr ausgereift, und nicht gut Konfigurierbar. Aber mit wenigen Anpassungen im Code, läßt sich einiges daraus machen. Er verwendet die von pischmae genannte Methode asyncExec().



package at.sterzl.swt;

import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;

public class StatusMessenger extends Thread {

private static String MESSAGE = "";

private static Label _statusLabel = null;

private static StatusMessenger _messenger = null;

private static int _counter = 0;

private static Display _display = null;

private static boolean messageChanged = false;

private StatusMessenger() {
super();
}

public static void instantiate(Label statusLabel, Display display) {
setLabel(statusLabel);
setDisplay(display);
}

private static void setLabel(Label statusLabel) {
_statusLabel = statusLabel;
}

private static void setDisplay(Display display) {
_display = display;
}

public static StatusMessenger getInstance() {
if (_messenger == null) {
_messenger = new StatusMessenger();
}
return _messenger;
}

public static void setMessage(String msg) {
if (_statusLabel != null) {
MESSAGE = msg;
}
_counter = 0;
messageChanged = true;
}

public void run() {
while (true) {
if (messageChanged) {
if (!_display.isDisposed()) {
_display.asyncExec(new TextSetter(MESSAGE));
messageChanged = false;
}
}
if (_counter == 10) {
setMessage("");
}
try {
Thread.sleep(500);
} catch (InterruptedException ex) {
setMessage(ex.getMessage());
}
_counter++;
}
}

private class TextSetter extends Thread {

private String message = "";

TextSetter(String msg) {
message = msg;
}

public void run() {
if (!_statusLabel.isDisposed()) {
synchronized (_statusLabel) {
_statusLabel.setText(message);
}
}
}
}
}


Du kannst den StatusMessenger in ein Label einbinden mit folgendem Code:



StatusMessenger.instantiate(_statusLabel, SWTUtil.getDisplay());
_statusMessenger = StatusMessenger.getInstance();
_statusMessenger.setDaemon(true);
_statusMessenger.start();


_statusLabel ist org.eclipse.swt.widgets.Label;
Dazu musst du noch das display übergeben.

Statt eines Labels kannst du jedes andere Widget nehmen und statt der Methode setText kannst du auch Text appenden. Dann musst du allerdings schauen, dass der Text nicht wieder verschwindet nach 5 Sekunden.

Ich hab unter dem Link unten mein Projekt aus Eclipse in ein Zip exportiert.
Da siehst du das mal in Action, wenn du die Klasse GUIMain startest.


http://sterzl.dyndns.org/SWTUtil.zip (http://sterzl.dyndns.org/SWTUtil.zip)

Es steht dir frei den Code zu verändern.

mfg, Christian

teamasta
26-01-2007, 14:26
@peschmae
Das Programm habe ich nicht selbst geschrieben. Ich soll es nur erweitern. Alle Ausgaben kommen bisher per System.out.println() auf die Console. Zusätzlich soll eine grafische Ausgabe möglich sein.

Die Oberfläche soll nicht blockieren, weil das eigentliche Programm ja weiterarbeiten soll.

@Waxolunist
Vielen Dank für deine Antwort. Ich werde es morgen testen.

gruß
jens

teamasta
12-02-2007, 16:58
Hallo,
ich habe es bisher noch nicht hinbekommen. Hier etwas konkreten Code um es etwas klarer zu machen.

Hauptthread:

public class Programm {
public static void main(String[] args) {
.....
.....
new Thread(new OutputWindow()).start();
...
...
System.out.println("Dieser Text soll ins OutputFenster kommen!");
...
}
}

OutputWindow:

import java.io.PrintStream;

import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;

public class OutputWindow implements Runnable {
private Display display;

private Shell shell;

private Text textArea;

public void run() {
display = new Display();
shell = new Shell(display, SWT.DIALOG_TRIM);
// System.out.println("Constructor");
shell.setText("U2Q Output");
shell.setSize(new Point(872, 486));
shell.setLayout(new FillLayout());
textArea = new Text(shell, SWT.MULTI | SWT.WRAP | SWT.V_SCROLL);
shell.open();

PrintStream backupSystemOutStream = System.out;
System.setOut(new PrintStream(backupSystemOutStream) {
public void println(final String s) {
display.asyncExec(new Runnable() {
public void run() {
textArea.append(s);
textArea.append("\n");
}
});
}
});

while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}
}


Was kann ich machen ? Gibt es eine ähnliche Methode ?
gruß
Jens

Lin728
12-02-2007, 17:26
was genau machts denn (nicht) ?
wie testest du ob es funktioniert oder nicht?

teamasta
12-02-2007, 17:47
Alle System.out.println("") sollen in der Textarea erscheinen. Ich möchte die normale Java Console in dem SWT Fenster stehen haben. Aber er schreibt nichts in das Fenster rein. Aber das Fenster geht auf und ist per Hand auch editierbar.

Wie kann ich nebenläufig den Text von System.out in die Textarea zeichnen lassen?

gruß
Jens

Waxolunist
12-02-2007, 17:55
Kommt es dir nicht ein bisschen seltsam vor, dass du den System.out verwendest um einen neuen System.out zu erzeugen?

PrintStream backupSystemOutStream = System.out;
System.setOut(new PrintStream(backupSystemOutStream) {

Waxolunist
12-02-2007, 18:09
Aber wieso machst du das so kompliziert mit Threads?



private void initOutputWindow() {
FormToolkit toolkit = new FormToolkit(SWTUtil.getDisplay());
_text = toolkit.createText(this, "", SWT.MULTI|SWT.WRAP);
_text.setEditable(false);
_text.setLayoutData(new GridData(GridData.FILL_BOTH));
_text.setBackground(SWTUtil.getDisplay().getSystem Color(
SWT.COLOR_WIDGET_BACKGROUND));
PrintStream p = new PrintStream(
new OutputStream() {
public void write( int b ) {
_text.append("" + (char)b );
}
}
);
System.setOut(p);
}

mfg, christian

teamasta
12-02-2007, 21:36
hmm, das sieht ja sehr gut aus. Welche Library muss ich für FormToolkit importen? Die SWT Lib reicht anscheinend nicht. Das Programm ist keine Rich-Client Anwendung. Ich möchte so wenig Libs wie möglich verwenden. Wenn der Code nicht blockierend ist, wäre mir sehr geholfen.

Danke
Jens

Waxolunist
13-02-2007, 08:52
Das FormToolKit brauchst du ja nicht unbedingt für deine PrintStream-Lösung. Ich habe einfach nur die Methode in mein SWTUtil-Projekt hineingeschrieben.

Aber ich verwende es gerne, da es die Arbeit mit den Widgets vereinfacht.
Es ist in der ui.forms-Lib dabei.

Anstatt createText könntest du auch ein gewöhnliches new Text nehmen. Kommt auf das gleiche raus.

Mfg, Christian

teamasta
14-02-2007, 17:06
Hallo nochmal,
ich habe jetzt in meinem Hauptprogramm den folgenden Code eingefügt:

Shell peter = new Shell(SWT.DIALOG_TRIM);
final Text _text = new Text(peter, SWT.MULTI | SWT.WRAP | SWT.V_SCROLL);

_text.setEditable(false);
_text.setLayoutData(new GridData(GridData.FILL_BOTH));
PrintStream p = new PrintStream(
new OutputStream() {
public void write( int b ) {
_text.append("" + (char)b );
}
}
);
System.setOut(p);
System.setErr(p);
peter.setVisible(true);

Das Fenster geht auch auf und die CPU Last steigt auf 100%. Leider wird der Inhalt im Fenster aber nicht gerefreshed. Wie kann ich erreichen, dass der Text angezeigt wird ? Geht das auch ressourcenschonender ?

gruß
jens

teamasta
07-03-2007, 00:31
Hi nochmal,
ich bin bisher noch erfolglos.

Hat keiner eine einfache Lösung der Form....

OutputWindow owindow = new OutputWindow();

dass ein SWT Fenster erzeugt (eigener Thread) und sich um das Umlenken der OutputStröme kümmert?

gruß
jens

Waxolunist
07-03-2007, 09:21
Du willst anscheinend echt alles vorgekaut. Und selbst wenn man dir Code in die Hand gibt, der schon genau das macht, was du möchtest, kommst du wieder und verstehst es nicht, weil du dich nicht damit auseinandergesetzt hast.

Allein schon dein erster Post hätte dich eigentlich verraten sollen:


ich brauche eine SWT Klasse, die nicht blockierend ist

Das heißt eigentlich schon, du möchtest etwas, ohne etwas dafür zu tun.

Ich hab dir aus meinen bisherigen Beispielen aber trotzdem mal schnell eine einzige Klasse, wie dus gern hättest, die genau das macht, was du möchtest, zusammenkopiert. Etwas, das jeder geschafft hätte, wenn er sich ein paar Stunden mit SWT (Java vorausgesetzt) beschäftigt hätte.


import java.io.OutputStream;
import java.io.PrintStream;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;

public class OutputWindow {

private static Display display = Display.getDefault();
private Shell sShell = null;

private Button button1 = null;
private Button button2 = null;

private Text text = null;

public static void main(String[] args) {
OutputWindow thisClass = new OutputWindow();
thisClass.createSShell();
thisClass.sShell.open();

while (!thisClass.sShell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}

private void createSShell() {
sShell = new Shell();
button1 = new Button(sShell, SWT.PUSH);
button1.setText("Button 1");
button1.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
button1.addSelectionListener(new SelectionListener() {
public void widgetSelected(SelectionEvent e) {
System.out.println("Button 1 pressed.");
}

public void widgetDefaultSelected(SelectionEvent e) {
}
});
button2 = new Button(sShell, SWT.PUSH);
button2.setText("Button 2");
button2.addSelectionListener(new SelectionListener() {
public void widgetSelected(SelectionEvent e) {
System.out.println("Button 2 pressed.");
}

public void widgetDefaultSelected(SelectionEvent e) {
}
});
button2.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
text = new Text(sShell, SWT.MULTI|SWT.WRAP|SWT.V_SCROLL);
text.setEditable(false);
text.setBackground(display.getSystemColor(SWT.COLO R_WHITE));
PrintStream p = new PrintStream(
new OutputStream() {
public void write( int b ) {
text.append("" + (char)b );
}
}
);
System.setOut(p);
GridData gridDataText = new GridData(GridData.FILL_BOTH);
gridDataText.horizontalSpan = 2;
text.setLayoutData(gridDataText);
GridLayout gridLayout = new GridLayout();
sShell.setLayout(gridLayout);
gridLayout.numColumns = 2;
}
}

Es ist ein einfaches Fenster mit 2 Buttons, die das Fenster mit Text füllen, sobald sie gedrückt werden.

Warum ich es trotzdem tue? Keine Ahnung, weil ich nett bin vielleicht. Aber diese Dreistigkeit, regt mich schon etwas auf.

mfg, christian

teamasta
13-03-2007, 11:43
Hallo Christian,
vielleicht habe ich mich etwas falsch ausgedrückt. Aber ich möchte nicht dreist sein. Danke für deinen ausführlichen Beispielcode.

Warum ich hier immer wieder schreibe ? Weil es nicht funktioniert. Dein Beispiel funktioniert. Das habe ich vorher auch schon gehabt. Aber mein Problem liegt woanders.

Das SWT Fenster soll nur die Ausgabe eines anderen Threads liefern, der Berechnungen durchführt. Deshalb muss das SWT Fenster nebenläufig sein, ich habe es daher in einen Thread umgebaut.

Alle System.out Ausgaben die innerhalb der SWT Fensterklasse geschrieben werden, landen wunschgemäß im SWT Fenster. Die wichtigen Ausgaben aus dem Hauptthread aber leider nicht.
Das ist mein Problem mit dem ich nicht weiterkomme.

Soll ich meinen jetzigen Stand posten ?

gruß
Jens

Waxolunist
13-03-2007, 12:40
Auch für diese Lösung wurde bereits der gesamte Code in diesem Thread gepostet. Hast du dir schon die mehrmals genannte Methode asyncExec angesehen?

Wenn nicht hier der Code:


package at.sterzl.swt.examples;

import java.io.OutputStream;
import java.io.PrintStream;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;

public class OutputWindow {

public static Text text = null;

public final static PrintStream p = new PrintStream(
new OutputStream() {
public void write( int b ) {
if(text != null)
text.append("" + (char)b );
}
}
);

static {
System.setOut(p);
}

private static Display display = Display.getDefault();
private Shell sShell = null;

private Button button1 = null;
private Button button2 = null;



public static void main(String[] args) {
OutputWindow thisClass = new OutputWindow();
thisClass.createSShell();
new PrintThread(display, text).start();
thisClass.sShell.open();

while (!thisClass.sShell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}

private void createSShell() {
sShell = new Shell();
button1 = new Button(sShell, SWT.PUSH);
button1.setText("Button 1");
button1.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
button1.addSelectionListener(new SelectionListener() {
public void widgetSelected(SelectionEvent e) {
System.out.println("Button 1 pressed.");
}

public void widgetDefaultSelected(SelectionEvent e) {
}
});
button2 = new Button(sShell, SWT.PUSH);
button2.setText("Button 2");
button2.addSelectionListener(new SelectionListener() {
public void widgetSelected(SelectionEvent e) {
System.out.println("Button 2 pressed.");
}

public void widgetDefaultSelected(SelectionEvent e) {
}
});
button2.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
text = new Text(sShell, SWT.MULTI|SWT.WRAP|SWT.V_SCROLL);
text.setEditable(false);
text.setBackground(display.getSystemColor(SWT.COLO R_WHITE));
GridData gridDataText = new GridData(GridData.FILL_BOTH);
gridDataText.horizontalSpan = 2;
text.setLayoutData(gridDataText);
GridLayout gridLayout = new GridLayout();
sShell.setLayout(gridLayout);
gridLayout.numColumns = 2;
}
}

class PrintThread extends Thread {
Text threadtext;
Display threaddisplay;

public PrintThread(Display display, Text text) {
threaddisplay = display;
threadtext = text;
}

public void run() {
while(true) {
if (!threaddisplay.isDisposed()) {
threaddisplay.asyncExec(new TextSetter("Threadausgabe"));
}

try {
Thread.sleep(500);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}

private class TextSetter extends Thread {

private String message = "";

TextSetter(String msg) {
message = msg;
}

public void run() {
if (!threadtext.isDisposed()) {
synchronized (threadtext) {
System.out.println(message);
}
}
}
}
}


mfg, Christian

teamasta
13-03-2007, 13:13
Hallo Christian,
deine Lösung geht davon aus, das der SWT Thread der Hauptthread ist, aus dem der Thread mit den Berechnungen gestartet wird.

Ich möchte mein Hauptprogramm (wo die Dinge ablaufen) starten, und darin dann eine neue SWT Klasse erzeugen (die dann nebenläufig ist), die die Dinge ausgibt. Also genau anders herum. Ist das genauso möglich?

Darin liegt mein Problem.

gruß
Jens

Waxolunist
13-03-2007, 13:32
Nicht einfach. Vor allem wenn man Funktionen hat, wie das Fenster beenden. Dann sollte auch das Programm beendet werden.

Grundsätzlich geht es.

Aber ich würde eher andersrum vorgehen. Du musst ja nur statt einer Klasse Textsetter eine andere Threadklasse in die Methode asyncExec einfügen, welche die Berechnungen durchführt, und danach trachten, dass der Zugriff auf das Outputwindow synchron abläuft.

mfg, christian

teamasta
13-03-2007, 19:43
Leider ist das Programm, an dem ich arbeite, schon soweit fortgeschritten, dass eine komplette Umstrukturierung nicht möglich ist. In Swing gibt es eine schöne Möglichkeit die auch super mit Threads funktioniert. In SWT ist das wegen der Event Schlfeife nicht so einfach.

Deshalb wäre es halt schön, wenn man eine neue Klasse erzeugt, die nicht blockiert und sich um die Ausgabe kümmert.

Kennt jemand eine Quelle,die sich explizit mit SWT und Threads auseinandersetzt?

gruß
Jens