"for" oder "while" Schleife mit Timer steuern



  • Hallo zusammen !!
    Ich arbeite an einer Aufgabe wo muss ein Kreis animieren der von innen nach außen immer größer wird. Diese Aktion muss durch einen Timer gesteuert werden , so kann man gezielt den Abstand zwischen den Zeichenoperationen festlegen. Ich komme mit allem klar außer Timer in "for" oder ""while" Schleife.
    Ich habe mehrmals versucht den Timer innerhalb oder außerhalb der Methode einzusetzen, ohne Erfolg. Ich mache das in QT. Dabei hänge ich nur die paintEvent(); Methode ohne meine Versuche.

    Die Frage ist: Wie kann ich die Schleife mit Timer steuern damit nach einem durchlauf eine Sekunde Pause macht und wieder zeichnen ?
    Und noch , ist das überhaupt " legal" was ich mir gedacht habe ? Ich verstehe dass es gibt andere Lösungen dafür aber das hier sehe ich als einfachste.
    Ich brauche nicht die Lösung sondern nur Tipps von euch.
    Freue mich über jeden Tipp und Kritik.
    Danke
    Viele Grüße

    void grafikspielerei::paintEvent(QPaintEvent *)
    {
        
        QPainter myPainter(this);
        int x = width()/2-5;
        int y = height()/2-5;
        int breite = 10;
        int hoehe = 10;
        durchlauf=0;
        check=false;    
        //kreisTimer->start(3000);
        
        while(durchlauf<30)
    {
            myPainter.drawEllipse(x, y, breite, hoehe);
        
        x = x - 5;
        y = y - 5;
        
        breite = breite+ 10;
        hoehe = hoehe + 10 ;
        durchlauf++;
        }
    }
    


  • @Splin sagte in "for" oder "while" Schleife mit Timer steuern:

    nur Tipps

    https://en.cppreference.com/w/cpp/thread/sleep_for

    Ob das was Du machst im Bezug auf Qt "legal" ist kann ich Dir nicht sagen.



  • @Swordfish danke dir !
    Ich schaue und versuche umzusetzen.



  • Eine andere Möglichkeit wäre QTimer und Signal / Slots. Das ist nicht blockierend.

    Funktioniert grob so:

    Du instantierst einen Timer mit einem Intervall. Wenn das Intervall abgelaufen ist, feuert dieser ein "timeout signal" an. Dieses Signal kannst du mit einem "slot" verbinden. Quasi eine Reaktion auf ein Event.

    QTimer *timer = new QTimer(this);
    connect(timer, &QTimer::timeout, this, &yourDrawFunction);
    timer->start();
    

    Hier musst du dir halt noch überlegen wie du den Timer wieder stoppst 😉 Das würde ich einfach von x bzw. y abhängig machen. In yourDrawFunction erhöhst du ja x,y und wenn die halt nen gewissen Wert erreicht haben, dann ist fertig.

    Eine andere Frage aber: Was hat es mit paintEvent auf sich? Das ist doch schon eine Event? Das wird vermutlich schon regelmäßig aufgerufen oder etwa nicht?

    Vermutung: Es wird öfter aufgerufen als notwendig?

    Dann würde ich das eher so machen:

    void grafikspielerei::paintEvent(QPaintEvent *)
    { 
        QPainter myPainter(this);   
        myPainter.drawEllipse(x, y, breite, hoehe);
    }
    

    In deiner Klasse hast du dann x, y, breite, hoehe als member. Und mit einem Timer (wie oben) veränderst du einfach diese Variablen eben 1 mal pro Sekunde. PaintEvent wird in der Zwischenzeit vermutlich öfter aufgerufen.
    Und den Timer kannst du natürlich hier start 30 mal einfach dann stoppen, wenn du die gewünschte höhe / breite hast.

    Und noch eine letze Frage: Warum willst du einen Timer? Ein animierter Kreis ist doch schöner, wenn er flüssig animiert. Wenn du den 1x pro Sekunde aufrufst und der jedes mal um mehrere pixel springt, sieht doch eher bescheiden aus:

    Sinnvoller wäre doch sowas hier:

    void grafikspielerei::paintEvent(QPaintEvent *)
    { 
        QPainter myPainter(this);   
        myPainter.drawEllipse(x, y, breite, hoehe);
    
       x -= 0.01;
       y -= 0.01;
      breite += 0.02; 
      hoehe += 0.02;
    }
    

    Also sprich: Die Methode paintEvent wird so oft wie nur irgendwie geht aufgerufen, aber die Werte ändern sich nur ganz leicht.



  • @Leon0402: Ein PaintEvent sollte keinerlei Logik enthalten, sondern nur das reine Zeichnen übernehmen.
    Man kann ja das Timer-Intervall entsprechend kleiner setzen (also z.B. 50 oder 100ms).

    Und im TimerEvent werden dann die Grafikwerte (x, y, ...) verändert und das Neuzeichnen angestoßen (repaint bzw. update), welche dann das PaintEvent aufruft.



  • @Leon0402
    Erstmal ein großer Dank für die Ausfürliche Erklärung, das ist genau was ich meinte, eine Lösung mit den Werkzeugen die schon vorhanden sind. Mit sleep(); , wie @Swordfish vorgeschlagen hat , würde auch funktionieren aber ich darf das nicht so Lösen.

    @Leon0402 sagte in "for" oder "while" Schleife mit Timer steuern:

    Warum willst du einen Timer? Ein animierter Kreis ist doch schöner, wenn er flüssig animiert.

    Laut Aufgabe ich muss mit einem Timer lösen und nicht anders.

    Ich versuche gleich die Idee umzusetzen und dann sag ich Bescheid.
    Grüße



  • Vermutlich solltest du noch die Antwort von Th69 berücksichtigen. Ich vermute mal, dass paintEvent doch nicht automatisch ausgeführt wird, sondern du das selbst machen musst.

    Das Setup wäre dann also:

    • Einen Timer erstellen, er in einem gewissen Intervall ein Signal abgibt
    • Die PaintEvent Methode connecten, die nur zeichnet d.h. myPainter.drawEllipse(x, y, breite, hoehe);
    • Eine zweite Methode connecten, die deine Logik enthält und x, y, breite, höhe setzt

    @Th69 Hat schon recht. Man sollte die Logik und das Zeichen nicht in der selben Methode machen 🙂



  • Also, es funktioniert. Nochmal DANKE an allen ihr seid toll !!!
    Ich habe berücksichtigt die Antworte von euch beide: @Leon0402 und @Th69 .
    Im Konstruktor habe die Koordinaten initialisiert und Timer mit SIGNAL/SLOT verbunden. In dem SLOT werden die Koordinaten geändert , repaint(); aufgerufen und Timer->stop hängt von Koordinate x ab. Dadurch dass im SLOT wird immer wieder repaint(); aufgerufen, muss ich den vorherigen Kreis nicht löschen(so ist die Bedingung). Weiter habe aus eigener Initiative so erweitert dass Zeichnung fängt immer wieder vom Anfang an. Danke euch🙂



  • @Splin Hallo, halte mich für blöd, aber ich habe das gerade ausprobiert... Bei mir werden munter Kreise gezeichnet, aber ich bekomme den Timer nicht sinnvoll eingebaut... Wo genau muss ich den Timer starten und wo die Methode Kreis Zeichnen?



  • Das ist doch hier alles gut erklärt: @Splin hat sogar seine Lösung direkt beschrieben.

    • Im Konstruktor den Timer mit einer Timer-Funktion (SLOT) verbinden
    • In dieser Timer-Funktion Logikänderungen durchführen und dann repaint() aufrufen
    • In der (überschriebenen) paintEvent-Funktion dann das Zeichnen durchführen

Anmelden zum Antworten