Ausführung Grafik kontrollieren

Hallo liebe Java-Experten und Java-Expertinnen,

zur Einleitung eines Java-Programms wird ein Logo aufgerufen.
Der Virenscanner arbeitet manchmal im Hintergrund und unterbricht
die Darstellung. Das sieht nicht schön aus.

Leider fehlen mir für diese Zeit Teile der Grafik, die auch nicht mehr dargestellt werden.

Zum Verständnis:

  • repaint() wird über einen Taktgeber aufgerufen, der in einem
    extra Thread läuft.
  • nacheinander wird ein Logo in verschiedenen Größen gezeichnet.
  • update(…) ist überschrieben und ruft nur paint(…) auf.

Die Methode paint(…) wird offenbar vollständig und sauber abgearbeitet.
Trotz allem bleiben manchmal Teile der Darstellung
auf dem Bildschirm aus.

Frage 1: Läuft die Grafikausgabe in einem extra Thread?
Frage 2: Wie erhalte ich die Information,
dass das Element dargestellt wurde.

Es wäre in Ordnung, wenn die Darstellung des Logos durch den Virenscanner kurzzeitig angehalten wird. Ich möchte nur die vollständige Darstellung erreichen.

Dazu würde ich den Takt ausblenden, so lange nicht signalisiert ist, dass der Teil des Logos auf dem Bildschirm angezeigt wurde.

Die Verwendung von wait() und notify() scheint mir naheliegend.
Da ich diese Methoden noch nicht eingesetzt habe und auch gar nicht weiß, wie man das genau macht, wäre ich für eine ausführliche Antwort dankbar.

Das Programm lässt sich hier aufrufen:
http://www.haller-mtl.de/breakout/index.html

Um den unerwünschten Effekt zu sehen ist es günstig, beim Starten des Programms den Virenscanner im Hintergrund den Computer prüfen zu lassen.

Vielen Dank für eure Mühe
Uwe Haller

.

Hi,

verwendest Du kein ImageIcon bzw. MediaTracker?

Gruß

Bonkers

Hallo Bonkers,

verwendest Du kein ImageIcon bzw. MediaTracker?

Im Applet wird ein Objekt der Klasse Pictures.java erzeugt.
Diese Klasse habe ich selbst entworfen.
Sie lädt mit einem MediaTracker zuerst alle Grafiken aus der .jar-Datei in statische Felder, von denen Sie dann bei Bedarf abgerufen werden.

Darin liegt es wohl leider nicht.

Zumal das Logo einmal geladen immer wieder benutzt wird.
Da muss es noch etwas anderes geben.

Danke für die Mühe
Uwe Haller

.

Hallo Uwe,

die Idee mit dem wait() und notify ist gar nicht so schlecht.
Versuche mal folgendes:

  1. Definiere ein LOCK Objekt, welches deine Grafikroutine als auch der Taktgeber kennt.
    Z.B. über „final static Object LOCK = new Object()“.
    2: in der paint(Graphics…) Methode führst du am Ende folgendes aus:

    synchronized (LOCK) {
    LOCK.notifyAll();
    }

  2. Im Taktgeber ist folgender Code zu benutzen:

    public void run() {
    while (!Thread.currentThread.isInterrupted()) {

    graphicComponent.repaint();
    synchronized (LOCK) {
    try {
    LOCK.wait();
    } catch (InterruptedException ex) {
    // someone wants to interrupt this thread
    Thread.currentThread().interrupt()
    // or just use a return statement to leave the thread.
    }
    }
    }
    }

Hierbei habe ich jetzt mal angenommen, dass es bei Dir ein run() Methode gibt, die du entsprechend überschreibst.

Frage 1: Läuft die Grafikausgabe in einem extra Thread?

Ja, das Repaint erzeugt ein Repaint-Ereignis. Dieses wird durch den EventDispatchThread (EDT) abgearbeitet.

Frage 2: Wie erhalte ich die Information,
dass das Element dargestellt wurde.

Obige Implementierung sollte immer das wait verlassen, wenn die Paint-Routine ein notifyAll auf dem LOCK Objekt macht.

Gruß,
Frank

Hallo Frank,
vielen Dank für die Anregung.

  1. Definiere ein LOCK Objekt, welches deine Grafikroutine als
    auch der Taktgeber kennt.
    Z.B. über „final static Object LOCK = new Object()“…usw.

Habe ich alles gemacht.

Hierbei habe ich jetzt mal angenommen, dass es bei Dir ein
run() Methode gibt, die du entsprechend überschreibst.

Das ist so.

Frage 2: Wie erhalte ich die Information,
dass das Element dargestellt wurde.

Obige Implementierung sollte immer das wait verlassen, wenn
die Paint-Routine ein notifyAll auf dem LOCK Objekt macht.

Sehr aufschlussreich. Jetzt ist mir in etwa klar, wie man wait und notify einsetzt.

Leider ist das Ergebnis nicht befriedigend.
Die Darstellung ist jetzt generell unvollständig.

Frage 1: Läuft die Grafikausgabe in einem extra Thread?

Ja, das Repaint erzeugt ein Repaint-Ereignis. Dieses wird
durch den EventDispatchThread (EDT) abgearbeitet.

Dies ist besonders aufschlussreich.

Ob paint(…) sauber abgearbeitet wird, hatte ich bereits geprüft.
Darin liegt es offenbar nicht.
Es scheint, als wird der EDT nicht fertig. Wenn ich vom EDT die Fertigmeldung erhalten könnte, das wäre toll.

Weißt du, wie das geht?

Danke für die Mühe
Uwe Haller

etwas besser
Hallo Frank,
die Methode paint(…) habe ich synchronized.
Dies bringt etwas bessere Ergebnisse.

Falls es noch weitere Ansätze gibt, wär ich dankbar.

Uwe Haller

Hallo Uwe,

das ‚synchronized‘ stellt nur sicher, dass nicht mehrere Threads quasi gleichzeit sich in der Methode aufhalten können.

Wo wird denn entschieden, welche Variante des Bildes dargestellt werden soll?
Wenn das in der Paint-Methode per Variabel hochzählen gemacht wird, dann kann das nur schiefgehen.

Der Taktgeber, muss die „Bildnummer“ vorgeben. Die Paint-Methode nutzt die „Bildnummer“ und zeichnet genau für dieser Bildnummer das Bild. Wenn jetzt zwei Paints hintereinander kommen, dann wird immer das gleicher Bild gezeichnet, solange der Taktgeber nicht hochzählt.

die Methode paint(…) habe ich synchronized.
Dies bringt etwas bessere Ergebnisse.

Falls es noch weitere Ansätze gibt, wär ich dankbar.

Uwe Haller

Gruß,
Frank

Hallo Frank,
danke, dass du dir noch Zeit für meine Sorgen nimmst.

das ‚synchronized‘ stellt nur sicher, dass nicht mehrere
Threads quasi gleichzeit sich in der Methode aufhalten können.

Das ‚synchronized‘ war eine Schnaps-Idee. Ist schon entfernt.

Wo wird denn entschieden, welche Variante des Bildes
dargestellt werden soll?

Zum Ablauf:
Der Taktgeber läuft als extra Thread.
Der Taktgeber ruft in der Klasse ‚Logo extends Panel‘ die Methode setPosition() auf.

setPosition() berechnet die neue Größe des Logos und ruft am Ende repaint() auf.

update() ist überschrieben und ruft als einzigen Aufruf paint(…) auf.

paint(…) zeichnet als einziges das Logo, mit den neu berechneten Koordinaten.

Jetzt passiert nichts, bis der Taktgeber nach ca 40 Millisekunden wieder setPosition() aufruft.

Wenn das in der Paint-Methode per Variabel hochzählen gemacht
wird, dann kann das nur schiefgehen.

Der Taktgeber, muss die „Bildnummer“ vorgeben. Die
Paint-Methode nutzt die „Bildnummer“ und zeichnet genau für
dieser Bildnummer das Bild. Wenn jetzt zwei Paints
hintereinander kommen, dann wird immer das gleicher Bild
gezeichnet, solange der Taktgeber nicht hochzählt.

Das verstehe ich leider nicht.

Zum EDT habe ich in der Literatur den Hinweis gefunden, dass es nur bei Swing-Componenten Verwendung findet. Da ich ausschließlich AWT-Elemente einsetze, kann es sein, dass dieser Ansatz wenig Erfolg verspricht.

Uwe

Hallo Uew,

Hallo Frank,
danke, dass du dir noch Zeit für meine Sorgen nimmst.

Ist doch gern geschehen :smile:

[…]
Jetzt passiert nichts, bis der Taktgeber nach ca 40
Millisekunden wieder setPosition() aufruft.

Okay, das könnte das Problem sein. Windows Rechner schaffen es i.d.R. nicht etwas „so schnell“ wieder darzustellen. Das liegt zum Teil an der Timer-Auflösung in diesem tollen Betriebssystem (liegt so bei 25-30ms).

Dein Timer sollte nicht zu festen Zeitpunkten zuschlagen, d.h. nach 40, 80, 120, … Millisekunden, sondern nach 40+delta1, 80+delta1+delta2, … D.h. so wie du das eigentlich haben willst, das zwischen den einzelnen Bildern jeweils mindestens 40ms liegen.
Das bedeuteet aber, dass das deltaX hier auch mal 40ms (und mehr) sein kann (siehe Argumentation oben).

Wenn dem nicht so sein soll, dann musst du eben immer den Timer zu festen zeitpunkten aufrufen, mit dem Effekt, dass Deine Animation zwar nach einer definierten Zeit fertig ist, aber evtl. manchmal zu schnell abläuft, bzw. Zwischenbilder fehlen.

In der Regel kannst Du es nicht verhinden, dass ein anderer Prozess (oder auch Thread) eine so hohe Priorität hat, dass er so gut wie alle Rechenzeit für sich beansprucht. Das ist unter Windows ein generelles Problem. Hier kann man mit einer kleinen Schleife in Java, fast das komplette Windows blockieren. Unter anderen Betriebssystemen kann man weiterhin mit dem System arbeiten, auch wenn bestimmte Aufgaben hier und da etwas länger dauern, aber es das System reagiert noch.

Wenn das in der Paint-Methode per Variabel hochzählen gemacht
wird, dann kann das nur schiefgehen.

Der Taktgeber, muss die „Bildnummer“ vorgeben. Die
Paint-Methode nutzt die „Bildnummer“ und zeichnet genau für
dieser Bildnummer das Bild. Wenn jetzt zwei Paints
hintereinander kommen, dann wird immer das gleicher Bild
gezeichnet, solange der Taktgeber nicht hochzählt.

Das verstehe ich leider nicht.

Ich hatte im Kopf, dass der Taktgeber eine Bildnummer verwaltet und der Taktgeber auch die Komponenteninstanz kennt. Du hast das ganze umgekehrt implementiert.
Beachte dabei, dass ein Aufruf von setBounds(…) bzw. setLocation(…) indirekt ein repaint() auslöst.

Zum EDT habe ich in der Literatur den Hinweis gefunden, dass
es nur bei Swing-Componenten Verwendung findet. Da ich
ausschließlich AWT-Elemente einsetze, kann es sein, dass
dieser Ansatz wenig Erfolg verspricht.

Auch AWT besitzt eine Event-Queue. Das kann man sehen, wenn man in der Paint-Methode sich den Namen des aktuellen Threads ausgeben lässt.

Gruß,
Frank

Hallo Frank,

Jetzt passiert nichts, bis der Taktgeber nach ca 40
Millisekunden wieder setPosition() aufruft.

Okay, das könnte das Problem sein.

Wir sind offenbar ganz nah dran.

Dein Timer sollte nicht zu festen Zeitpunkten zuschlagen,
D.h. …, das zwischen den einzelnen Bildern jeweils
mindestens 40ms liegen.

Habe ich geändert.

Der Taktgeber taktet jetzt nicht mehr das Logo.
Statt dessen starte ich die Klasse Logo mit der Methode start().
Die Klasse Logo läuft nicht in einem extra Thread.

In der Methode start() läuft eine while-Schleife, die bei jedem Durchlauf 40 Millisekunden Pause macht und dann setPosition() aufruft.

In der Methode setPosition() werden x, y, w und h verändert.
Das sind alles private Parameter der Klasse Logo.
Am Ende von setPosition() wird repaint() aufgerufen.

public final void paint(Graphics graphics)
{
 if (this.process == LOGO)
 {
 // Das Logo wird gezeichnet.
 graphics.drawImage(Pictures.stoneUh,
 absHor(this.x),
 absVer(this.y),
 absHor(this.w),
 absVer(this.h),
 this);
 }

 if (this.process == TEXT)
 {
 // Die Schriftart und -größe wird gesetzt.
 graphics.setFont(new Font("Arial Black",Font.PLAIN , absHor(this.h)));

 // Die Schriftfarbe wird gesetzt. 
 graphics.setColor(new Color(this.r, this.g, this.b)); 

 // Der Text wird gezeichnet.
 graphics.drawString("präsentiert", absHor(this.x), absVer(this.y));
 }
}

Leider hat es nichts gebracht. Die Schleife wird abgearbeitet, doch wenn ein anderer Prozess dazwischen funkt, wird ein Teil nicht auf dem Bildschirm dargestellt.

Jetzt wo wir wissen, dass ein Repaint-Ereignis ausgelöst wird, das in einem separaten Thread abgearbeitet wird, müssen wir diesen separaten Thread nur noch unter Kontrolle bekommen.

In der Regel kannst Du es nicht verhinden, dass ein anderer
Prozess (oder auch Thread) eine so hohe Priorität hat, dass er
so gut wie alle Rechenzeit für sich beansprucht.

Das ist vielleicht gar nicht nötig.

Auch AWT besitzt eine Event-Queue.

Das könnte die Lösung sein.
Es gibt da etwas mit inkoverLater(). Allerdings stelle ich mich offenbar nicht sehr glücklich an, es zu implementieren.
Falls dies ein Weg ist, das Logo sicher auf dem Bildschirm auszugeben, dann sei so lieb und stelle den genauen Code dar.

Uwe

Hallo Frank,
falls dir nichts mehr einfällt, dann lasse ich es jetzt so.
Das Logo hat jetzt mehr Zeit bekommen, sich zu zeichnen.

Es wäre schöner, wenn die Startsequenz etwas schneller abläuft.
Vielleicht hat irgend jemand mal eine Idee.
Das Logo wird jetzt meistens vollständig dargestellt. :open_mouth:)

Für deine Mühe vielen Dank.
Jetzt darfst du einmal spielen:
http://www.haller-mtl.de/breakout/appletstart.html

und tschüs
Uwe

.

Hallo Uwe,

nettes Spiel. Ich konnte zwar nie selber Dein Problem sehen, aber evtl. ist es acuh in 90% ein „akademisches“.

Falls es Dich weiterhin interessiert findest Du unter den folgenden Links evtl. noch ein paar Hinweise:

Gruß,
Frank

PS. Gibts irgendwann noch mehr Spiele?

[Bei dieser Antwort wurde das Vollzitat nachträglich automatisiert entfernt]