QT入门-信号与槽

1.QT基本框架

#include "myWindow.h"#include <QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);myWindow w;w.show();return a.exec();
}

QApplicata:应用程序对象,必须有且只能有一个

Qwidget:qt自带空窗口类,即运行时的窗口。

myWindow:自定义窗口类,继承自QWidget。

a.exec():进入消息循环,除非人为结束进程,否则阻塞在这里等待用户输入,死循环。

qmake:qt的编译器。

.pro文件:qt的项目文件。

 拖动按钮到窗口中,然后右键转到槽,自定义实现功能

#include "mainwindow.h"
#include "ui_mainwindow.h"#include<QMessageBox>
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::on_pushButton_clicked()
{
//this表示设置父对象为MainWindowQMessageBox::information(this,"hello","my first qt application!");
}

 

运行:
 

2.对象树

对象树是qt中非常重要的内容,主要用于自动析构对象。

在qt中,我们自定义的类可以继承自qt中已有的类。比如上述myWindow就是继承自QWidget。

QWidget又是继承自QObject。

在qt中QObject是所有类的祖先,向下生成了许多子类,这样一个关系就叫做对象树。

 (注意这里的父子关系并不是语法里面的父子继承关系,而是qt里面的一种机制)

使用时,需要通过setParent函数将对象间确定父子关系,之后子对象会进入父对象的children列表。 

当析构时,会先从父对象开始,先走父对象析构函数(注意,只是析构没有真正释放),然后依旧children列表析构子对象,直到走到叶子对象后,释放叶子对象,再返回父对象释放,直到回到一开始的父对象。

当释放一个对象时,会把它所有的子对象都释放掉,因此,之后不能再去释放子对象,否则会发生二次释放的错误。

错误代码1:

int main(int argc, char *argv[])
{QApplication a(argc, argv);myWidget * c = new myWidget;QWidget * s = new QWidget;c->setParent(s);//s是c的父对象delete s;//删释放s同时也会将c释放delete c;//错误,二次释放return 0;
}

更改:

int main(int argc, char *argv[])
{QApplication a(argc, argv);myWidget * c = new myWidget;QWidget * s = new QWidget;c->setParent(s);//s是c的父对象delete s;//删释放s同时也会将c释放return 0;
}

错误代码2:

int main(int argc, char *argv[])
{QApplication a(argc, argv);myWidget c;QWidget s;// s后定义,结束时先析构c.setParent(&s);//s为父,c为子return 0;//发生错误,s先析构会把c也析构,//之后c调用析构函数时会二次析构
}

更改:

int main(int argc, char *argv[])
{QApplication a(argc, argv);QWidget s;//s析构子对象时发现c已经被析构了,就不会在析构了,这是qt中实现的myWidget c;c.setParent(&s);return 0;
}

防止二次析构的实现:

Qt 并不直接使用智能指针来管理对象树中的对象生命周期(如 std::shared_ptrQSharedPointer),但它的机制类似于引用计数。每个 QObject 都知道有多少子对象以及它们是谁,而子对象知道它们的父对象是谁。当对象被删除时,它会通知其父对象和子对象调整它们的指针和列表

3.信号与槽

1.1 信号和槽

1,信号和槽(Signal&Slot)

  • 信号与槽(Signal&Slot)是Qt的基础,也是Qt的一大创新,因为有了信号与槽的编程机制,在Qt中处理 界面各组件的交互操作时,变得更加直观和简单,它可以让app开发人员把互不了解的对象绑定在一起

2,信号(Signal)

  • 信号就是在特定情况下被发送的事件,eg:PushButton最常见的信号就是鼠标单击时发送的clicked()信号,一个ComboBox最常见的信号是选择的列表项变化时发送的CurrentIndexChanged()信号。
  • GUI程序设计主要就是对界面各组件的信号相应,只需要知道什么情况下发送哪些信号,合理去响应和处理这些信号即可。

3,槽(Slot)

  • 槽就是对信号的响应函数,与一般的C++函数一样,可以定义在类的public,private,protect,可以具有任何参数,也可以直接被调用
  • 槽函数与一般函数不同的是,槽函数可以与一个信号关联,当信号被发送,关联的槽函数被自动执行
  • 信号和槽关联是用,QObject.connect()函数实现的,基本格式为:
QObject::connect(sender, SIGNAL(signal()), receiver, SLOT(slot()));
参数1:信号的发送者
参数2:发送的信号
参数3:信号的接收者
参数4:处理函数(槽函数)

实例:信号与槽示例QPushButton

信号

1.我们打开Qt助手,搜索QPushButton,发现没有signal,只有个public slot

2.找QPushButton的父类,QAbstractButton,找到signals 

3.可以看到里面有4个信号

槽函数:

1.自动生成的槽函数一般都是定义在窗口类中的

2.同时,我们使用的很多槽函数都是定义在QWidge(MainWindow的父类)中的

代码实现:

#include "mainwindow.h"
#include "ui_mainwindow.h"#include<QMessageBox>
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);//由于connect时QOBJECT下的成员函数,而QWidget继承于QOBJECT,因此阔以直接使用connect(ui->mybutton,&QPushButton::clicked,this,&QWidget::close);
}MainWindow::~MainWindow()
{delete ui;
}//使用
void MainWindow::on_pushButton_clicked()
{QMessageBox::information(this,"hello","my first qt application!");
//    QWidget::close();
}

注意

1:

通过ui界面拖动的组件,定义在ui对象中,要想使用就通过ui->来调用

private:Ui::MainWindow *ui;

 2:
信号和槽传入的指针而不是函数,下面的写法是错的:

    connect(ui->mybutton,&QPushButton::clicked(),this,&QWidget::close());

 3:一个信号可以触发多个槽,这里就将ui定义的组件重新connect到close上了

结果:点击按键后窗口关闭

 1.2 自定义信号与槽

 在上面的工程添加类

定义两个类 

 

 1.signal

头文件如下: 

#ifndef MYSIGNAL_H
#define MYSIGNAL_H#include<QObject>
//为signals添加头文件
class mysignal : public QObject
{Q_OBJECT
public:explicit mysignal(QObject *parent = nullptr);
signals:void send_signal();
};#endif // MYSIGNAL_H

signal是一个宏,不是c++语法内容,我们定义一个信号后,不需要实现,直接通过

//emit是发送信号的标识,不写会报警告,版本问题,(非必须)emit obj.send_signal();

 就能实现信号的发送,因此,cpp文件不用写

2.slot

头文件如下: 

#ifndef MYSLOT_H
#define MYSLOT_H
#include<QObject>class myslot : public QObject//要写上public
{Q_OBJECT
public:explicit myslot(QObject *parent = nullptr);//防止隐式转换的写法
signals:
public slots:void ack();//需要实现
};#endif // MYSLOT_H

注意slots的格式

cpp文件:

#include "myslot.h"
#include<QDebug>
myslot::myslot(QObject * parent) : QObject(parent)
{}void myslot::ack()
{qDebug()<<"recv!!!";
}

3.main

#include "mainwindow.h"#include <QApplication>
#include <QLocale>
#include <QTranslator>
#include <mysignal.h>
#include <myslot.h>int main(int argc, char *argv[])
{QApplication a(argc, argv);QTranslator translator;const QStringList uiLanguages = QLocale::system().uiLanguages();for (const QString &locale : uiLanguages) {const QString baseName = "myfirst_qt_" + QLocale(locale).name();if (translator.load(":/i18n/" + baseName)) {a.installTranslator(&translator);break;}}MainWindow w;mysignal s(&w);myslot r(&w);w.connect(&s,&mysignal::send_signal,&r,&myslot::ack);emit s.send_signal();w.show();return a.exec();
}

效果:

1.3 信号与槽函数的重载

1.直接重载

报错如下:

2.两种通过函数指针实现重载的方式

 1.普通函数指针
返回类型 (*指针名称)(参数类型列表);

 案例:

int add(int x, int y) {return x + y;
}int main() {// 声明一个指向函数的指针int (*funcPtr)(int, int) = &add;// 使用函数指针调用函数int result = funcPtr(3, 4); // 等同于 add(3, 4)return 0;
}
2.类成员函数指针 

 类如下:

class Calculator {
public:int add(int x, int y) {return x + y;}
};

实现:

int main() {Calculator calc;// 声明一个指向成员函数的指针int (Calculator::*calcPtr)(int, int) = &Calculator::add;// 使用成员函数指针调用函数int result = (calc.*calcPtr)(5, 6); // 等同于 calc.add(5, 6)return 0;
}
 方法一:
    void (mysignal::*send_str)(QString&) = &mysignal::send_signal;void (myslot::*ack_str)(QString&) = &myslot::ack;w.connect(&s,send_str,&r,ack_str);

先定义一个带指定参数的函数指针,然后指向对应的信号或者槽,由于参数类型确定,那么会自动给函数指针匹配合适的重载函数

方法二:
w.connect(&s,(void(mysignal::*)(QString&))&mysignal::send_signal,&r,(void(myslot::*)(QString&))&myslot::ack);

节约行数,但是可读性差,不推荐 

核心:注意信号重载函数指针指向了哪一个函数,对重载函数的信号连接时,要指明到底连接的是哪一个槽函数

效果:

 mian代码:

#include "mainwindow.h"#include <QApplication>
#include <QLocale>
#include <QTranslator>
#include <mysignal.h>
#include <myslot.h>int main(int argc, char *argv[])
{QApplication a(argc, argv);QTranslator translator;const QStringList uiLanguages = QLocale::system().uiLanguages();for (const QString &locale : uiLanguages) {const QString baseName = "myfirst_qt_" + QLocale(locale).name();if (translator.load(":/i18n/" + baseName)) {a.installTranslator(&translator);break;}}MainWindow w;mysignal s(&w);myslot r(&w);//    w.connect(&s,&mysignal::send_signal,&r,&myslot::ack);
#if 0void (mysignal::*send_str)(QString&) = &mysignal::send_signal;void (myslot::*ack_str)(QString&) = &myslot::ack;w.connect(&s,send_str,&r,ack_str);
#elsew.connect(&s,(void(mysignal::*)(QString&))&mysignal::send_signal,&r,(void(myslot::*)(QString&))&myslot::ack);
#endifemit s.send_signal();QString str = "jjj";emit s.send_signal(str);w.show();return a.exec();
}

 1.4 信号和槽的扩展

1.信号链接信号

 定义一个按键,先按键触发信号,然后信号触发槽

    mysignal s(&w);myslot r(&w);QPushButton bnt;bnt.setParent(&w);bnt.setText("触发信号");
//    w.connect(&s,&mysignal::send_signal,&r,&myslot::ack);
#if 1void (mysignal::*send_str)() = &mysignal::send_signal;void (myslot::*ack_str)() = &myslot::ack;void (mysignal::*send_str1)() = &mysignal::send_signal;w.connect(&bnt,&QPushButton::clicked,&s,send_str1);w.connect(&s,send_str,&r,ack_str);
//    emit s.send_signal();
//    QString str = "jjj";
//    emit s.send_signal(str);w.show();return a.exec();

效果:

点击后触发槽函数:

即:

信号连接信号,一个消息信号可以绑定多个槽,同样的,一个槽函数也可以绑定多个消息信号。(多对多关系

2.在ui函数中使用自定义变量

 1.如果变量定义在外面,无法进行connect:

2.如果变量定义在里面:
 

由于时局部变量,无法长期维持, mysignalmyslot 的实例 sr 是在 MainWindow 构造函数的局部作用域中创建的。这意味着它们只在构造函数执行期间存在,一旦构造函数执行完毕,这些局部变量就会被销毁。因此,当您点击按钮试图触发信号时,sr 的实例可能已经不存在了,导致信号无法被成功触发或接收。

如果执意运行,会发现点击按钮无响应(s,r早被销毁了) 

3.使用成员函数:

效果:

3.信号的断开-disconnect()

    connect(ui->mybutton,&QPushButton::clicked,&s,send_str1);connect(&s,send_str,&r,ack_str);disconnect(&s,send_str,&r,ack_str);

 效果:

点击无反应(连接已经断开) 

 1.5 lambda辅助槽函数

1.为何要用lambda 

有时候槽函数的内容不简单,但是需要在各处定义槽函数和其实现,那么全部重写很麻烦,由此引入lambda的使用

直接在要用槽函数的地方(connect处),把函数整个传入当作参数,这样就不用先定义再实现再调用了,简便很多

比如下例:

    connect(ui->bnt1,&QPushButton::clicked,this,[this]()mutable{a+=10;qDebug()<<a;});

简单的功能直接使用lambda实现,避免了重复的简单操作 

2.lambda解析

  • 捕获列表。在C++规范中也称为Lambda导入器, 捕获列表总是出现在Lambda函数的开始处。实际上,[]是Lambda引出符。编译器根据该引出符判断接下来的代码是否是Lambda函数,捕捉上下文中的变量以供Lambda函数使用。(能够使用的变量,不是传入参数)
    • []中括号函数对象里面对应参数有以下形式:
      空,没有使用任何函数对象参数
      =,函数体内使用Lambda所在范围内的可见局部变量,包括所在类的this的传值方式,相当于编译器给Lambda所在范围的所有局部变量**赋值**一份给Lambda函数
      &,函数体内使用Lambda所在范围内的可见局部变量,包括所在类的this的引用方式,相当于编译器给Lambda所在范围的所有局部变量**引用**一份给Lambda函数
      this,函数体内可以使用Lambda所在内的成员变量
      a,不是字母,而是指具体一个变量a,Lambda内拷贝一个变量a使用
      &a,Lambda内引用变量a
      a, &b,拷贝a,引用b
      =,&a,&b,除a,b引用外,其他变量做拷贝操作
      &,a,b,除a,b拷贝外,其他变量做引用操作
  • 参数列表与普通函数的参数列表一致。如果不需要参数传递,则可以连同括号“()”一起省略。
  • 可变规格。mutable修饰符, 默认情况下Lambda函数总是一个const函数,mutable可以取消其常量性。在使用该修饰符时,参数列表不可省略(即使参数为空)使用后我们就可以改变捕获列表中变量的值
  • 异常说明。用于Lamdba表达式内部函数抛出异常。
  • 返回类型。 追踪返回类型形式声明函数的返回类型。我们可以在不需要返回值的时候也可以连同符号”->”一起省略。此外,在返回类型明确的情况下,也可以省略该部分,让编译器对返回类型进行推导。
  • lambda函数体内容与普通函数一样,不过除了可以使用参数之外,还可以使用所有捕获的变量。

 3.lambda在QT中的使用-无参数的信号调用有参数的槽函数

在上文代码的基础上实现以下连接 

    connect(ui->mybutton,&QPushButton::clicked,&s,send_str1);
//    connect(&s,send_str,&r,ack_str);QString str = "hello";//注意QString& 不能接受const char字符串,因此这里将前面的ack参数改为了QStringconnect(&s,send_str,&r,[=](){r.ack("aaaaaaa");});

 效果:

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

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

相关文章

Python入门:常用模块—os模块及sys模块

os模块 sys模块 import sys print(sys.argv) # 命令参数list&#xff0c;第一个元素是程序本身路径 print(sys.exit()) # 退出程序&#xff0c;正常退出是exit(0) print(sys.version) # 获取python解释程序的版本信息 print(sys.maxint()) # 最大…

【开源】JAVA+Vue.js实现衣物搭配系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、研究内容2.1 衣物档案模块2.2 衣物搭配模块2.3 衣物收藏模块 三、系统设计3.1 用例设计3.2 E-R图设计3.3 数据库设计3.3.1 衣物档案表3.3.2 衣物搭配表3.3.3 衣物收藏表 四、系统实现4.1 登录页4.2 衣物档案模块4.3 衣物搭配模块4.4…

Zustand:简化状态管理的现代React状态库

Zustand&#xff1a;简化状态管理的现代React状态库 Zustand是一个用于管理状态的现代React状态库。它提供了简洁、可扩展和高效的状态管理解决方案&#xff0c;使得在React应用中处理复杂的状态逻辑变得更加容易和直观。本文将介绍Zustand的主要特点、使用方法以及它在React开…

Zabbix6.x配置中文界面 解决乱码问题

Zabbix6.x配置中文界面 解决乱码问题 Zabbix6.x界面无法选择中文&#xff0c;通过安装语言包解决。后面也解决了zabbix6中文方块&#xff08;乱码&#xff09;问题。 配置中文语言包 系统中默认没有携带中文语言包&#xff0c;可以通过以下命令查看 localectl list-locales #…

STM32CubeMX,定时器之定时功能,入门学习,如何设置prescaler,以及timer计算PWM输入捕获方法(重要)

频率变小&#xff0c;周期变长 1&#xff0c;参考链接&#xff08;重要&#xff09; STM32CubeMX——定时器之定时功能&#xff08;学习使用timer定时器的设置&#xff09; STM32测量PWM信息&#xff08;学习使用设置pwm输入捕获&#xff09; 通用定时器中两个重要参数的设置心…

计算机网络——04接入网和物理媒体

接入网和物理媒体 接入网络和物理媒体 怎样将端系统和边缘路由器连接&#xff1f; 住宅接入网络单位接入网络&#xff08;学校、公司&#xff09;无线接入网络 住宅接入&#xff1a;modem 将上网数据调制加载到音频信号上&#xff0c;在电话线上传输&#xff0c;在局端将其…

自己动手打包element UI官方手册文档教程

经常用element ui朋友开发的比较郁闷&#xff0c;官方文档网基本上都是打不开的&#xff0c; 官方&#xff1a;https://element.eleme.io/ 一直打不开&#xff0c;分析下是里面用的cdn链接ssl证书无效。 就想着自己搭建一个element UI文档 自己搭建的&#xff1a; Element文档网…

精灵图,字体图标,CSS3三角

精灵图 1.1为什么需要精灵图 一个网页中往往会应用很多小的背景图像作为修饰&#xff0c;当网页中的图像过多时&#xff0c;服务器就会频繁的接受和发送请求图片&#xff0c;造成服务器请求压力过大&#xff0c;这将大大降低页面的加载速度。 因此&#xff0c;为了有效地减少…

SpringBoot 接入讯飞星火大模型实现对话

申请地址 https://xinghuo.xfyun.cn/sparkapi?scrprice 免费申请200万Token 开发文档 https://www.xfyun.cn/doc/spark/Web.html#_1-接口说明 页面最下面有相关demo可以参考 介绍 接口是以套接字的形式分段返回&#xff0c;而且非http请求&#xff0c;比较繁琐&#xff0c;官…

fast.ai 深度学习笔记(三)

深度学习 2&#xff1a;第 1 部分第 6 课 原文&#xff1a;medium.com/hiromi_suenaga/deep-learning-2-part-1-lesson-6-de70d626976c 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 来自 fast.ai 课程的个人笔记。随着我继续复习课程以“真正”理解它&#xff0c;这…

模型蒸馏distill /模型剪枝 论文汇总

文章目录 引言1. 蒸馏1&#xff09;白盒蒸馏DistilBERTPatient Knowledge DistillationTinyBERT 2020MiniLLM 2&#xff09;黑盒蒸馏Stanford alpacaVicunaWizardlmInstruction tuning with gpt-4Minigpt-4 2. 剪枝16个注意力头比一个好吗Movement pruning 1&#xff09;结构化…

【实习】深信服防火墙网络安全生产实习

一、实习概况 1.1实习目的 1.掌握防火墙规则的作用2.掌握代理上网功能的作用3.掌握端口映射功能的作用 1.2实习任务 1.防火墙的WEB控制台 2.需要在防火墙上配置dnat …

【设计模式】23中设计模式笔记

设计模式分类 模板方法模式 核心就是设计一个部分抽象类。 这个类具有少量具体的方法&#xff0c;和大量抽象的方法&#xff0c;具体的方法是为外界提供服务的点&#xff0c;具体方法中定义了抽象方法的执行序列 装饰器模式 现在有一个对象A&#xff0c;希望A的a方法被修饰 …

CVE-2021-44915 漏洞复现

CVE-2021-44915 路由/admin/admin.php是后台&#xff0c;登录账号和密码默认是admin、tao&#xff0c;选择管理栏目菜单。 点击编辑&#xff0c;然后随便改点内容&#xff0c;提交时候抓包。 id是注入点。直接拿sqlmap跑就行了。

20240210使用剪映识别字幕的时候的GPU占比RX580-RTX4090

20240210使用剪映识别字幕的时候的GPU占比RX580-RTX4090 2024/2/10 17:54 【使用剪映识别不同的封装格式&#xff0c;不同的音视频编码&#xff0c;对GPU的占用率可能会有比较大的不同&#xff01;】 很容易发现在在WIN10下使用剪映的时候&#xff0c;X99RX550组合。 GPU部分&…

探索C语言的内存魔法:动态内存管理解析

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;C语言学习 贝蒂的主页&#xff1a;Betty‘s blog 1. 静态开辟内存 通过前面的学习&#xff0c;我们已经掌握了两种开辟内存的方…

ChatGPT高效提问—prompt常见用法(续篇八)

ChatGPT高效提问—prompt常见用法(续篇八) 1.1 对抗 ​ 对抗是一个重要主题,深入探讨了大型语言模型(LLM)的安全风险。它不仅反映了人们对LLM可能出现的风险和安全问题的理解,而且能够帮助我们识别这些潜在的风险,并通过切实可行的技术手段来规避。 ​ 截至目前,网络…

4核8g服务器能支持多少人访问?2024新版测评

腾讯云轻量4核8G12M轻量应用服务器支持多少人同时在线&#xff1f;通用型-4核8G-180G-2000G&#xff0c;2000GB月流量&#xff0c;系统盘为180GB SSD盘&#xff0c;12M公网带宽&#xff0c;下载速度峰值为1536KB/s&#xff0c;即1.5M/秒&#xff0c;假设网站内页平均大小为60KB…

酷开系统 | 拓展内容营销边界,酷开科技大屏价值全面升维

丰富的内容是智能大屏吸引消费者的关键。随着智能大屏各类垂直应用的增多&#xff0c;和长、短视频等多元内容的加入&#xff0c;使消费者的使用需求进一步激发和释放&#xff0c;这些流量的加入&#xff0c;也使大屏成为了营销的天然宝藏。酷开科技一直致力于OTT大屏营销&…

《杨绛传:生活不易,保持优雅》读书摘录

目录 书简介 作者成就 书中内容摘录 良好的家世背景&#xff0c;书香门第为求学打基础 求学相关 念大学 清华研究生 自费英国留学 法国留学自学文学 战乱时期回国 当校长 当小学老师 创造话剧 支持钱锺书写《围城》 出任震旦女子文理学院的教授 接受清华大学的…