Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Unable to send signal from mainwindow to working thread



  • I wish to display videos using OpenCV using multithreading. I have followed [this link] and results are as expected. Here is code snippet.

    class Worker : public QObject
    {
        Q_OBJECT
    public:
        Worker(QString path, int id);
        ~Worker();
    
    public slots:
        void readVideo(QString path = "");
        void get_from_main(QString path);
    
    signals:
        // frame and index of label which frame will be displayed
        void frameFinished(cv::Mat frame, int index);
    
        void finished(int index);
    
    private:
        QString filepath;
        int index;
    };
    
    //worker.cpp
    #include "worker.h"
    #include <QDebug>
    #include <QThread>
    #include <QTime>
    Worker::Worker(QString path, int id) : filepath(path), index(id)
    {
    
    }
    
    Worker::~Worker()
    {
    }
    
    void Worker::get_from_main(QString path)
    {
          qDebug() << "updating";
    }
    
    void Worker::readVideo(QString path)
    {
        if (path.length() > 0)
            filepath = path;
    
        cv::VideoCapture cap(filepath.toStdString());
    
        if (! cap.isOpened())
        {
            qDebug() << "Can't open video file " << filepath;
            emit finished(index);
            return;
        }
    
        cv::Mat frame;
        while (true)
        {
            cap >> frame;
            if (frame.empty())
            {
                frame = cv::Mat(cv::Size(720, 576), CV_8UC3, cv::Scalar(192, 0, 0));
                emit frameFinished(frame, index);
                break;
            }
    
    
            emit frameFinished(frame.clone(), index);
            QThread::msleep(30);
        }
    
        emit finished(index);
    }
    
    //mainwindow.h
    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    
    #include <QMainWindow>
    #include <opencv2/opencv.hpp>
    
    #include "worker.h"
    
    #define MAX_NUM_CAM 8
    
    namespace Ui {
    class MainWindow;
    }
    
    class QThread;
    class QLabel;
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        explicit MainWindow(QWidget *parent = 0);
        ~MainWindow();
    
        void init();
    
    private slots:
        void displayFrame(cv::Mat frame, int index);
        void file_open_clicked();
    
    signals:
    send_to_worker(QString path);
    
    
    private:
        Ui::MainWindow *ui;
    
        int numCams;
        QLabel *labels[MAX_NUM_CAM];
        QThread* threads[MAX_NUM_CAM];
        Worker* workers[MAX_NUM_CAM];
    };
    
    #endif // MAINWINDOW_H
    
    
    //mainwindow.cpp
    
    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    
    #include <QDebug>
    #include <QThread>
    #include <QLabel>
    #include <QGridLayout>
    
    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
        qRegisterMetaType< cv::Mat >("cv::Mat");
    
        qDebug() << "Main thread " << QThread::currentThreadId();
        init();
    }
    
    MainWindow::~MainWindow()
    {
        delete ui;
    }
    
    void MainWindow::init()
    {
        QGridLayout *grid = new QGridLayout;
        int numCols = 2;
    
        numCams = 4;
    
        int row = 0, col = 0;
        for (int i = 0; i < numCams; i++)
        {
            labels[i] = new QLabel;
    
            row = i / numCols;
            col = i % numCols;
            grid->addWidget(labels[i], row, col);
    
    
            threads[i] = new QThread;
            workers[i] = new Worker(QString("/home/shang/Videos/%1.mp4").arg(i+1), i);
            workers[i]->moveToThread(threads[i]);
    
            connect(workers[i], SIGNAL(frameFinished(cv::Mat, int)), this, SLOT(displayFrame(cv::Mat,int)));
            connect(threads[i], SIGNAL(started()), workers[i], SLOT(readVideo()));
    
            connect(workers[i], SIGNAL(finished(int)), threads[i], SLOT(quit()));
            connect(workers[i], SIGNAL(finished(int)), workers[i], SLOT(deleteLater()));
    
            connect(threads[i], SIGNAL(finished()), threads[i], SLOT(deleteLater()));
    
            threads[i]->start();
        }
    
        this->centralWidget()->setLayout(grid);
    
    }
    
    void MainWindow::file_open_clicked(){
           QString Path = QFileDialog::getSaveFileName( this,tr("OpenVideo"),"","Video (*.avi)");
        if(Path.isEmpty())
            return;
         view =3; 
        connect(this, SIGNAL(send_to_worker(QString)),workers[view], SLOT(get_from_main(QString)));
        emit this->send_to_worker(recorder_Path);
    }
    
    void MainWindow::displayFrame(cv::Mat frame, int index)
    {
        QPixmap p = QPixmap::fromImage(QImage(frame.data, frame.cols, frame.rows, frame.step, QImage::Format_RGB888).rgbSwapped());
        p = p.scaled(QSize(frame.cols/2, frame.rows/2));
        labels[index]->setPixmap(p);
    }
    

    However, slot get_from_main is never triggered. May I please know how to correctly implement QThreading.

    (https://stackoverflow.com/questions/29338250/qt-opencv-play-videos-with-stdthread)


  • Lifetime Qt Champion

    Hi
    Code does look valid.
    Did you check the connect statements that they all return TRUE ?



  • @mrjj
    Thanks for the reply.

    How do I check if they return true?
    I am indeed able to view video. only facing problem with
    connect(this, SIGNAL(send_to_worker(QString)),workers[view], SLOT(get_from_main(QString)));


  • Lifetime Qt Champion

    The slot can't be called since the thread never reaches it's eventloop. So no chance to retrieve the signal and pass it to your slot.



  • @Christian-Ehrlicher ,
    can you explain it in detail. Also, is there any other way to modify private variable "path" of worker class


  • Lifetime Qt Champion

    What detail? As I said an eventloop is needed to deliver signals to slots. You don't run an eventloop in your thread and therefore the slot gets no called. See also http://doc.qt.io/qt-5/qthread.html#details
    It's not forbidden to call a function of an object in another thread directly. You must only take care that this access is synchronized e.g. with a semaphore. See http://doc.qt.io/qt-5/threads-synchronizing.html for more information.


  • Moderators

    @magicstar said in Unable to send signal from mainwindow to working thread:

    while (true)

    This is an infinite loop. It causes Worker::readVideo() to block forever and never return. When the function never returns, the event loop never gets the chance to process any more signals. (In other words, a thread that is running an infinite loop cannot run any slots).

    A thread can only have run an infinite loop, or it can only run an event loop. It cannot run both.




Log in to reply