QT基础二、信号和槽

news2025/3/26 7:57:53

一、什么是信号和槽?

1、简述

在Qt框架中,信号和槽(Signals and Slots) 是一种用于对象间通信的机制。它是一种非常强大且灵活的设计模式,广泛应用于事件驱动编程中。信号和槽机制允许对象之间以松耦合的方式进行交互,而不需要直接调用彼此的方法。

2、核心概念
  • 信号(Signal):

    • 信号是当某个特定事件发生时发出的通知。例如,按钮被点击、滑块值改变等。
    • 信号本身不执行任何操作,它只是通知其他对象发生了某些事情。
  • 槽(Slot):

    • 槽是一个普通的成员函数,可以像普通函数一样被调用。
    • 槽的主要作用是响应信号,并执行相应的逻辑。
  • 连接(Connection):

    • 信号和槽通过 QObject::connect 函数建立连接。
    • 当信号被触发时,与之连接的槽函数会被自动调用。
3、工作原理

信号和槽机制的核心思想是基于回调机制,但比传统的回调更安全和灵活。以下是其工作流程:

  1. 定义信号和槽:

    • 在类中使用 signals 关键字声明信号。
    • 使用 slots 关键字声明槽函数。
  2. 建立连接:

    • 使用 QObject::connect 将信号与槽绑定在一起。
  3. 触发信号:

    • 当某个事件发生时,信号会被发射(emit)。
    • 连接的槽函数会自动被调用。

4、既然信号和槽机制的核心思想是基于回调机制,为什么不直接使用 C/C++的回调函数,而去使用信号槽?
1 )信号与槽,是松耦合的,信号发送者,不需要去了解接收者的具体信息(有哪些接口等)
回调函数,是紧密耦合,直接调用目标对象的特定函数。
2 )信号槽,比回调函数,使用起来更灵活

5、信号和槽的特点
  • 类型安全:

    • Qt 的信号和槽机制是类型安全的。如果信号和槽的参数类型不匹配,编译器会报错。
  • 松耦合:

    • 信号和槽机制使得对象之间的依赖关系最小化。发送信号的对象不需要知道接收信号的对象是谁。
  • 多对多连接:

    • 一个信号可以连接到多个槽,多个信号也可以连接到同一个槽。
6、信号槽的缺点
1. 使用信号槽,比使用回调函数,运送速度慢:
        信号与槽函数执行,可能是异步的(仅使用直连方式连接信号槽时,槽函数才会被同步
执行,执行完之后,发送信号( emit) 语句之后的代码才会被执行。
2.使用回调函数,都是同步方式执行的。

二、信号和槽的关联(手动)

1、打开上一期的代码,在ui图搜索scroll(滚动条)

2、将垂直滚动条Vertical Scroll Bar拖动至合适位置 

取一个name

Vertical Scroll Bar ---> scrollBar

3、mainwindow.cpp的代码:
// MainWindow 构造函数定义
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)  // 调用父类构造函数,将 parent 传递给 QMainWindow 的构造函数
    , ui(new Ui::MainWindow)  // 初始化 ui 成员变量,创建一个 Ui::MainWindow 对象
{
    ui->setupUi(this);

    // 设置窗口标题为 "欢迎来到召唤师峡谷"
    setWindowTitle("欢迎来到召唤师峡谷");

    // 使用信号和槽机制连接滚动条 (QScrollBar) 和数字显示框 (QSpinBox 或 QLCDNumber)
    // 当滚动条的值发生变化时,将新的值传递给数字显示框进行更新
    connect(ui->scrollBar,                  // 滚动条对象
            SIGNAL(valueChanged(int)),      // 滚动条的 valueChanged 信号(当滚动条值变化时触发)
            ui->age,                        // 数字显示框对象
            SLOT(setNum(int)));             // 数字显示框的 setNum 槽函数(用于设置显示的数字)
}

 运行结果:

拖动滚动条,age会被改变


三、信号和槽的关联(自动)

1、添加一个label和line edit

label ---> textLabel

line edit ---> textEdit

2、右击文本框,点击转到槽,点击textChanged

 3、在生成的函数写这段代码
// 定义一个槽函数,当 textEdit 的内容发生变化时触发
void MainWindow::on_textEdit_textChanged(const QString &arg1)
{
    // 将 textEdit 的内容(通过参数 arg1 传递)设置为 textLabel 的显示文本
    ui->textLabel->setText(arg1);
}
4、运行结果

textLabel的内容会随着textEdit的内容变化而变化

注:能自动关联就尽量自动关联,一般来说,自己写的类只能手动关联


 四、自定义信号和槽

1、定义信号和槽的基本规则 
  • 信号(Signal):

    • 使用 signals 关键字声明。
    • 信号没有函数体,只需声明函数签名。
    • 信号通过 emit 关键字触发。
  • 槽(Slot):

    • 使用 slots 关键字声明(在 Qt 5 及以上版本中,普通成员函数也可以作为槽)。
    • 槽函数可以有函数体,像普通成员函数一样实现逻辑。
  • 连接信号和槽:

    • 使用 QObject::connect 函数将信号与槽绑定

2、添加一个MyClass类

注:开发中不会让信号和槽出现在同一个类,以下都是演示效果

 3、在MyClass.h写入一下代码
#ifndef MYCLASS_H
#define MYCLASS_H


#include <QApplication>
#include <QObject>
#include <QDebug>

// 自定义类,继承自 QObject
class MyClass : public QObject {
    Q_OBJECT  // 必须包含此宏,用于启用元对象系统

public:
    MyClass(QObject *parent = nullptr) : QObject(parent) {}

    // 定义一个槽函数
    void mySlot(const QString &message) {
        qDebug() << "接收到消息:" << message;
    }

signals:
    // 定义一个信号
    void mySignal(const QString &message);

public:
    // 触发信号的函数
    void doSomething() {
        QString message = "Hello, World!";
        emit mySignal(message);  // 使用 emit 关键字发射信号
    }

};

#endif // MYCLASS_H
4、在main.cpp添加一下代码
#include "mainwindow.h"
#include "myclass.h"
#include <QApplication>

void test()                //测试用的函数
{
    // 创建两个对象
    MyClass sender;   // 发送信号的对象
    MyClass receiver; // 接收信号的对象

    // 连接信号和槽
    QObject::connect(&sender,
         &MyClass::mySignal,
         &receiver,
         &MyClass::mySlot);

    // 触发信号
    sender.doSomething();
}

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    test();
    //MainWindow w;
    //w.show();
    return a.exec();
}
5、运行结果


五、信号和槽的连接方式

1、自动连接(默认方式)
  • 特点:
    • 如果信号和槽在同一线程中,使用直接连接(Qt::DirectConnection)。
    • 如果信号和槽在不同线程中,使用队列连接(Qt::QueuedConnection)。
  • 适用场景:
    • 默认情况下,QObject::connect 使用自动连接方式。
QObject::connect(sender, 
        &Sender::mySignal, 
        receiver, 
        &Receiver::mySlot);
2、 直接连接(Qt::DirectConnection
  • 特点:
    • 槽函数在信号发出的线程中立即执行。
    • 适用于同一线程中的对象通信。
  • 适用场景:
    • 当需要快速响应信号时。
QObject::connect(sender, 
        &Sender::mySignal, 
        receiver, 
        &Receiver::mySlot, 
        Qt::DirectConnection);
        效果类似于“函数调用”,同步执行: 使用 emit 发送信号后,槽函数被直接调用,调用完成之后,再执行 emit 之后的语句。

3、队列连接(Qt::QueuedConnection
  • 特点:
    • 槽函数在接收者的线程中异步执行。
    • 适用于跨线程通信。
  • 适用场景:
    • 当信号发出者和接收者位于不同的线程时。
QObject::connect(sender, 
        &Sender::mySignal, 
        receiver, 
        &Receiver::mySlot, 
        Qt::QueuedConnection);
        效果类似于“异步函数调用”。当信号发出后,信号被添加到“信号队列”中,需等到接收对象所属线程的事件循环取得控制权时才取得该信号,再调用相应的槽函数。 emit 发送信号后,直接执行 emit 后面的代码,不需要等待槽函数执行完毕。

4、阻塞队列连接(Qt::BlockingQueuedConnection
  • 特点:
    • 类似于队列连接,但会阻塞信号发出的线程,直到槽函数执行完成。
    • 仅适用于跨线程通信。
  • 适用场景:
    • 当需要确保槽函数执行完成后再继续信号发出者的逻辑时。
  • 注意:
    • 如果信号发出者和接收者在同一线程中使用此连接方式,会导致死锁。
QObject::connect(sender, 
        &Sender::mySignal, 
        receiver, 
        &Receiver::mySlot, 
        Qt::BlockingQueuedConnection);
        与队列连接的基础上,加上阻塞发送信号所在的线程。 用于在不同线程之间进行对象之间的通信。 它可以确保发送者在发出信号后立即等待接收者处理完槽函数后才继续执行。 当信号被触发时,发送者会阻塞直到接收者处理完对应的槽函数,并且该槽函数会在接收者所属的线程中执行。这种连接类型适用于需要不同线程需要同步处理的情况。

5、独占连接(Qt::UniqueConnection
  • 特点:
    • 确保同一个信号和槽只能连接一次。
    • 如果尝试重复连接,QObject::connect 会返回 false
  • 适用场景:
    • 当需要避免重复连接时。
bool success = QObject::connect(sender, 
                &Sender::mySignal, 
                receiver, 
                &Receiver::mySlot, 
                Qt::ConnectionType(Qt::AutoConnection|Qt::UniqueConnection));
if (!success) {
    qDebug() << "连接失败:信号和槽已存在连接。";
}
        不能单独使用,需要和其他类型组合使用, 用来确保指定的发送者、指定的信号、指定的接受者、指定的槽,只存在唯一的一种连接。
即,用来避免:
1. 避免信号和槽,以某种方式连接后,然后又调用 connect 以另一种方式连接
2. 避免建立多个重复的信号槽后,一个信号,将导致重复发送多个信号
注:一个信号可以关联多个槽,一个槽也可以关联多个信号

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2300153.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【深度学习】计算机视觉(CV)-目标检测-Faster R-CNN —— 高精度目标检测算法

1.什么是 Faster R-CNN&#xff1f; Faster R-CNN&#xff08;Region-based Convolutional Neural Network&#xff09; 是 目标检测&#xff08;Object Detection&#xff09; 领域的一种 双阶段&#xff08;Two-Stage&#xff09; 深度学习方法&#xff0c;由 Ross Girshick…

Blazor-父子组件传递任意参数

在我们从父组件传参数给子组件时&#xff0c;可以通过子组件定义的[Parameter]特性的公开属性进行传值&#xff0c;但是当我们需要传递多个值的时候&#xff0c;就需要通过[Parameter]特性定义多个属性&#xff0c;有没有更简便的方式&#xff1f; 我们可以使用定义 IDictionar…

【原创】vue-element-admin-plus完成编辑页面中嵌套列表功能

前言 vue-element-admin-plus对于复杂业务的支持程度确实不怎么样&#xff0c;我这里就遇到了编辑页面中还要嵌套列表的真实案例&#xff0c;比如字典&#xff0c;主字典嵌套子信息&#xff0c;类似于一个树状结构。目前vue-element-admin-plus给出的例子是无法满足这个需求的…

DeepSeek教unity------MessagePack-02

内置支持类型&#xff1a; 对象序列化 MessagePack for C# 可以序列化你自己定义的公共类或结构体类型。默认情况下&#xff0c;可序列化的类型必须用 [MessagePackObject] 属性进行注解&#xff0c;成员需要用 [Key] 属性进行注解。键可以是索引&#xff08;整数&#xff09;…

H5应用抓包及调试技巧

由于图片和格式解析问题&#xff0c;可前往 阅读原文 在现代移动互联网时代&#xff0c;H5 应用以其跨平台、轻量化、快速迭代的特性&#xff0c;成为移动开发的重要一环。然而&#xff0c;随着功能的复杂化和用户体验要求的提升&#xff0c;H5应用的调试也面临着诸多挑战&…

自学Java-面向对象高级(final、单例类、枚举类、抽象类、接口)

自学Java-面向对象高级&#xff08;final、单例类、枚举类、抽象类、接口&#xff09; 一、final关键字1、认识final关键字2、final修饰变量的注意3、常量 二、单例类&#xff08;设计模式&#xff09;1、设计模式的概念2、单例设计模式3、单例类有很多形式4、懒汉式单例类5、小…

docker下部署kong+consul+konga 报错问题处理

前言&#xff1a; 由于在docker下部署一些项目比较特殊&#xff0c;特别是网络这一块&#xff0c;如果没有搞清楚&#xff0c;是很容易出问题的。 先上docker-compose 编排 这里的docker-compose for kong可以在 kong-compose 获取代码 version: 3.9x-kong-config:&kong…

网络优化工作流程

DT路测 移动测试&#xff08;Drive Test&#xff09; CQT 定点测试&#xff08;通信质量测试&#xff09; DT 测试不能体现实际话务质量&#xff1a;回音、串音等网络问题不能通过 DT 测试发现&#xff0c;因此 CQT 拨打测试是 DT 测试很好的补充&#xff0c;也是目前室内外测…

React入门 - 0.React简介

React入门 - React简介 A Brief Introduction to React By JacksonML 1. 关于React React是一个知名的Web框架。众所周知&#xff0c;jQuery, Angular, Vue等框架都曾闪亮登场&#xff0c;并且&#xff0c;都仍然在全球市场占有一席之地。React这个颇有担当的新锐&#xff0…

SpringCloud系列教程:微服务的未来(二十四)Direct交换机、Topic交换机、声明队列交换机

前言 在现代消息队列系统中&#xff0c;交换机是实现消息传递和路由的核心组件。本文将重点探讨三种常见的交换机类型&#xff1a;Direct交换机、Topic交换机和声明队列交换机。通过对这三种交换机的详细分析&#xff0c;我们将学习它们的工作原理、应用场景以及如何在实际项目…

Sojson高级加密技术科普

1. 引言 什么是Sojson&#xff1f; Sojson是一款用于JavaScript代码加密与混淆的工具&#xff0c;它能够有效保护前端代码的知识产权&#xff0c;避免开发者的心血被随意窃取。 为什么需要代码加密&#xff1f; 在当今的互联网环境下&#xff0c;代码被轻易复制、篡改或逆向…

mysql多主集群 galera cluster for mysql 8安装配置启动重启集群

[TOC] 一、安装mysql 1、安装 系统环境&#xff1a; Ubuntu 18.04 64位 MySQL 8.0.19 下载MySQL APT安装配置包 首先访问 https://dev.mysql.com/downloads/repo/apt/ 获取配置包下载地址 wget https://dev.mysql.com/get/mysql-apt-config_0.8.14-1_all.deb sudo dpkg -i mysq…

mybatis 入门案例

前言 我们清楚 mybatis 是一个持久层框架&#xff0c;可以非常便捷的操作数据库。如最常见的对数据进行增删改查操作。 项目准备 1 在mybatis 数据库 创建 user 用户表 并插入以下两条数据 以下是一个user.sql 脚本文件如何使用 脚本文件可以参照MySQL数据库的备份与还原_控…

Python使用Flask结合DeepSeek开发

一、背景 我之前关于DeepSeek使用ollama部署的文章大家可以把DeepSeek大模型部署起来。那么ollama还提供了可以调用对应部署模型的API接口。我们可以基于这些接口&#xff0c;做自己的二次开发。使用pythonflaskollama就可以进行模型对话调用。并且前端采用SSE的技术&#xff0…

前端常见面试题-2025

vue4.0 Vue.js 4.0 是在 2021 年 9 月发布。Vue.js 4.0 是 Vue.js 的一个重要版本&#xff0c;引入了许多新特性和改进&#xff0c;旨在提升开发者的体验和性能。以下是一些关键的更新和新特性&#xff1a; Composition API 重构&#xff1a;Vue 3 引入了 Composition API 作为…

大模型开发实战篇7:语音识别-语音转文字

语音识别大模型&#xff0c;是人工智能领域的一项重要技术&#xff0c;它能够将人类的语音转换为文本。近年来&#xff0c;随着深度学习技术的不断发展&#xff0c;语音识别大模型取得了显著的进展&#xff0c;并在各个领域得到了广泛应用。 主流语音识别大模型 目前&#xf…

QML 部件获得焦点触发的全局槽函数 onActiveFocusItemChanged

在qml的window窗口中&#xff0c;假如添加里许多其他部件&#xff0c;当这些部件改变时&#xff0c;会有一个全局部件焦点改变槽函数触发&#xff0c;就是 onActiveFocusItemChanged 可以通过此槽函数就可以知道当前焦点在哪一个部件上&#xff0c;也可以做一些自动化测试等&…

如何用ClassFinal加密JAR保护知识产权!

0.前言 凌晨三点的办公室&#xff0c;咖啡杯底凝着褐色的残渍&#xff0c;键盘上跳跃的手指突然停滞。张工程师盯着屏幕上的反编译窗口&#xff0c;自己耗时三个月开发的规则引擎此刻像被解剖的标本般赤裸裸摊开——这正是上周交付给客户的jar包。当.class文件以伪代码形式暴露…

轨迹优化 | 基于LBFGS优化器的无约束路径平滑(附ROS C++仿真)

目录 0 专栏介绍1 LBFGS优化器1.1 拟牛顿法框架1.2 LBFGS-Lite库 2 基于LBFGS的轨迹优化3 ROS C仿真 0 专栏介绍 &#x1f525;课设、毕设、创新竞赛必备&#xff01;&#x1f525;本专栏涉及更高阶的运动规划算法轨迹优化实战&#xff0c;包括&#xff1a;曲线生成、碰撞检测…

Vue2到Vue3:无痛升级之路

为什么要从 Vue2 升级到 Vue3 Vue 3 带来了众多令人瞩目的改进和新特性&#xff0c;这些优势使得升级到 Vue 3 对项目的长期发展具有重要意义。 性能显著提升&#xff1a;Vue 3 采用了基于 Proxy 的响应式系统&#xff0c;相比 Vue 2 使用的 Object.defineProperty&#xff0c…