4

In my qt application i have a class (worker) which is called from the object which runs in a thread. In my worker class i create QList, like this

QList <QString> albums;

while (i.hasNext())
{
  QRegularExpressionMatch album = i.next();
  albums.append(album.captured("album"));
}
emit SignalGotAlbums(albums);

I receive this signal in another class which is wrapping my worker, for thread-usage. Let's call this class GetAlbumsThread. In there i successfully receive SignalGotAlbums in a slot

void GetAlbumsThread::Reply(QList <QString> &list)
{

 emit gotAlbums(list);
 emit finished();
}

In this slot i'am firing another signal gotAlbums which is suppose to be connected with a slot in my gui thread, and pass my QList in there. My problem is, when im trying to pass QList from a thread to gui, it's just not working! Slot not receiving gotAlbums Signal;

Signal is declared like so:

void gotAlbums(QList<QString> &);

And connected to my gui slot (of course in my gui thread) like that:

private slots:
    void AlbumsReceived(QList<QString> &list)
    ...

    QThread* albumsThread = new QThread();

    GetAlbumsThread *albumsObject = new GetAlbumsThread();

    albumsObject->moveToThread(albumsThread);

    connect(albumsThread, SIGNAL(started()), albumsObject, SLOT(process()));

    connect(albumsObject, SIGNAL(gotAlbums(QList<QString> &)), this, SLOT(AlbumsReceived(QList<QString> &));

     albumsThread->start();

AlbumsReceived never get's called for some reason. connect returns true. Can someone help me with this. I think the problem is in the passing QList between threads.

Mr_and_Mrs_D
  • 32,208
  • 39
  • 178
  • 361
SirLanceloaaat
  • 213
  • 9
  • 18
  • Unless I'm missing something, you're creating QList albums as a local variable on the stack, then passing a reference to it in your signal, which means that QList could have gone out of scope and been destroyed byt the time the queued connections work their way back up to the GUI thread. This shouldn't prevent the slot receiving the signal, but is potentially a nasty bug in its own right. – Ninjammer Jun 12 '13 at 22:51
  • should i make a QList global? or create it with a new? – SirLanceloaaat Jun 12 '13 at 23:56
  • btw i also tried to make QList in a heap, by calline new Qlist, but i get invalid application of 'sizeof' to incomplete type 'QStaticAssertFailure' error – SirLanceloaaat Jun 13 '13 at 02:17
  • I would suggest you change the title since it is definitely an issue relative with the use of threads and passing a reference between them with signals/slots (a QList or something else). The modification might help somebody else find the solution. – Boris Dalstein Jun 13 '13 at 18:58

2 Answers2

6

Did you try to register your QList object before calling Object::connect(...) ?

You can declare the meta-type using this code:

qRegisterMetaType< QList<QString> >( "QList<QString>" );
Dufeu
  • 61
  • 1
5

The issue here is that you are using a reference for your signal/slot, i.e. a QList<QString> &. This is not compatible with threads since they use their own private stack, and in this case what you do is passing a pointer to an object in the stack from one thread to another.

Possibilities are:

  1. Use raw QList<QString> signals/slots, that will force a copy.
  2. Allocate the QList<QString> with a new (hence will go in the heap instead of the stack), and use QList<QString> * signals/slots.

The following code illustrates these two methods:

// A.h

#include <QObject>
#include <QDebug>

class A: public QObject
{
Q_OBJECT

public slots:
    void foo(int i) { qDebug() << i; }
    void bar(QList<int> l) { foreach(int i, l) qDebug() << i; }
    void bar2(QList<int> * l) { foreach(int i, *l) qDebug() << i; }
};


// Worker.h

#include <QObject>

class Worker: public QObject
{
    Q_OBJECT

public slots:
    void process()
{
        // pass an int
        emit foo(1);

        // pass a list by value
        emit bar(QList<int>() << 2 << 3 << 4);

        // pass a poniter to a list
        list = new QList<int>();
        *list << 5 << 6 << 7;
        emit bar2(list);

        emit finished();
    }

signals:
    void finished();
    void foo(int);
    void bar(QList<int>);   
    void bar2(QList<int> *); 

private:
    QList<int> * list;
};


// main.cpp

#include <QApplication>
#include <QThread>
#include <QObject>

#include "A.h"
#include "Worker.h"

int main(int argc, char** argv)
{
    QApplication app(argc, argv);

    A * a = new A();
    Worker * worker = new Worker();
    QObject::connect(worker, SIGNAL(foo(int)), a, SLOT(foo(int)));
    QObject::connect(worker, SIGNAL(bar(QList<int>)), a, SLOT(bar(QList<int>)));
    QObject::connect(worker, SIGNAL(bar2(QList<int>*)), a, SLOT(bar2(QList<int>*)));

    QThread * thread = new QThread();
    worker->moveToThread(thread);

    QObject::connect(thread, SIGNAL(started()), worker, SLOT(process()));
    QObject::connect(worker, SIGNAL(finished()), thread, SLOT(quit()));
    QObject::connect(thread, SIGNAL(finished()), &app, SLOT(quit()));
    thread->start();

    return app.exec();
}

Output:

1 
2 
3 
4 
5 
6 
7
Boris Dalstein
  • 7,015
  • 4
  • 30
  • 59
  • Tried p2. I'm getting an error with a heap QList allocation: "invalid application of 'sizeof' to incomplete type 'QStaticAssertFailure'". – SirLanceloaaat Jun 13 '13 at 02:35
  • @SirLanceloaaat Did you change the signal and slot definition using `QList *` instead of `QList`? See my edit for a complete working example. – Boris Dalstein Jun 13 '13 at 08:47
  • Yes i did change signal slot definitions, but still getting QStaticAssertFailure error (requires metatype definition blabla). So i just changed definitions and QList* to QStringList*, now it works fine. Ty for help. – SirLanceloaaat Jun 13 '13 at 14:47
  • @SirLanceloaaat: Uh, that is weird. Well, after checking, QStringList is not a raw typedef QList as I though, but it inherits from it, that may explain the different behaviour. But have you tried my code? Because I used `QList *` passing an object from the heap, and it worked well, while I get the same error than you if I use `QList &` passing an object from the stack. So either there's something else in your code that is broken, or your configuration is broken. (or my configuration is broken by being more permissive than it should be) – Boris Dalstein Jun 13 '13 at 18:52
  • Yes i tried exactly what you wrote, i changed all signal slot delegates to QList *. Maybe i missed some include. – SirLanceloaaat Jun 14 '13 at 13:00
  • @SirLanceloaaat I mean, have you tried this exact code outside of your project, as a minimal compilable test? Because maybe (hard to know for sure) there is something broken somewhere else in your code. Trying out with a small test code would help you to know if the issue is your configuration (computer, compiler, version of Qt, etc.) or your current code. :) – Boris Dalstein Jun 14 '13 at 19:52
  • No, im going to try it out. – SirLanceloaaat Jun 14 '13 at 21:53