• QT多线程项目中子线程无法修改主线程的ui组件


    情况描述

    今天我创建了一个QT多线程的工程,框架如下。我希望通过指针的方式,让子线程去直接修改主线程的ui组件,但事与愿违。

    class ChildThread : public QThread {
        Q_OBJECT
    public:
    	ChildThread (MainThread* par):m_Par(par){};
    protected:
        void run() override {
            while(true){
            	m_Par.ui->label.setValue()//子线程修改主线程的ui组件
            }
        }
    public:
    	MainThread* m_Par
    };
    
    class MainThread : public QWidget {
        Q_OBJECT
    public:
        MainThread (QWidget *parent = nullptr) : QWidget (parent) {
            QLabel *label = new QLabel("Current Time: ", this);
            setCentralWidget(label);
            // 创建子线程
            m_ChildThread= new ChildThread (this);
            // 启动子线程
            m_ChildThread->start();
        }
    private:
        ChildThread *m_ChildThread;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    存在的问题:子线程修改主线程ui是不安全操作

    在Qt中,GUI相关的操作(例如更新UI元素)通常应该在主线程中执行。直接在子线程中更新UI是不安全的,可能会导致未定义的行为或崩溃。
    正确的做法是:利用QT的信号与槽函数机制。在子线程处理完业务后,发出信号并携带上结果给主线程,交给主线程渲染。

    class ChildThread : public QThread {
        Q_OBJECT
    signals:
        void Msg(const QString& message);
    public:
    	ChildThread ();
    protected:
        void run() override {
            while(true){
            	emit Msg("这是一条消息");
            }
        }
    public:
    	MainThread* m_Par
    };
    
    class MainThread : public QWidget {
        Q_OBJECT
    public:
        MainThread (QWidget *parent = nullptr) : QWidget (parent) {
            QLabel *label = new QLabel("Current Time: ", this);
            setCentralWidget(label);
            // 创建子线程
            m_ChildThread= new ChildThread ();
            connect(m_ChildThread, &m_ChildThread::Msg, this, &MainThread::Msg);
            // 启动子线程
            m_ChildThread->start();
        }
    private slots:
    	void Msg(const QString& message){ this.label.setText(message); }
    private:
        ChildThread *m_ChildThread;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    这里要注意两点:
    1、信号携带的数据要和槽函数接受的数据保持一致,变量名可以不一样。

    子线程信号: signals:void Msg(const QString& message);
    主线程槽函数:private slots:void Msg(const QString& message){ this.label.setText(message); }

    2、信号与槽函数链接,如果官方的链接方式无效果,就换成直接引用。

    方式一 connect(m_ChildThread, SINGAL(m_ChildThread::Msg), this, SLOT(&MainThread::Msg));
    方式二 connect(m_ChildThread, &m_ChildThread::Msg, this, &MainThread::Msg);

  • 相关阅读:
    Springboot+网上眼镜商场 毕业设计-附源码241659
    RocketMQ —消费进度管理
    NL2SQL技术方案系列(1):NL2API、NL2SQL技术路径选择;LLM选型与Prompt工程技巧,揭秘项目落地优化之道
    C: 两个星号的含义
    JavaScript相关面试题5
    golang工程——grpc 连接池简单实现
    基于QT实现简单的连连看小游戏
    123.Impala查询缓慢问题与解决
    ESP8266/ESP32 +1.3“ or 0.96“ IIC OLED指针式时钟
    突破编程_C++_面试(模板编程(3))
  • 原文地址:https://blog.csdn.net/qq_51323520/article/details/134489388