Solved 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)
-
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))); -
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 -
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. -
@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.
-
@JKSH , @Christian-Ehrlicher , Thanks