【QT入门】 Qt自定义信号后跨线程发送信号

往期回顾:

【QT入门】 lambda表达式(函数)详解-CSDN博客

【QT入门】 Qt槽函数五种常用写法介绍-CSDN博客

【QT入门】 Qt实现自定义信号-CSDN博客

 【QT入门】 Qt自定义信号后跨线程发送信号

由于Qt的子线程是无法直接修改ui,需要发送信号到ui线程进行修改,所以会跨线程发送信号。

一、思路

思路基本一致,子线程发送一个信号,父线程接收信号并执行槽函数,把子线程传递的数据展示在父线程ui上。

二、步骤

1.如何创建子线程

右键单击项目,选择Add New->C++>C++ Class即可

ChildThread是我们自己取的子线程名字
下面的基类因为没有合适的基类,我们选择Custom,继承QThread类

这里注意,创建以后由于是自己填的继承自QThread类,它不一定包含了相应的头文件,需要我们自己补上。

2、添加Q_OBJECT宏

自己创建的子线程是不包含Q_OBJECT宏的,如果需要用到信号槽,需要自己补上Q_OBJECT ,一般建议大家不管用不用,创建后就都补上。

3、子线程重写run方法

子线程继承父线程之后需要重写父线程的run方法,关于线程这一块知识点,后续会有更加详细的讲解,比如:子线程重写的run方法在子线程里,但是其构造函数却是在父线程里等

void ChildThread::run()
{
   //打印当前线程的线程号
   qDebug()<<"child thread id= "<<QThread::currentThreadId();
   Score s;
   s.name="zhangsan";
   s.age=18;
   s.id=001;

   emit sig_sendScore(s);

}

注:

当我们分不清代码运行在哪个线程的时候,可以用QThread::currentThreadId();方法打印当前线程的线程号来判断。

这里数据方面用了一个结构体来写数据

struct Score
{
    string name;
    int age;
    int id;
};

4、启动子线程

 父线程按钮点击的槽函数里创建子线程,接受子线程的信号并启动子线程

void Widget::on_btnOpen_clicked()
{
    ChildThread *ch =new ChildThread();

    connect(ch,&ChildThread::sig_sendScore,[=](Score s){
        string info="name="+s.name+" age="+to_string(s.age)+" id="+to_string(s.id);
        ui->lineEdit->setText(QString::fromStdString(info));
    });

    qDebug()<<"widget thread id= "<<QThread::currentThreadId();
    
    ch->start();
}

针对代码看几个注意点:

1、要在Qt用c++的string类型,一个加头文件,二个加命名空间
2、age和id这种int类型要转成string,用一个to_string(),复习,int转Qstring?用QString::number()
3、setText放的是QString类型的,这里info是string类型,所以需要转QString,用QString::fromStdString()

但是,由于ChildThread的ch对象的槽函数sig_sendScore连接到了lambda表达式中,lambda表达式可能在ChildThread的线程中执行。这导致槽函数执行时在ChildThread的线程中运行,而不是在主线程中。

为了让槽函数在父线程执行,要么不用lambda表达式,改用槽函数,要么改写lambda表达式

4.1 改用槽函数
void Widget::on_btnOpen_clicked()
{
    ChildThread *ch =new ChildThread();

    connect(ch,&ChildThread::sig_sendScore,this,&Widget::showIofo);

    qDebug()<<"ui01 thread id= "<<QThread::currentThreadId();

    ch->start();
}
4.2 改写lambda表达式
void Widget::on_btnOpen_clicked()
{
    ChildThread *ch =new ChildThread();

    connect(ch, &ChildThread::sig_sendScore, this, [=](Score s){
        string info = "name=" + s.name + " age=" + to_string(s.age) + " id=" + to_string(s.id);
        ui->lineEdit->setText(QString::fromStdString(info));
        qDebug()<<"slots thread id= "<<QThread::currentThreadId();
    }, Qt::QueuedConnection);


    qDebug()<<"ui01 thread id= "<<QThread::currentThreadId();

    ch->start();
}

使用Qt::QueuedConnection连接信号和槽,这样信号会被投递到接收者所在的线程中执行。可以确保槽函数在接收者所在的线程中执行,从而解决可能的线程问题。

三、报错

当成功在父线程执行后,报了一个错误:

QObject::connect: Cannot queue arguments of type 'Score'
(Make sure 'Score' is registered using qRegisterMetaType().)

这是告诉我们Score是一个非基础类型参数,需要进行注册,在子线程的构造函数实现里注册即可

ChildThread::ChildThread()
{
    //非基础类型参数注册
   qRegisterMetaType<Score>("Score");
}

四、最终代码

 最后,附上最终代码,以便供大家参考

1、widget.h

#ifndef WIDGET_H
#define WIDGET_H
#include "childthread.h"
#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE


class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private slots:
    void on_btnOpen_clicked();
    void showIofo(Score s);


private:
    Ui::Widget *ui;
};
#endif // WIDGET_H

2、widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QDebug>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
}

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


void Widget::on_btnOpen_clicked()
{
    ChildThread *ch =new ChildThread();

    //ch对象的槽函数sig_sendScore连接到了lambda表达式中,lambda表达式可能在ChildThread的线程中执行。
    //这可能导致槽函数执行时在ChildThread的线程中运行,而不是在主线程中。
//    connect(ch,&ChildThread::sig_sendScore,[=](Score s){
//        string info="name="+s.name+" age="+to_string(s.age)+" id="+to_string(s.id);
//        ui->lineEdit->setText(QString::fromStdString(info));
//        qDebug()<<"slots thread id= "<<QThread::currentThreadId();
//    });

//    connect(ch, &ChildThread::sig_sendScore, this, [=](Score s){
//        string info = "name=" + s.name + " age=" + to_string(s.age) + " id=" + to_string(s.id);
//        ui->lineEdit->setText(QString::fromStdString(info));
//        qDebug()<<"slots thread id= "<<QThread::currentThreadId();
//    }, Qt::QueuedConnection);

    //
    connect(ch,&ChildThread::sig_sendScore,this,&Widget::showIofo);

    qDebug()<<"ui01 thread id= "<<QThread::currentThreadId();

    ch->start();
}


void Widget::showIofo(Score s)
{

    qDebug()<<"ui02 thread id= "<<QThread::currentThreadId();

    string info="name="+s.name+" age="+to_string(s.age)+" id="+to_string(s.id);
    //setText放的是QString类型的,这里info是string类型,所以需要转QString
    ui->lineEdit->setText(QString::fromStdString(info));
}

3、childthread.h

#ifndef CHILDTHREAD_H
#define CHILDTHREAD_H
#include <QThread>
#include <string>

using namespace std;

//定义一个结构体函数
struct Score
{
    string name;
    int age;
    int id;
};

class ChildThread : public QThread
{
    Q_OBJECT

public:
    ChildThread();

protected:
    void run() override ;

signals:
    void sig_sendScore(Score s);

};

#endif // CHILDTHREAD_H

4、childthread.cpp

#include "childthread.h"
#include <QDebug>
ChildThread::ChildThread()
{
    //非基础类型参数注册
   qRegisterMetaType<Score>("Score");
}

void ChildThread::run()
{
   //打印当前线程的线程号
   qDebug()<<"child thread id= "<<QThread::currentThreadId();
   Score s;
   s.name="zhangsan";
   s.age=18;
   s.id=001;

   emit sig_sendScore(s);

}


都看到这里了,点个赞再走呗朋友~

加油吧,预祝大家变得更强!

  • 14
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Qt线程信号和槽连接需要使用Qt线程通信机制。下面是一些基本的步骤: 1. 使用QObject::moveToThread()方法将对象移动到目标线程。 2. 在目标线程创建一个QObject,并调用QObject::connect()方法将信号与槽连接起来。 3. 如果信号和槽需要传递参数,则需要使用Qt的元对象系统(Meta-Object System)来注册参数类型。可以使用Q_DECLARE_METATYPE宏来注册自定义类型。 4. 在发送信号时,需要使用QMetaObject::invokeMethod()方法,将该方法的第一个参数设置为接收信号的对象,第二个参数设置为接收信号的函数名,第三个参数设置为Qt::QueuedConnection,以确保信号被放入目标线程的事件队列。 下面是一个简单的示例: ```cpp class Worker : public QObject { Q_OBJECT signals: void resultReady(int result); public slots: void doWork() { int result = 0; // 计算结果 emit resultReady(result); } }; class Controller : public QObject { Q_OBJECT public: Controller() { Worker *worker = new Worker(); QThread *workerThread = new QThread(); worker->moveToThread(workerThread); connect(workerThread, &QThread::started, worker, &Worker::doWork); connect(worker, &Worker::resultReady, this, &Controller::handleResult); workerThread->start(); } ~Controller() { // 停止线程 } public slots: void handleResult(int result) { // 处理结果 } }; ``` 在这个例子,Worker对象的doWork()方法在一个新的线程执行,当计算完成后,使用信号resultReady(int result)发送结果。Controller对象将自己的handleResult(int result)槽连接到该信号上,在目标线程处理结果。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值