QThread 子类化 打开相机

5 篇文章 0 订阅

QThread subclass 的用法:  1、派生出QThread的子类WorkerThread;2、重写 run() 函数,即线程需要处理的逻辑、事情;3、在调用线程的地方,将该子类实例化,4、链接信号和槽 5、通过调用 对象的start(),启动线程;

class WorkerThread : public QThread
  {
      Q_OBJECT
      void run() Q_DECL_OVERRIDE {
          QString result;
          /* ... here is the expensive or blocking operation ... */
          emit resultReady(result);
      }
  signals:
      void resultReady(const QString &s);
  };

  void MyObject::startWorkInAThread()
  {
      WorkerThread *workerThread = new WorkerThread(this);
      connect(workerThread, &WorkerThread::resultReady, this, &MyObject::handleResults);
      connect(workerThread, &WorkerThread::finished, workerThread, &QObject::deleteLater);
      workerThread->start();
  }

In that example, the thread will exit after the run function has returned. There will not be any event loop running in the thread unless you callexec().

在上述例子中,线程将会在run()函数执行完时推出。 在线程中不存在任何的事件循环除非调用过函数 exec();

It is important to remember that a QThread instance lives in the old thread that instantiated it, not in the new thread that calls run(). This means that all of QThread's queued slots will execute in the old thread. Thus, a developer who wishes to invoke slots in the new thread must use the worker-object approach; new slots should not be implemented directly into a subclassed QThread.

请务必记住:一个线程实例依附于创建该线程的线程(老线程),而并不是那个调用run()函数的线程。这就意味着该线程的所有槽函数都将在创建该线程的线程(老线程)中执行。这样的话,开发者就无法使槽函数在新线程中执行。要么就是重新创建一个新的线程子类(保护原来线程的槽);如果使用 worker-object 的方法,就可减少这种工作量
When subclassing QThread, keep in mind that the constructor executes in the old thread while run() executes in the new thread. If a member variable is accessed from both functions, then the variable is accessed from two different threads. Check that it is safe to do so.

同样还需要记住的是:线程的构建者在老线程中运行,而 run()在新的线程中运行,如果有两个线程都需要访问的变量,请确认访问安全。

Thread Affinity(线程关联)    这对我来说是个新概念  姑且这么翻译吧
A QObject instance is said to have a thread affinity, or that it lives in a certain thread. When a QObject receives a queued signal or a posted event, the slot or event handler will run in the thread that the object lives in.

对于一个QObject实例是有一个线程关联的,或者说该实例在某一线程下存活。当一个QObject(实例)接收到一个信号或者一个事件时,相应的槽或事件句柄是在创建该实例的线程中运行。
Note: If a QObject has no thread affinity (that is, if thread() returns zero), or if it lives in a thread that has no running event loop, then it cannot receive queued signals or posted events.

如果一个QObject(实例)没有线程相关(此时,函数thread()函数返回值为0),或者该QObject实例生存在一个没有事件循环的线程里,那么它不可能接受到信号或事件。
By default, a QObject lives in the thread in which it is created. An object's thread affinity can be queried using thread() and changed using moveToThread().

一般来讲,一个QObject生存在创建它的线程中。一个对象的线程相关可以通过函数 thread() 查询出来,可以用 moveToThread() 来改变。


All QObjects must live in the same thread as their parent. Consequently:

所有QObjects对象必须生存在和父对象一样的线程内。因此


     setParent() will fail if the two QObjects involved live in different threads.

     如果两个QObject对象在不同的线程内, setParent()函数操作会失败。无法指定这两对象的父子关系
      When a QObject is moved to another thread, all its children will be automatically moved too.

     当一个QObject对象被移入另一个线程中,它的所有子对象将会自动的移入。
     moveToThread() will fail if the QObject has a parent.

     无法对一个有父对象的QObject对象执行 moveToThread() 操作
     If QObjects are created within QThread::run(), they cannot become children of the QThread object because the QThread does not live in the thread that calls QThread::run().

     当QObject对象是由 QThread::run()创建的,它不可能成为它的创建对象的子对象,因为QThread线程不会生存在调用 QThread::run() 的对象内。
Note: A QObject's member variables do not automatically become its children. The parent-child relationship must be set by either passing a pointer to the child's constructor, or by calling setParent(). Without this step, the object's member variables will remain in the old thread when moveToThread() is called.


突然发现 Thread Affinity 是个比较大的坑,现在还没能力完全弄明白。还是返回我们的任务,子类化 QThread 打开相机。通过前面我们知道 Thread的执行函数是 run(), 但是开启函数 是 start(), 这么看来是无法 通过信号来传递参数了(即使可以使用信号来传递参数,如需使用非Qt里面规定的变量,还需先注册变量),先用全局变量吧。从前面timer的方式中,知道需要两个变量HObjectho_Image;   HTuplehv_AcqHandle;


以下代码需要注意的地方:
1、全局变量 ho_Image, hv_AcqHandle作用;
2、QThread 子类 内部变量 whoCanChangeMe 的使用, Ui线程 和 拍照线程都能对其 访问
3、函数 whereAmI() 在不同地方运行的不同结果;
4、若在显示的程序内稍微弄点延迟时间,会发现 ui显示的图 并不是当时 线程丢出信号时的那张 图了。 数据不安全了。
可以看出 QThread 子类 里面的变量及函数 是可以 被run() 函数在新线程中运行,也可以在老线程中运行。

昨晚突想起来,这里没有做一下SLOT的试验。
于是在imggrabthread 里添加了一个槽函数
void ImgGrabThread :: slotWhereAmI ()
{
    qDebug()<<"slotWhereAmI() :"<<currentThread();
    whoCanChangeMe++;
    qDebug()<<"whoCanChangeMe :"<<whoCanChangeMe;
}
再链接个信号
connect ( ui -> buttonBox , SIGNAL (accepted ()), imgGrabThread , SLOT (slotWhereAmI ()));
可以看出 改slot 是在主线程中运行的。

imggrabthread.h
#ifndef IMGGRABTHREAD_H
#define IMGGRABTHREAD_H
#include <QThread>
#include "HalconCpp.h"
using namespace HalconCpp;
class ImgGrabThread : public QThread
{
    Q_OBJECT

public:
    ImgGrabThread();
    void whereAmI();
    int whoCanChangeMe = 0;
protected:
    void run();
signals:
    void imgShowSignal();
};
#endif // IMGGRABTHREAD_H
imggrabthread.cpp
#include "imggrabthread.h"
#include <QDebug>
ImgGrabThread::ImgGrabThread()
{
}

void ImgGrabThread:: run()
{
   extern HObject ho_Image;
   extern HTuple  hv_AcqHandle;

   qDebug()<<"imgGrabThread object lives in :"<<thread();
   qDebug()<<"imgGrabThread current thread :"<<currentThread();
   whereAmI();
   while(1)
   {
       GrabImage(&ho_Image, hv_AcqHandle);
       emit imgShowSignal();
   }
}

void ImgGrabThread::whereAmI()
{
    qDebug()<<"WhereAmI() :"<<currentThread();
    whoCanChangeMe++;
    qDebug()<<"whoCanChangeMe :"<<whoCanChangeMe;
}
opencamera.h
#ifndef OPENCAMERA_H
#define OPENCAMERA_H
#include <QDialog>
#include "imggrabthread.h"
#include "HalconCpp.h"
using namespace HalconCpp;
namespace Ui {
class OpenCamera;
}

class OpenCamera : public QDialog
{
    Q_OBJECT

public:
    explicit OpenCamera(QWidget *parent = 0);
    ~OpenCamera();
signals:
    void imgGrabNeeded();
private:
    Ui::OpenCamera *ui;
    ImgGrabThread *imgGrabThread;
public:
    HTuple hv_Width, hv_Height, hv_WindosID;
public slots:
    void imgShowThreadStartOrStop(bool flag=0);
    void imgShowUpdate();
};

#endif // OPENCAMERA_H
opencamera.cpp
#include "opencamera.h"
#include "ui_opencamera.h"
#include <QDebug>
#include <QTime>
#if _MSC_VER >= 1600
       #pragma execution_character_set("utf-8")
 #endif

OpenCamera::OpenCamera(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::OpenCamera)
{
    extern HObject ho_Image;
    extern HTuple  hv_AcqHandle;

    ui->setupUi(this);
    Hlong winID = (Hlong)ui->imgDisplayLabel->winId();
    OpenFramegrabber("DirectShow", 1, 1, 0, 0, 0, 0, "default", 8, "rgb", -1, "false",
        "default", "[0] Lenovo EasyCamera", 0, -1, &hv_AcqHandle);
    SetCheck("~father");
    OpenWindow(0,0,(Hlong)ui->imgDisplayLabel->width(),(Hlong)ui->imgDisplayLabel->height(),winID,"","",&hv_WindosID);
    SetCheck("father");
    GrabImage(&ho_Image, hv_AcqHandle);
    GetImageSize(ho_Image,&hv_Width,&hv_Height);

    SetPart(hv_WindosID,0,0,hv_Height-1,hv_Width-1);
    imgGrabThread = new ImgGrabThread;
    connect(ui->continueGrabCheckBox,SIGNAL(clicked(bool)),this,SLOT(imgShowThreadStartOrStop(bool)));
    connect(imgGrabThread,SIGNAL(imgShowSignal()),this,SLOT(imgShowUpdate()));
    qDebug()<<"QDialog OpenCamera 的线程相关:"<<thread();
    qDebug()<<"imgGrabThread object 的线程相关:"<<imgGrabThread->thread();

}

OpenCamera::~OpenCamera()
{
    delete ui;
}

void OpenCamera::imgShowUpdate()
{
    extern HObject ho_Image;
    DispObj(ho_Image,hv_WindosID);
}

void OpenCamera::imgShowThreadStartOrStop(bool flag)
{    
    qDebug()<<"*************";
    if(flag==1)
    {
        imgGrabThread->start();

    }
    else
    {
        imgGrabThread->terminate();
        qDebug()<<"whoCanChangeMe :"<<imgGrabThread->whoCanChangeMe;
        imgGrabThread->whereAmI();
    }

}
main.cpp

#include "opencamera.h"
#include <QApplication>
#include "HalconCpp.h"
using namespace HalconCpp;

HObject ho_Image;
HTuple  hv_AcqHandle;

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    OpenCamera w;
    w.show();
    return a.exec();
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值