SWT SWT flackern beheben

P@u1

Aktives Mitglied
Hallo zusammen,
ich hab mal ein bischen angefangen mit SWT (vorher habe ich nur mit Swing gearbeitet) und wollte nen bischen was zeichnen, möglichst ohne störendes flackern.
Ich hab schon ein Canvas mit SWT.DoubleBuffered benutzt, das ergebnis hat mich aber nicht wirklich überzeugt (flackern trat trotzdem auf).
Jetzt habe ich ein org.eclipse.swt.graphics.Image benutzt, es geht ganz gut, aber ein kleines bischen flackert es immer noch ab und zu...
Wie kann ich das besser machen?
Und außerdem finde ich es etwas umständlich bei SWT ein repaint anzuforder, ich habe das mit
Java:
display.asyncExec(new Runnable() {
						public void run() {
							if (!shell.isDisposed())
								shell.redraw();
						}
					});
gemacht, gibts vll bessere Alternativen dazu?

Dann hier drunter nochmal das was ich gemacht hab, bei mir flackert es ab und zu ein kleines bischen, nicht super schlimm, aber ich fänds besser, wenns ganz weg wäre.
Das Beispiel sollte eigentlich kompilierbar sein.

Java:
import java.util.Random;

import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

public class SWTThreadTest {
	public static void main(String[] args) {
		final Display display = Display.getDefault();
		final Shell shell = new Shell(display);
		int WIDTH = 1000;
		int HEIGHT = 1000;
		shell.setSize(new Point(WIDTH, HEIGHT));
		shell.open();
		final Image img = new Image(display, WIDTH, HEIGHT);
		final GC gc = new GC(img);
		shell.addPaintListener(new PaintListener() {
			@Override
			public void paintControl(PaintEvent e) {
				e.gc.drawImage(img, 0, 0);
			}
		});
		new Thread() {
			{
				setDaemon(true);
			}

			public void run() {
				Random rnd = new Random();
				int d = 1000;
				while (true) {
					gc.drawLine(rnd.nextInt(d), rnd.nextInt(d), rnd.nextInt(d),
							rnd.nextInt(d));
					display.asyncExec(new Runnable() {
						public void run() {
							if (!shell.isDisposed())
								shell.redraw();
						}
					});
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}.start();
		while (!shell.isDisposed())
			if (!display.readAndDispatch())
				display.sleep();
		display.dispose();
	}
}

Wenn jemand verbesserungsvorschläge hat -> bitte melden, danke :)
 

Wildcard

Top Contributor
Der 2te Thread ist Grundfalsch. Du musst im UI Thread zeichnen. Dann brauchst du im Normalfall auch kein Image. Ausserdem fällt dann das asyncExec um das redraw weg.
Beispiel (so sieht es doch besser aus, oder ;) ):

Java:
public class SWTThreadTest {
    public static void main(String[] args) {
        final Display display = Display.getDefault();
        final Shell shell = new Shell(display);
        int WIDTH = 1000;
        int HEIGHT = 1000;
        shell.setSize(new Point(WIDTH, HEIGHT));
        shell.open();
        shell.setLayout(new FillLayout());
        new MyCanvas(shell);
        while (!shell.isDisposed())
            if (!display.readAndDispatch())
                display.sleep();
        display.dispose();
    }
}

class MyCanvas extends Canvas implements PaintListener
{
	
    private Random rnd = new Random();

	public MyCanvas(Composite parent) {
		super(parent, SWT.NONE);
		addPaintListener(this);
		new Updater(this).schedule(1000);
	}

	@Override
	public void paintControl(PaintEvent e) {
		GC gc = e.gc;
		int d = 1000;
		for(int i=0;i<20;i++)
		{
			gc.drawLine(rnd.nextInt(d), rnd.nextInt(d), rnd.nextInt(d),
					rnd.nextInt(d));			
		}
		
	}
	
}

class Updater implements Runnable{
	private Canvas canvas;
	private int milliseconds;

	public Updater(Canvas canvas) {
		super();
		this.canvas = canvas;
	}
	
	public void schedule(int milliseconds){
		this.milliseconds = milliseconds;
		canvas.getDisplay().timerExec(milliseconds, this);
	}
	
	@Override
	public void run() {
		canvas.redraw();
		canvas.getDisplay().timerExec(milliseconds, this);
        
	}
}
 

P@u1

Aktives Mitglied
Danke für deine Hilfe!

Es funktioniert bei mir undzwar ohne flackern (liegt das daran, dass canvas irgendnen buffering macht?).
Allerdings noch zwei Sachen: (die erste ist wesentlich wichtiger als die zweite):

- Die Linien kommen erst, sobald ich einmal die größe des fensters geändert habe - warum ist das so und wie kann mans ändern?
- So wird der ja alle x millisekunden neuzeichnen, auch wenn keine änderung vorliegt - einfach so akzeptieren, oder kann man das verbessern?

EDIT:
Bei deiner Lösung stimmt irgendwas noch nicht so ganz, ich habe gerade folgende Exception bekommen:
Java:
Exception in thread "main" org.eclipse.swt.SWTException: Widget is disposed
	at org.eclipse.swt.SWT.error(SWT.java:4083)
	at org.eclipse.swt.SWT.error(SWT.java:3998)
	at org.eclipse.swt.SWT.error(SWT.java:3969)
	at org.eclipse.swt.widgets.Widget.error(Widget.java:468)
	at org.eclipse.swt.widgets.Widget.checkWidget(Widget.java:340)
	at org.eclipse.swt.widgets.Control.redraw(Control.java:2175)
	at thredTest2.Updater.run(SWTThreadTest2.java:82)
	at org.eclipse.swt.widgets.Display.runTimer(Display.java:4167)
	at org.eclipse.swt.widgets.Display.messageProc(Display.java:3256)
	at org.eclipse.swt.internal.win32.OS.DispatchMessageW(Native Method)
	at org.eclipse.swt.internal.win32.OS.DispatchMessage(OS.java:2459)
	at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3655)
	at org.eclipse.swt.widgets.Display.release(Display.java:3713)
	at org.eclipse.swt.graphics.Device.dispose(Device.java:295)
	at thredTest2.SWTThreadTest2.main(SWTThreadTest2.java:31)

ich denke mal das liegt daran, wenn durch timerExec ein redraw ausgeführt wird, und kurz davor das fenster geschlossen wurde...
vll müsste man noch im runnable von timerExec abfragen, ob das canvas disposed ist?
 
Zuletzt bearbeitet:

Swoop

Gesperrter Benutzer
ruf mal composite.layout() auf ... so macht er den inhalt nochmal neu und dadurch könnt es dann gehen!

Grüße Swoop
 

P@u1

Aktives Mitglied
danke für den tipp. Das Problem, dass erst nach Größe ändern neugezeichnet wird, ist dadurch gelöst.
Allerdings bestehen noch die anderen Probleme...
 

Wildcard

Top Contributor
Es funktioniert bei mir undzwar ohne flackern (liegt das daran, dass canvas irgendnen buffering macht?).
Normal brauchst du kein explizites Double Buffering. Wenn doch, dann schreibt man das normalerweise nicht selbst, sondern verwendet das Double Buffering Style Bit.

- Die Linien kommen erst, sobald ich einmal die größe des fensters geändert habe - warum ist das so und wie kann mans ändern?
In dem Code Sample ist mir eine Zeile verrutscht:
Java:
        shell.setSize(new Point(WIDTH, HEIGHT));
        shell.setLayout(new FillLayout());
        new MyCanvas(shell);
        shell.open();
- So wird der ja alle x millisekunden neuzeichnen, auch wenn keine änderung vorliegt - einfach so akzeptieren, oder kann man das verbessern?
Das war nur ein Beispiel um zu zeigen das es eben normalerweise nicht flackert. Wann und ob du manuell ein redraw aufrufen musst hängt davon ab was du eigentlich erreichen möchtest.

Bei deiner Lösung stimmt irgendwas noch nicht so ganz, ich habe gerade folgende Exception bekommen:
Das war auch nur ein Beispiel. Man muss auf isDisposed prüfen und darf dann natürlich auch kein timedExec mehr aufrufen.
 

P@u1

Aktives Mitglied
Der 2te Thread ist Grundfalsch. Du musst im UI Thread zeichnen. Dann brauchst du im Normalfall auch kein Image. Ausserdem fällt dann das asyncExec um das redraw weg.
Beispiel (so sieht es doch besser aus, oder ;) ):

Ich wollte das analog zu Swing machen:

Man hat einen hauptthread, der daten ändert und bei änderung dem EDT bescheid sagt, dass er neumalen muss
und einen EDT, der sich um die anzeige kümmert

nur kann man da wohl nicht einfach redraw aufrufen vom nicht-EDT-thread, sondern muss das ganze noch in ein asyncexec/timerexec, oder so wrappen...
Und evtl. werden redraw requests dann nichtmal zusammengefügt, wie es bei swing der fall ist.
 

P@u1

Aktives Mitglied
Warum sollte das in Swing nicht so gehen?

Ich kann doch ein Modell-Objekt haben, dass einen bestimmten zustand hat.
Ich kann dann außerhalb des EDT das modell ändern und dem EDT dann mit repaint bescheid sagen, dass das modell sich geändert hat und er es neu zeichnen soll, oder ist das falsch?
Und ob das Modell seinen Zustand jetzt in einer Liste speichert, oder in einem BufferedImage (was ja im grunde auch als zweidimensionales array von farbwerten gesehen werden kann), sollte doch egal sein.
Das einzige was passieren könnte ist, dass der EDT das Modell in einem "zwischenzustand" sieht, was mir aber relativ egal ist für die Anwendungen bei denen ich das anwenden wollte.

Kannst du mir bitte nochmal erklären, was genau daran jetzt falsch ist?
 

Wildcard

Top Contributor
Zeichnen muss Betriebssystem Resourcen allokieren, sogar wenn es sich um ein Image handelt. In Swing ist das unter bestimmten Vorraussetzungen anders (Headless Mode), in SWT aber immer so, daher ist es nicht so das du einfach ein 'Modell' veränderst. Du zeichnest in einem separaten Thread und das macht man weder in Swing, noch in SWT.
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
J Flackern wie mit BufferedImage beheben AWT, Swing, JavaFX & SWT 4
T Flackern trotz DoubleBuffering AWT, Swing, JavaFX & SWT 8
L JButton flackern - Programm hängt sich auf AWT, Swing, JavaFX & SWT 3
G Applet Applet Komponenten flackern AWT, Swing, JavaFX & SWT 10
G Swing Flackern nach Override von paintComponent() AWT, Swing, JavaFX & SWT 3
S Swing Flackern und ausbrechen bei neuzeichnung AWT, Swing, JavaFX & SWT 7
K AWT flackern trotz buffering AWT, Swing, JavaFX & SWT 4
Z Flackern trotz Offscreen Image / Doublebuffer, (+ Frage zu Pixelvergleich) AWT, Swing, JavaFX & SWT 25
V Applet JApplet Flackern durch Repaint AWT, Swing, JavaFX & SWT 11
L Swing Flackern in Frame bei erstem Klick AWT, Swing, JavaFX & SWT 19
R Flackern beim JPanel-Übermalen AWT, Swing, JavaFX & SWT 9
D Undecorated JFrame ohne Flackern resizen AWT, Swing, JavaFX & SWT 21
K Flackern trotz Double-Buffering AWT, Swing, JavaFX & SWT 10
sylo Flackern des Mauszeigers bei DND AWT, Swing, JavaFX & SWT 4
G ColorReader, bei Fadenkreuz zeichnen mit repaint() flackern AWT, Swing, JavaFX & SWT 19
eQuest Swing Snake repaint() "flackern" AWT, Swing, JavaFX & SWT 13
J Flackern in JEditorPane() AWT, Swing, JavaFX & SWT 14
JRTHEFROG Auflösungsabhängiges Flackern von Bildschirmmasken AWT, Swing, JavaFX & SWT 3
B JFrame flackern durch setSize() AWT, Swing, JavaFX & SWT 8
N Flackern beim neuzeichnen von JPanel AWT, Swing, JavaFX & SWT 19
S Falsche Position, Elemente erscheinen nicht, Flackern AWT, Swing, JavaFX & SWT 6
S animation Flackern trotz doppelpufferung wieso? AWT, Swing, JavaFX & SWT 2
U Flackern verhindern AWT, Swing, JavaFX & SWT 5
J Flackern beim ersten laden von JPanel AWT, Swing, JavaFX & SWT 4
T Labels verschieben / Flackern AWT, Swing, JavaFX & SWT 2
C Menue verschwindet hinter AWT-List Box.wie beheben? AWT, Swing, JavaFX & SWT 3

Ähnliche Java Themen


Oben