Hallo,
ich bin gerade dabei mich mit SWT zu beschäftigen und bin dabei auf ein Verhalten gestoßen, das ich mir nicht so recht erklären kann. Und zwar hält mein Programm einfach ungewollt an wenn man eine gewisse Zeit lang nichts macht. Da dieses Verhalten in einem Programm auftritt, dessen Code mehrere hundert Zeilen lang ist und ich euch einen so langen Code nicht zumuten möchte, habe ich einen Code geschrieben der ähnlich aufgebaut ist, das gleiche Verhalten aufweist, aber deutlich kürzer und zudem kompilierbar ist. Und so schaut er aus:
Folgender Code startet schlichtweg die Anwendung:
Und das dazugehörige ClockWidget:
Ich konnte mit Hilfe von Debug-Ausgaben das Auftreten dieser plötzlichen Pausierung des Programmes auf Zeile 79 der ClockWidget-Klasse eingrenzen. Also sobald dort die syncExec-Methode aufgerufen wird, pausiert das Programm plötzlich wenn man längere Zeit nichts macht (also keine Tasten drückt, die Maus nicht bewegt, etc. während das Programm läuft). Es scheint so als würde das in der Main-Klasse erstellte Display-Objekt einfach "schlafen", da es durch das Nichtstun keine Events zum abarbeiten gibt und durch den synchronen Aufruf innerhalb der ClockWidget-Klasse wird damit eben auch mein Thread lahmgelegt.
Meine Frage: Kennt jemand evtl. einen Weg ein (vermutlich) eingeschlafenes Display mittels Code wieder aufzuwecken? Oder sollte man anders vorgehen als ich, wenn man von einem anderem Thread aus als dem UI-Thread ein Widget ständig aktualisieren muss?
Schon einmal vielen Dank im voraus für jeden Tipp
ich bin gerade dabei mich mit SWT zu beschäftigen und bin dabei auf ein Verhalten gestoßen, das ich mir nicht so recht erklären kann. Und zwar hält mein Programm einfach ungewollt an wenn man eine gewisse Zeit lang nichts macht. Da dieses Verhalten in einem Programm auftritt, dessen Code mehrere hundert Zeilen lang ist und ich euch einen so langen Code nicht zumuten möchte, habe ich einen Code geschrieben der ähnlich aufgebaut ist, das gleiche Verhalten aufweist, aber deutlich kürzer und zudem kompilierbar ist. Und so schaut er aus:
Folgender Code startet schlichtweg die Anwendung:
Java:
import org.eclipse.swt.*;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.widgets.*;
public class Main
{
public void startGui()
{
Display display = new Display();
Shell shell = new Shell(display);
shell.setLayout(new FillLayout());
ClockWidget clock = new ClockWidget(shell, SWT.NO_BACKGROUND);
clock.startClock();
shell.pack();
shell.open();
while(!shell.isDisposed()) {
if(!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
}
public static void main(String[] args)
{
new Main().startGui();
}
}
Java:
import java.util.*;
import org.eclipse.swt.*;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.widgets.*;
public class ClockWidget extends Canvas implements Runnable
{
private Composite parent;
private Calendar calendar;
private boolean running;
private String timeString;
private Color circleColor;
private Point circleSize;
private static final Point MAX_CIRCLE_SIZE = new Point(100, 100);
public ClockWidget(Composite parent)
{
this(parent, 0);
}
public ClockWidget(Composite parent, int style)
{
super(parent, style);
this.parent = parent;
this.calendar = new GregorianCalendar();
this.running = false;
this.timeString = "" + calendar.get(Calendar.HOUR_OF_DAY) + ":" + calendar.get(Calendar.MINUTE) + ":" + calendar.get(Calendar.SECOND);
this.circleColor = new Color(getDisplay(), 0, 0, 255);
this.circleSize = new Point(0, 0);
addDisposeListener(new DisposeListener()
{
@Override
public void widgetDisposed(DisposeEvent e)
{
ClockWidget.this.widgetDisposed(e);
}
});
addPaintListener(new PaintListener()
{
@Override
public void paintControl(PaintEvent e)
{
ClockWidget.this.paintControl(e);
}
});
}
public void startClock()
{
if(!running) {
running = true;
Thread t = new Thread(this);
t.start();
}
}
@Override
public void run()
{
long lastTime = System.nanoTime();
long delta = 0;
long framelimitTime = (long)1e9 / 60;
long sleepTime = 0;
while(running) {
delta = System.nanoTime() - lastTime;
sleepTime = framelimitTime - delta;
lastTime = System.nanoTime();
calendar.setTimeInMillis(System.currentTimeMillis());
timeString = "" + calendar.get(Calendar.HOUR_OF_DAY) + ":" + calendar.get(Calendar.MINUTE) + ":" + calendar.get(Calendar.SECOND);
circleSize.x = MAX_CIRCLE_SIZE.x * calendar.get(Calendar.SECOND) / 60;
circleSize.y = MAX_CIRCLE_SIZE.y * calendar.get(Calendar.SECOND) / 60;
getDisplay().syncExec(new Runnable()
{
@Override
public void run()
{
if(parent.isDisposed()) {
running = false;
return;
}
redraw();
}
});
if(sleepTime > 0) {
try {
Thread.sleep(sleepTime / (long)1e6);
} catch(InterruptedException e) {
running = false;
}
} else {
Thread.yield();
}
}
}
@Override
public Point computeSize(int wHint, int hHint, boolean changed)
{
Image image = new Image(getDisplay(), 1, 1);
GC gc = new GC(image);
Point size = gc.stringExtent(timeString);
gc.dispose();
image.dispose();
size.x += MAX_CIRCLE_SIZE.x;
size.y += MAX_CIRCLE_SIZE.y;
return new Point(wHint == SWT.DEFAULT ? size.x : wHint, hHint == SWT.DEFAULT ? size.y : hHint);
}
private void paintControl(PaintEvent e)
{
Image image = new Image(getDisplay(), getBounds());
GC gc = new GC(image);
gc.drawText(timeString, 0, 0);
gc.setBackground(circleColor);
gc.fillOval(gc.stringExtent(timeString).x, 0, circleSize.x, circleSize.y);
e.gc.drawImage(image, 0, 0);
gc.dispose();
image.dispose();
}
private void widgetDisposed(DisposeEvent e)
{
running = false;
circleColor.dispose();
}
}
Meine Frage: Kennt jemand evtl. einen Weg ein (vermutlich) eingeschlafenes Display mittels Code wieder aufzuwecken? Oder sollte man anders vorgehen als ich, wenn man von einem anderem Thread aus als dem UI-Thread ein Widget ständig aktualisieren muss?
Schon einmal vielen Dank im voraus für jeden Tipp