c++ - QT从主窗口更新工作线程的变量

标签 c++ multithreading qt opencv

我按照建议使用 QT 多线程概念显示 opencv 视频 here

我还希望在单击按钮时更新工作线程类的私有(private)“路径”变量。所以我加了

connect(this, SIGNAL(send_to_worker(QString)),workers[view], SLOT(get_from_main(QString)));

但是,get_from_main(QString) 函数永远不会被调用。

请问从主窗口更新工作线程类变量最安全的方法是什么?

完整代码..

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);
}

Qt + OpenCV play videos with std::thread

最佳答案

您正在尝试定期执行操作,同时仍在处理事件——这只是调用 QTimer .

(请注意,以下代码未经测试。)

更改您的 Worker 类以使用 QTimer 而不是阻塞事件队列的循环...

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;
  QTimer           timer;
  cv::VideoCapture cap;
};

Worker::Worker (QString path, int id)
  : filepath(path)
  , index(id)
  , timer(this)
  , cap(filepath.toStdString())
{

  /*
   * Connect QTimer::timeout to the readVideo slot that will read a
   * single frame on each signal at 30ms intervals.
   */
  connect(&timer, &QTimer::timeout, this, &Worker::readVideo);
  timer.start(30);
}

Worker::~Worker ()
{
}

void Worker::get_from_main (QString path)
{
  qDebug() << "updating";
  filepath = path;
  cap = cv::VideoCapture(filepath);
  if (!cap.isOpened()) {
    qDebug() << "Can't open video file " << filepath;
    emit finished(index);
  }
}

void Worker::readVideo ()
{
  cv::Mat frame;
  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);
}

现在 Worker::readVideo 只是从捕获中读取单个帧,然后返回到事件循环。

同时删除行...

connect(threads[i], SIGNAL(started()), workers[i], SLOT(readVideo()));

来自 MainWindow::init。正如我上面所说,这是未经测试的,可能需要很多更多的错误检查。但它应该能让您清楚了解需要什么。

关于c++ - QT从主窗口更新工作线程的变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49588884/

相关文章:

java - 为什么 Swing 线程模型被认为是错误的,它应该如何?

c++ - 使用 C++/Qt 读取(和写入)RTF 文件

c++ - 使用内联的显式模板函数实例化

c++ - Makefile:创建构建目录

c++ - 为什么 C++11 不支持在静态成员函数上声明 extern "C"?

c++ - 关于移动构造函数

多线程问题

javascript - Node JS 异步 I/O 执行

qt - 如何提高 QPainter 的性能?

c++ - QtCreator 4.1.0 不显示 MainWindow 表单编辑器的 webengineview(QT 5.7)