3

In my project I need to generate thousands of unique QPixmap variables based on data collected.

This data is being collected in worker thread because it can be a little slow. At the end of the thread's work, it will create one big image containing all the little QPixmaps put together. However, QPixmaps can't be created and QPainter doesn't work in non GUI threads.

At first, I decided to do all QPixmap generation AFTER the thread already finished, however this is too slow. Converting the data into something that can be turned into a QPixmap takes just long enough to cause visible stuttering.

How can I generate QPixmaps from inside a worker thread so that the program doesn't hiccup?

Thanks for your time.

EDIT: A small example was requested:

QImage image(8, 8, QImage::Format_ARGB32_Premultiplied);
image.fill(Qt::transparent);

QPainter p(&image);

for(int x=0; x<16; x+=2)
{
    for(int z=0; z<16; z+=2)
    {
        quint16 id = blockData[0][x][z];
        quint8 data = id/4096;
        id = id-(data*4096);

        p.setPen(QColor("#ff0000"));
        p.drawPoint(x/2, z/2);
    }
}

p.end();

    chunkResponse->blocks = QPixmap::fromImage(image);

Output is:

QPainter::setPen: Painter not active
QPainter::drawPoints: Painter not active
QPainter::end: Painter not active, aborted
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
mrg95
  • 2,371
  • 11
  • 46
  • 89
  • 3
    Why do you want `QPixmap`? Use `QImage`. – Matteo Italia Apr 02 '18 at 23:25
  • They are being generated inside the thread. Like, drawn on with QPainter... (well, that's what I want at least) – mrg95 Apr 02 '18 at 23:26
  • What painting operation do you want to make, or just the join of QPixmap? – eyllanesc Apr 02 '18 at 23:28
  • It depends on the data collected. Sometimes, it's an 8x8 pixmap of specific colors. Other times, it's a 256x256 pixmap of a specific arrangement of sprites from a sprite sheet. It totally differs depending on the data – mrg95 Apr 02 '18 at 23:29
  • 2
    A `QPainter` will work just fine on a `QImage`, without any particular restriction about threads. – Matteo Italia Apr 02 '18 at 23:30
  • @MatteoItalia I will give QImage a try, it will take a while for me to edit and test. eyllanesc I will do that afterwards. – mrg95 Apr 02 '18 at 23:33
  • @MatteoItalia Wrong. There was no change between QPixmap and QImage. QPainter throws "Painter not active" when drawing on either type. – mrg95 Apr 02 '18 at 23:36
  • @eyllanesc I've added a short example. – mrg95 Apr 02 '18 at 23:41
  • That's bizarre, are you sure that the `QApplication` (or `QGuiApplication`) has already been created when this code runs? – Matteo Italia Apr 02 '18 at 23:45
  • Definitely. This is run after a mouse event inside a separate dialog. – mrg95 Apr 02 '18 at 23:45
  • Is this Qt 4 or Qt 5? – Matteo Italia Apr 02 '18 at 23:48
  • Qt 5.6.2 compiled with MSVC2010. What I did to test was isolate only the example code I've posted above. I've edited my question to show my QImage implementation... – mrg95 Apr 02 '18 at 23:50
  • Does using QPainter::begin() explicitly also return false? – hyde Apr 02 '18 at 23:57
  • Besides, the `QImage` example is wrong because you just cannot create a pixmap from another thread; only the main thread can talk about pixmaps. `blocks` must be a `QImage` as well; if you need to convert it to a `QPixmap` (why?) you must do it in the GUI thread. Also, the `QImage` should have format `QImage::Format_ARGB32_Premultiplied` for best performance. – Matteo Italia Apr 02 '18 at 23:57
  • Sorry! I found a mistake. `p.end()` should never have been in the loop. It should have been after it! Still doesn't get rid of the issue however. – mrg95 Apr 03 '18 at 00:01

1 Answers1

1

You do not have to use QPixmap, but QImage, and at the end you use what you convert from QPixmap to be sent through the signal:

#include <QtWidgets>

struct Chunk
{
    QImage blocks;
    QDateTime time;
};

Q_DECLARE_METATYPE(Chunk)

class Thread: public QThread{
    Q_OBJECT
public:
    using QThread::QThread;
    ~Thread() { quit(); wait();}
    Q_SIGNAL void sendChunk(const Chunk &);
protected:
    void run() override{

        while (true) {

            Chunk chunkResponse;
            QImage image(128, 128, QImage::Format_ARGB32_Premultiplied);
            image.fill(Qt::transparent);

            QPainter p(&image);

            for(int x=0; x<256; x+=2)
            {
                for(int z=0; z<256; z+=2)
                {
                    QColor color(qrand() % 256, qrand() % 256, qrand() % 256);
                    p.setPen(color);
                    p.drawPoint(x/2, z/2);
                }
            }

            p.end();
            chunkResponse.blocks = image;
            chunkResponse.time = QDateTime::currentDateTime();
            Q_EMIT sendChunk(chunkResponse);
            QThread::msleep(10);
        }
    }
};


int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QLabel w;
    w.setFixedSize(128, 128);
    Thread thread;
    QObject::connect(&thread, &Thread::sendChunk, [&](const Chunk & chunk){
        w.setPixmap(QPixmap::fromImage(chunk.blocks));
        qDebug()<<chunk.time.toString();
    });
    thread.start();
    w.show();
    return a.exec();
}

#include "main.moc"
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • Yes I'm looking for that myself. Sit tight, I'll figure it out :) – mrg95 Apr 03 '18 at 00:12
  • chunkResposne is a struct that contains information about the pixmap, including the pixmap itself – mrg95 Apr 03 '18 at 00:12
  • Okay so I had a copy of the function elsewhere that was being called. Sorry about that. isolating it so only the example above is run throws no errors, but also doesn't seem to generate any colors on the image. Still testing... – mrg95 Apr 03 '18 at 00:19
  • Okay so I decided to output the image to a file, and indeed, the colors are working! I'll have to mess around and figure out why the second half of my program didn't show them properly. Thanks for your help! The solution was to switch to `QImage`. and thanks to Matteo Italia for the suggestion as well – mrg95 Apr 03 '18 at 00:26