How to Efficiently Draw a QImage to a Window.



  • I need to efficiently draw a QImage to a widget several times per second. I had planned to put the draw code in the Widget's paintEvent routine and call update on a timer, as shown here:

    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow)
    {
        // Set a timer to update the displayed image every 50 ms
        QTimer *timer = new QTimer(this);
        connect(timer, SIGNAL(timeout()), this, SLOT(update()));
        timer->start(50);
    }
    
    void MainWindow::paintEvent(QPaintEvent *event) {
     
    }
    

    But on my computer (2015 MacBook Pro) this uses 20-25% CPU, which seems rather high. Is there a more efficient way to do draw a QImage to a widget?


  • Lifetime Qt Champion

    Hi and welcome to devnet,

    You need to provide more details before talking optimization. What kind of image is that ? What size ? How are you loading it ? Why do you need to redraw so quickly ?



  • Size is 800x600. Kind is QImage, "loaded" with new QImage(800, 600, QImage::Format_ARGB32);. I'm drawing shapes/applying effects to them to synthesise video. All of these manipulations individually have little discernible impact on CPU utilisation except for drawing to the widget.


  • Lifetime Qt Champion

    Ok, then since you are drawing anyway, why not draw directly on the widget ?

    On a side note, there's rarely need to allocate a QImage on the heap, why to you need to do it ?



  • I don't understand what you mean by "draw directly" - I thought it was only possible to draw to widgets in paintEvent().


  • Lifetime Qt Champion

    Let me reformulate: since you are drawing your QImage on the widget, do you really need to pass by a QImage ? Can't you do the same drawing directly on the widget ?



  • In my case I don't think so. But even if I could I don't see how that would help - the significant computational cost seems to be in getting paintEvent() to run in the first place, not initialising, manipulating or drawing QImages.


  • Lifetime Qt Champion

    If you could that would mean you don't have to allocate a QImage each time you call paint event which in itself is already a gain.

    You should also rather work on a QPixmap since you're going to draw it, QPixmap is optimized for showing image on screen while QImage is optimized for IO and direct pixel access.

    What operations do you need to do exactly ?



  • I think we're getting a little distracted trying to optimise the system instead of the drawing.

    Assuming that I could draw directly to a widget and therefore had no need for QImages or QPixmaps, I might be able to express that like this:

    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
    
        QTimer *timer = new QTimer(this);
        connect(timer, SIGNAL(timeout()), this, SLOT(update()));
        timer->start(50);
    }
    
    void MainWindow::paintEvent(QPaintEvent *event) {
        QPainter painter(this);
        painter.drawLine(QPointF(10, 10), QPointF(20, 20));
        painter.end();
    }
    

    This uses 25% CPU, which seems like a lot just to draw a short line segment. How could this simple example be optimised (if at all)?


  • Lifetime Qt Champion

    Is your application painting static content ? If so, then don't trigger update that much, there's no real need.

    paintEvent will be called when needed so with your last sample, calling update every 50ms is useless and power consuming.



  • Ok, so that may not have been a great example. In reality it will be dynamic content, so I really do need a frequent screen refresh. This is a slightly more representative example:

    int x = 0;
    void MainWindow::paintEvent(QPaintEvent *event) {
        if (x > 500) x = 0;
        QPainter painter(this);
        painter.drawLine(QPointF(10, 10), QPointF(x++, x++));
        painter.end();
    }
    

  • Lifetime Qt Champion

    Do you need some kind of graph ?



  • No, it's arbitrary shapes.


  • Lifetime Qt Champion

    Is there some kind of history for these shapes are do you need to re-draw them all every time ?



  • Need to redraw.


  • Lifetime Qt Champion

    The first thing I'd do is optimize the data sent e.g. do you really need to build a line of 500 points if you already know the final coordinates ?

    Otherwise, maybe consider using OpenGL



  • So to confirm: there's no way to draw in Qt without invoking the relatively expensive update() first?


  • Lifetime Qt Champion

    Yes there is, do the drawing in another thread on a QImage and then trigger the update with that image. You have an example of this in the Mandelbrot example


  • Qt Champions 2016

    @as2388

    Yes there is, do the drawing in another thread on a QImage and then trigger the update with that image.

    I'd add to @SGaist's great point - even convert the image to a QPixmap in the worker (after drawing it), and in the paint event just splat it onto the widget.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.