0

I am trying to put some heavy calculation on a separate thread so I don't freeze the UI. For this I am using Qt 5.5.

I tried to implement the worker thread example from the official site, but unfortunately with not much success. Below you can see my code:

main.cpp:

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

    DepthMap* depthmap = new DepthMap();
    DepthMapGrabber* grabber = new DepthMapGrabber(depthmap);

    PointCloudWindow myPointCloudWindow;
    myPointCloudWindow.setDepthMap(grabber);
    myPointCloudWindow.show();

    ProcessorController* controller = new ProcessorController(depthmap);
    controller->startThread();

    return app.exec();
} 

ProcessController.h:

class ProcessorController : public QObject
{
Q_OBJECT
// functions
private:
    QThread workerThread;

public:
    ProcessorController(const DepthMap* depthmap);
    ~ProcessorController();

    void startThread();

public slots:
    void handleResults(const QString &);

signals:
    void operate(const QString &);

// variables
private:
    Processor* m_processor;
};

ProcessController.cpp:

ProcessorController::ProcessorController(const DepthMap* depthmap) {
    m_processor = new Processor(depthmap);
}

ProcessorController::~ProcessorController() {
    if (m_processor != nullptr) {
        delete m_processor;
        m_processor = nullptr;
    }

    workerThread.quit();
    workerThread.wait();
}

void ProcessorController::startThread() {
    m_processor->moveToThread(&workerThread);
    connect(&workerThread, &QThread::finished, m_processor, &QObject::deleteLater);
    connect(this, &ProcessorController::operate, m_processor, &Processor::doWork);
    connect(m_processor, &Processor::resultReady, this, &ProcessorController::handleResults);
    workerThread.start();

    // LOG
    std::cout << "Worker thread has been started!" << std::endl;
}

void ProcessorController::handleResults(const QString &) {
    // LOG
    std::cout << "Worker thread finished!" << std::endl;
}

Processor.h:

class Processor : public QObject
{
Q_OBJECT

// functions
public:
    Processor(const DepthMap* depthmap);

public slots:
    void doWork(const QString &parameter);

signals:
    void resultReady(const QString &result);

// variables
private:
    QMutex m_mutex;
    const DepthMap* m_depthmap;
};

Processor.cpp:

Processor::Processor(const DepthMap* depthmap) : m_depthmap(depthmap) {}

void Processor::doWork(const QString &parameter) {
    // LOG
    std::cout << "Worker thread is working..." << std::endl;

    QString result;

    // copy cloud and work on the copy
    PointCloudPtr myCloud;
    m_mutex.lock();
    pcl::copyPointCloud(*m_depthmap->cloud, *myCloud);
    m_mutex.unlock();

    /* ... here is the expensive or blocking operation ... */

    emit resultReady(result);
}

The problem is, that doWork from Processor never gets called. What am I doing wrong? Thanks in advance for any explanation and help!

EDIT:

If I put

QString result;
emit operate(result);

after

workerThread.start();

in startThread(), the thread gets executed!

Can anyone please explain what is the mechanism behind this?

Silex
  • 2,583
  • 3
  • 35
  • 59
  • Where's emit operate? – Alexander V Nov 17 '15 at 22:44
  • I am really new to this, so I tried to do everything as it is in the example under the link I provided. There is no emit operate...I was actually thinking what is the signal operate for? – Silex Nov 17 '15 at 22:48
  • 1
    You have written the code connect(this, &ProcessorController::operate, m_processor, &Processor::doWork); and that implies that doWork is started by emitting singal operate from ProcessController. If you just want to immediately start calculation you can do it by connecting QThread::started signal to Processor::doWork. – Alexander V Nov 17 '15 at 22:57
  • This actually explains my EDIT too. Thank you! If you put this in a bit longer context as an answer I willing to accept it! Thank you very much for your help! – Silex Nov 17 '15 at 23:00

1 Answers1

1

For a long time Qt provides virtual task objects on the thread we able to manipulate. That is accomplished with QObject::moveToThread and the QObject-derived object must have the slot to start the job from. In this case the slot is doWork and that is connected to signal operate.

connect(this, &ProcessorController::operate, m_processor, &Processor::doWork);

and that is why:

emit operate(desiredStr);

Should resolve the issue.

BTW. If no specific push from user defined signal needed we can always use QThread::started signal for that. In this case doWork slot does not receive the parameter, though.

connect(&workerThread, &QThread::started, m_processor, &Processor::doWorkNoParams);
Alexander V
  • 8,351
  • 4
  • 38
  • 47