0

I'm trying to use signals and slots to pass information to the GUI thread from another thread, as I can't modify a pixmap from any other thread. I'm encountering a runtime error:

Object::connect: No such signal QThread::image_change(std::string) in visualiser.cpp:33

Judging from this, though I may be wrong, it looks like the signal is being searched for in the wrong namespace, as it is actually defined in Visualiser::image_change().

My code is as follows:

Visualiser.cpp:

    QFutureWatcher<void> watcher;
    connect(watcher.thread(), SIGNAL(image_change(std::string)), QCoreApplication::instance()->thread(), SLOT(update_image(std::string)), Qt::QueuedConnection);
    QFuture<void> update_thread = QtConcurrent::run(this, &Visualiser::update_state);
    watcher.setFuture(update_thread);

   ...

    emit(image_change(imageSrc));

   ...

    void Visualiser::update_image(std::string src)
{
    QImage image;
    image.load(src.c_str());
    ui->visualContainer->setPixmap(QPixmap::fromImage(image));
}

visualiser.h:

    signals:
    void image_change(std::string src);

public slots:
    void update_image(std::string src);
alexisdm
  • 29,448
  • 6
  • 64
  • 99
dutchgold92
  • 125
  • 1
  • 1
  • 6

2 Answers2

2

Don't pass thread pointers into connect - pass pointers to the sender and receiver of the event (like this). Because you're giving it QThread pointers instead, Qt is looking for those signals and slots in QThread, where they don't exist. If you give it Visualizer pointers instead, Qt will look for those functions in Visualizer, where they really are, and everything will work.

Hope that helps!

Xavier Holt
  • 14,471
  • 4
  • 43
  • 56
  • So how do I set the first parameter of connect() to acknowledge that the signal will come from the QtConcurrent::run() thread? – dutchgold92 Nov 17 '12 at 13:30
  • @dutchgold92 - Um... Well, I'm not expert on Qt multithreading, but I believe that Qt takes care of this all by itself, and that signals are moved to the correct thread automatically. – Xavier Holt Nov 17 '12 at 13:32
  • But I'm not sure how to set the parameters based on what you're saying. For example, if I set them like this, nothing seems to happen: connect(this, SIGNAL(image_change(std::string)), this, SLOT(update_image(std::string)), Qt::QueuedConnection); – dutchgold92 Nov 17 '12 at 13:34
  • @dutchgold92 - Have you called `QApplication::exec()`? That's what starts the event queue running, and you have to have an event queue for `Qt::QueuedConnection` signals to work. (Sorry for the late reply!) – Xavier Holt Nov 18 '12 at 12:55
1

The source and the target of the connection are the same object, this, so the connect call should be:

connect(this, SIGNAL(image_change(std::string)), this, SLOT(update_image(std::string)));

Since the signal will be emitted from another thread than the one the Visualizer has an affinity with (see QObject::moveToThread()), the connection with the slot will automatically be queued, and the slot will be executed by the correct thread.

But for queued connection to work, Qt has to store temporarily the parameter until it can actually call the slot, which is done by converting it to QVariant, storing it somewhere, and then reconverting it to the actual type when the receiving thread is ready to execute the slot.

So you need to register std::string to Qt's metatype system with Q_DECLARE_METATYPE or change the signal and slot parameter type to one that is already registered to (like QString or QByteArray).

alexisdm
  • 29,448
  • 6
  • 64
  • 99