0
votes

I'm attempting to create a simple animation in Qt through QPainter, currently i've been able to draw in my window, but i'm unable to figure out how i could draw the same thing periodically.

Here's my current code:

int main(int argc, char *argv[]){
QApplication a (argc, argv);
QLabel l;
Qpicture pi;
Qpainter p(&pi);
Qpen pen;
/*
Some ellipses and lines using p.drawLine and p.drawEllipse
*/
p.end();
l.setPicture(pi);
l.show();

return a.exec();

I've tried creating a thread, but i can't create a QApplication outside of main and if i try to give my painter, label, picture and pen to a thread, then it won't compile as they are private:

void PrintThread ( QLabel * l, QPicture * pi, Qpainter * p, QPen * pen){
    /*
    print lines and ellipses
    */
}

int main(int argc, char *argv[]){
QApplication a (argc, argv);
    QLabel l;
    Qpicture pi;
    Qpainter p(&pi);
    Qpen pen;
std::thread doPaint (PrintThread, l, pi, p, pen);
doPaint.join();

How would a sample code that draws anything (lines/ellipses) using Qpainter every X milliseconds look like?

2
You can use a QTimer and call the update function every time the timer is elapsed. - dgrat

2 Answers

2
votes

There's this crazy disease going on, where people automatically associate periodic/timed task with multithreading. You need nothing of the sort - just use a timer:

// https://github.com/KubaO/stackoverflown/tree/master/questions/picture-async-44201102
#include <QtWidgets>
#include <QtConcurrent>

class PictureSource : public QObject {
   Q_OBJECT
public:
   QPicture draw() {
      QPicture pic;
      QPainter p(&pic);
      p.rotate(QTime::currentTime().msec()*360./1000.);
      p.drawLine(0, 0, 50, 50);
      pic.setBoundingRect({-50, -50, 100, 100});
      emit pictureChanged(pic);
      return pic;
   }
   Q_SIGNAL void pictureChanged(const QPicture &);
};
Q_DECLARE_METATYPE(QPicture)

int main(int argc, char ** argv) {
   QApplication app{argc, argv};
   qRegisterMetaType<QPicture>();
   QLabel label;
   PictureSource source;
   QObject::connect(&source, &PictureSource::pictureChanged,
                    &label, &QLabel::setPicture);
   source.draw();
   label.show();
   QTimer timer;
   timer.start(50); // every 50 ms
   QObject::connect(&timer, &QTimer::timeout, &source, &PictureSource::draw);
   return app.exec();
}
#include "main.moc"

Now - if you think you need to - you can do the drawing in a worker thread from the thread pool. Replace the connection with:

  QObject::connect(&timer, &QTimer::timeout, [&source]{
    auto pool = QThreadPool::globalInstance();
    QtConcurrent::run(pool, [&source]{ source.draw(); });
  });

Ensure that draw() is thread-safe, or that you otherwise don't modify the source object concurrently from the main thread without proper synchronization.

0
votes

I eventually worked out on a clean solution, adding support for reading keypresses aswell:

main.cpp

#include <QtWidgets>
#include <QApplication>
#include "screen.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    screen Screen;

    // size of your choice
    Screen.setFixedSize(1024,576);

    Screen.show();

    return a.exec();
}

screen.h

#ifndef SCREEN_H
#define SCREEN_H

#include <QWidget>
#include <QLabel>

class screen : public QLabel
{
    Q_OBJECT

public:
    screen();
    void keyPressEvent(QKeyEvent *);
    void keyReleaseEvent(QKeyEvent *);
public slots:
    void tick();
};

#endif // SCREEN_H

screen.cpp

#include "screen.h"

#include <QtWidgets>
#include <QTimer>

QPen pen;
QPicture pic;
QPainter p;

screen::screen(){

    QTimer * timer = new QTimer();
    connect(timer,SIGNAL(timeout()),this,SLOT(tick()));

    timer->start(16);
}
void screen::keyPressEvent(QKeyEvent *event)
{
    // event for key pressed
}

void screen::keyReleaseEvent(QKeyEvent *event)
{
    // event for key released
}

void screen::tick(){
    p.begin(&pic);

    // start draw



    // finish draw

    p.end();
    this->setPicture(pic);
}