首页 > 编程语言 >纯C++代码理解Qt中的信号和槽函数

纯C++代码理解Qt中的信号和槽函数

时间:2023-09-12 18:13:44浏览次数:32  
标签:std Qt 代码 C++ 连接 wrapper 信号 Signal void

槽函数的调用是一个多步骤的过程:

1. 连接(Connect)

首先,通过使用QObject::connect()函数来建立信号和槽之间的连接。

QObject::connect(sender, SIGNAL(signalName(args)), receiver, SLOT(slotName(args)));

这里的senderreceiver是QObject派生的对象,而signalNameslotName则分别是信号和槽的名称。

2. 元对象信息

moc(Meta-Object Compiler)编译源代码时,它会为每个用Q_OBJECT宏标记的类生成元对象信息。这些信息包括信号和槽的函数签名,以及它们在类的成员函数列表中的索引。

3. 发射信号(Emit Signal)

信号在某个特定事件发生时发射:

emit signalName(args);

4. 查找连接列表

当一个信号被发射,Qt元对象系统会查找所有连接到这个信号的槽。这些信息通常存储在一个内部数据结构中,该结构能够快速地找到与给定信号连接的所有槽。

5. 参数传递与转换

在调用槽函数之前,信号的参数需要与槽函数的参数进行匹配。如果槽函数的参数数量少于信号,那么多余的信号参数会被忽略。

6. 调用槽函数

  • 直接连接(Qt::DirectConnection: 如果连接类型是直接连接,则槽函数将直接(在相同的线程中)被调用,就像是一个普通的C++函数调用。

  • 队列连接(Qt::QueuedConnection: 如果连接类型是队列连接,槽函数的调用将被排入事件队列,并在稍后的事件循环中被处理。这通常会在接收者所在的线程的事件循环中完成。

7. 多对多关系

一个信号可以连接到多个槽,一个槽也可以与多个信号连接。在这种情况下,每次发射信号时,所有连接的槽都会被按照它们连接的顺序依次调用。

8. 断开连接(Disconnect)

可以使用QObject::disconnect()函数来断开已有的连接。


code:

#include <iostream>
#include <map>
#include <vector>
#include <functional>
#include <thread>
#include <queue>
#include <mutex>
#include <condition_variable>

enum ConnectionType {
    AutoConnection,
    DirectConnection,
    QueuedConnection
};

enum EventType {
    WorkDone,
    WorkStarted
};

template<typename... Args>
class Signal {
public:
    using Slot = std::function<void(Args...)>;
    void connect(EventType event, Slot slot, ConnectionType type = AutoConnection);
    void emit(EventType event, Args... args);
    void processEvents();

private:
    struct SlotWrapper {
        Slot slot;
        std::thread::id tid;
        ConnectionType type;
        SlotWrapper(Slot s, std::thread::id id, ConnectionType t) : slot(s), tid(id), type(t) {}
    };

    std::map<EventType, std::vector<SlotWrapper>> slots_;
    std::mutex mutex_;
    std::queue<std::function<void()>> taskQueue_;
    std::mutex queueMutex_;
    std::condition_variable cv_;
    bool hasPendingEvents_ = false;

    ConnectionType determineEffectiveConnectionType(const SlotWrapper& wrapper);
    void enqueueTask(std::function<void()> task);
};

template<typename... Args>
ConnectionType Signal<Args...>::determineEffectiveConnectionType(const SlotWrapper& wrapper) {
    if (wrapper.type == AutoConnection) {
        return (wrapper.tid == std::this_thread::get_id()) ? DirectConnection : QueuedConnection;
    }
    return wrapper.type;
}

template<typename... Args>
void Signal<Args...>::connect(EventType event, Slot slot, ConnectionType type) {
    std::lock_guard<std::mutex> lock(mutex_);
    slots_[event].emplace_back(slot, std::this_thread::get_id(), type);
}

template<typename... Args>
void Signal<Args...>::emit(EventType event, Args... args) {
    std::lock_guard<std::mutex> lock(mutex_);
    for (const auto& wrapper : slots_[event]) {
        ConnectionType effectiveType = determineEffectiveConnectionType(wrapper);
        if (effectiveType == DirectConnection) {
            wrapper.slot(args...);
        } else if (effectiveType == QueuedConnection) {
            enqueueTask([=]() { wrapper.slot(args...); });
        }
    }
}

template<typename... Args>
void Signal<Args...>::processEvents() {
    std::unique_lock<std::mutex> lock(queueMutex_);
    cv_.wait(lock, [&](){ return hasPendingEvents_; });
    while (!taskQueue_.empty()) {
        auto task = taskQueue_.front();
        taskQueue_.pop();
        task();
    }
    hasPendingEvents_ = false;
}

template<typename... Args>
void Signal<Args...>::enqueueTask(std::function<void()> task) {
    taskQueue_.push(task);
    hasPendingEvents_ = true;
    cv_.notify_one();
}


class Worker {
public:
    Signal<> workDone;
    void doWork() {
        workDone.emit(WorkDone);
    }
};

/* compile :  g++ -std=c++11 -lpthread example.cp */
int main() {
    Signal<> signal;
    signal.connect(WorkDone, []() {
        std::cout << "Slot executed in thread 1: " << std::this_thread::get_id() << std::endl;
    });
    signal.emit(WorkDone);

    // 模拟在其他线程connect和emit
    std::thread workerThread([&]() {
        signal.connect(WorkDone, []() {
            std::cout << "Slot executed in thread 3: " << std::this_thread::get_id() << std::endl;
        });
        signal.emit(WorkDone);
    });


    std::this_thread::sleep_for(std::chrono::seconds(1));
    // 主线程循环
    while (true) {
        signal.processEvents();
        if (!workerThread.joinable()) break;
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }

    workerThread.join();
    return 0;
}

标签:std,Qt,代码,C++,连接,wrapper,信号,Signal,void
From: https://www.cnblogs.com/dujn/p/17697426.html

相关文章

  • 三分钟读完《Essential C++》
    #include<iostream>#include<string>#include<vector>#include<memory>#include<algorithm>#include<mutex>#include<condition_variable>#include<atomic>#include<tuple>#include<map>#in......
  • 优维低代码实践:菜单
    优维低代码技术专栏,是一个全新的、技术为主的专栏,由优维技术委员会成员执笔,基于优维7年低代码技术研发及运维成果,主要介绍低代码相关的技术原理及架构逻辑,目的是给广大运维人提供一个技术交流与学习的平台。优维低代码实践连载第17期《菜单》▽在微应用中,通过菜单的配置,可以更方便......
  • bilibili B站:makefile 编译Linux C/C++项目快速入门
    视频摘自:https://www.bilibili.com/video/BV1vg41177zT    ......
  • C++模板介绍
    C++模板C++模板是一种强大的泛型编程工具,它允许我们编写通用的代码,可以用于处理多种不同的数据类型。模板允许我们在编写代码时将类型作为参数进行参数化,从而实现代码的重用性和灵活性。在C++中,模板由关键字template开始,并且后面跟着模板参数列表。模板参数可以是类型参数......
  • wkt转换成geojson的代码
    安装及导入(在安装shapely之前一定要先安装geos)pipinstallgeospipinstallshapely下面是一个使用Python将WKT转换为GeoJSON的代码示例:importjsonfromshapely.wktimportloadswkt="POINT(1010)"shape=loads(wkt)geojson=json.dumps(shape.__geo_inter......
  • 教你2种方法,将iOS设备通过MQTT协议连接到华为云物联网平台
    本文分享自华为云社区《如何将iOS设备通过MQTT协议连接到华为云物联网平台:Flutter和Swift两种方法》,作者:张俭。前言当今时代,物联网技术正逐步改变我们的生活和工作方式。华为云IoTDA服务,为开发者提供了一个开放、稳定、可靠的基础设施,以便实现设备与云端的无缝连接和双向通......
  • 上位机使用JS SerialPort进行串口通信, 包含开发环境搭建和完整示例代码
    在嵌入式开发中,我们经常需要使用上位机(PC)与一些电路模块进行通信,用于获取一些传感器的数据,或者发送命令控制相应的电路模块。NodeJS目前支持使用SerialPort模块进行串口通信,本文主要介绍如何搭建测试开发环境和如何使用SerialPort进行串口通信。 开发前准备:1.下载安装NodeJS......
  • .NET Core(C#)通过SharpCifs访问操作Windows(smb)共享目录方法代码
    .NETCore(C#)通过SharpCifs访问操作Windows(smb)共享目录方法代码本文主要介绍.NETCore中,使用SharpCifs访问windows共享目录或smb协义共享目录,或操作共享文件的方法代码。 1、SharpCifs的安装引用使用Nuget管理工具搜索"SharpCifs"=>找到选择"安装"相关文档:VS(Vis......
  • c++高精度模板
    #include<iostream>#include<stdio.h>#include<string.h>#include<math.h>#include<algorithm>#include<string>#include<vector>#include<list>usingnamespacestd;constintmaxn......
  • 程序员 AI 助手来了,蚂蚁正式开源代码大模型 CodeFuse
    9月8日,外滩大会分论坛上,蚂蚁集团首次开源了代码大模型CodeFuse。支付宝小程序云负责人李铮宣布CodeFuse正式开源这是蚂蚁自研的代码生成专属大模型,根据开发者的输入提供智能建议和实时支持,帮助开发者自动生成代码、自动增加注释,自动生成测试用例,修复和优化代码等,以提升研发......