Qt入门日记1
目录
1.Qt简介和案例
2.第一个Qt程序
3.学会查看帮助文档
4.创建一个按钮
5.对象树简介
6.Qt的坐标系
7. 信号和槽
7.1自定义信号和槽
7.2信号连接信号
7.3拓展
7.4Qt4版本以前的connect
1.Qt简介和案例
Qt是一个跨平台的C++图形用户界面应用程序框架(就是一个库吧),它是完全面向对象的,允许真正意义上的组件式编程。
Qt是Linux桌面环境KDE的基础,它有商业版和开源版。
Qt的成功案例有很多,例如Linux桌面环境KDE、WPS等等。
它的优点如下:
- 跨平台,几乎支持所有平台
- 接口简单
- 一定程度上简化了内存回收机制(对象树)
- 可以进行嵌入式开发
2.第一个Qt程序
在创建项目时会提示选择三个基类当中的某一个:

这三个基类之间的关系如下:

QMainWindow和QDialog继承自QWidget:
- QWidget是一个空白窗口
- QMainWindow是一个带有菜单栏、工具栏、状态栏等等的一个窗口
- QDialog是一个提示对话框
项目创建完成后会有一个".pro"文件,这个文件时当前项目的工程文件,里面有一些很重要的内容,不要不要随意修改".pro"文件的内容甚至不要加注释。

可以看到默认情况下,Qt包含了core模块和gui模块,如果是4版本以上的话还会添加一个widgets模块。原因在于4版本以前widgets模块属于gui模块,4版本以后将widgets独立出来了。
当前项目自动构建出了一些源文件和头文件,可以很轻松的找到主函数:

其中,应用程序对象有且仅有一个, Widget类继承自QWidget,调用该类的show方法可以单独显示一个窗口:

注意Widget类定义当中的"Q_OBJECT",这是一个宏,它的作用是让该类支持信号和槽机制。还要注意其构造函数,有一个名为parent指向QWidget类的指针,这与对象树有关,稍后做解释。
3.学会查看帮助文档
学习任何技术都需要有帮助文档,Qt提供了这样的帮助文档。选中关键字并且按下"F1"可以弹出帮助文档:

还有一种方法是直接打开帮助文档的独立引用程序,它的位置在"Qt安装路径/版本号/mingw_64/bin"下:

4.创建一个按钮
Qt当中的按钮类为"QPushButton",通过帮助文档可以查看的到:
接下来在代码当中创建一个按钮并让其显示:
#include "widget.h"#include <QApplication>
#include <QPushButton> // 包含头文件
int main(int argc, char *argv[])
{QApplication a(argc, argv);Widget w;QPushButton *btn = new QPushButton;btn->show();// 显示w.show();return a.exec();
}

可以看到如果使用QPushButton类的show方法,是单独打开一个窗口并显示。很显然这并不是想要的效果。如果想要依附于已存在的窗口显示,只需要为按钮指定一个父亲即可:
#include "widget.h"#include <QApplication>
#include <QPushButton> // 包含头文件
int main(int argc, char *argv[])
{QApplication a(argc, argv);Widget w;QPushButton *btn = new QPushButton;btn->setParent(&w);// 依附于已经存在的窗口//btn->show();// 显示w.show();return a.exec();
}

此时按钮便依附于已存在的窗口了。如果想要设置一下按钮的大小、显示文字、改变位置,可以使用QPushButton类的某些成员函数:
#include "widget.h"#include <QApplication>
#include <QPushButton> // 包含头文件
int main(int argc, char *argv[])
{QApplication a(argc, argv);Widget w;QPushButton *btn = new QPushButton;btn->setParent(&w);// 依附于已经存在的窗口btn->setText("First Button");// 显示文字btn->resize(200,200);// 设置一下按钮的大小btn->move(200,100);// 移动到指定为止//btn->show();// 显示w.resize(600,400);// 固定一下窗口的大小w.show();return a.exec();
}

5.对象树简介
在没有接触Qt之前,上面的代码对于C++程序员来说是难受的,因为new了一个QPushButton对象却并没有做delete操作。
事实上在Qt当中有些情况下并不需要做delete操作,这就是对象树的作用。
对象树是Qt当中的一种自动回收内存的机制,它可以简化程序员的工作。就如上面的代码,程序员可以不用担心内存泄漏,即不用delete。
想要不delete堆上的内存空间,必须符合两个条件:
- new出来的对象必须是QObject的亲缘类对象
- 该对象必须指定一个QObject的亲缘类对象
QObject是最顶层的基类,基本上Qt当中的内置类都继承自于QObect。因此比如QWidget、QMainWindow、QDialog、QPushButton等等,它们的最顶层的基类就是QObject。
当new出来的对象指定了父亲之后,就相当于加入了对象树。这颗对象树的任意节点(对象)析构时,会自动释放其所有子孙节点(对象)。
此时可以做一个实验,即自定义一个类,取名为"MyTest"并继承自QPushButton类,指定它的父亲为Widget类:
#ifndef MYTEST_H
#define MYTEST_H#include <QWidget>
class MyTest : public QWidget
{Q_OBJECT
public:explicit MyTest(QWidget *parent = nullptr);~MyTest();
signals:};#endif // MYTEST_H
#include "mytest.h"
#include <QDebug>
MyTest::MyTest(QWidget *parent): QWidget{parent}
{}MyTest::~MyTest()
{qDebug() << "~MyPushButton()";
}
修改一下Wdiget类的析构函数:
#include "widget.h"
#include <QDebug>
Widget::Widget(QWidget *parent): QWidget(parent)
{
}Widget::~Widget()
{qDebug() << "~Widget()";
}
然后在主函数当中new出来MyPushButton类,指定其父亲为Wdiget,运行后关闭窗口观察打印结果:
#include "widget.h"#include <QApplication>
#include <QPushButton> // 包含头文件
#include "mytest.h"
int main(int argc, char *argv[])
{QApplication a(argc, argv);Widget w;MyTest *mbtn = new MyTest(&w);w.resize(600,400);w.show();return a.exec();
}

关闭窗口后的输出结果,可以看到mbtn所指向的堆空间并没有手动delete,但是由于对象树的存在,不会造成内存泄漏。
这里可以用一幅图来解释:

此时可能会有个问题,即刚才说的是对象树当中的某个对象释放时会先析构其所有子孙节点。那么为什么先打印父对象的析构,后打印子孙对象的析构呢?这个原因在于析构函数分成两部分执行,一是执行析构函数函数体内的代码,二是执行函数体之后的清理工作。函数体内的代码是释放当前对象的大部分资源,例如对象内部new出来的空间;函数体之后是清理对象本身。所以对象树可以理解成清理对象本身之前先析构其所有子孙对象。
6.Qt的坐标系
Qt的窗口是有坐标系的,上面在控制按钮的位置时涉及到坐标系。Qt窗口的坐标系如下图所示:

7. 信号和槽
其实信号和槽可以理解为"信号和信号处理"。熟悉Linux系统编程的话,理解信号和槽不会太难。
信号和槽涉及到两个角色,即信号的发送方和信号的接收方;涉及到两个动作,即信号的发送和信号的处理。
总体而言可以分成两个部分:

connect是一个函数,作用类似于Linux当中的signal()。它的作用我个人理解成"注册一个信号产生时需要做的动作"。
通过帮助文档可以查看该函数:

原型可以是这样的:
connect(信号的发送发,要发送的信号,信号的接收方,接收到信号后要做的动作)
做一个小任务,即创建一个按钮,点击该按钮后关闭窗口:
#include "widget.h"#include <QApplication>
#include <QPushButton> // 包含头文件#include "mytest.h"
int main(int argc, char *argv[])
{QApplication a(argc, argv);Widget w;QPushButton *btn = new QPushButton(&w);btn->setText("PushButton");btn->move(200,100);btn->resize(100,100);QObject::connect(btn,&QPushButton::clicked,&w,&Widget::close);// 注册信号w.show();return a.exec();
}

该按钮点击后,窗口就关闭了。
综上,信号和槽的优点非常明显:
- 使得编程多样化,可以实现更多丰富的功能,非常自由
- 低耦合,信号的发送方和接收方是低耦合的,这样设计有利于代码维护,出错的概率降低
7.1自定义信号和槽
如果自定义了一些类,并且想让这些类也能够使用信号和槽机制,Qt是支持的。
创建一个Student类和Teacher类,signals关键字下写自定义信号函数,该函数只声明不实现;public slots关键字(较高版本的Qt可以写到public下和全局下)下写自定义槽函数,该函数要声明和定义。
当前的任务是Teacher发出一个"下课"信号,学生做出"走出教室"动作。
#ifndef TEACHER_H
#define TEACHER_H#include <QObject>class Teacher : public QObject
{Q_OBJECT
public:explicit Teacher(QObject *parent = nullptr);signals:void ClassOver();// 下课,只声明不实现
};#endif // TEACHER_H
#ifndef STUDENT_H
#define STUDENT_H#include <QObject>class Student : public QObject
{Q_OBJECT
public:explicit Student(QObject *parent = nullptr);void GoOutOfClass();// 要声明和实现
signals:};#endif // STUDENT_H
#include "student.h"
#include <QDebug>
Student::Student(QObject *parent): QObject{parent}
{}
void Student::GoOutOfClass()
{qDebug() << "学生走出教室!";
}
此时在主函数当中完成任务:
int main(int argc, char *argv[])
{QApplication a(argc, argv);Student *stu = new Student;Teacher *tec = new Teacher;QObject::connect(tec,&Teacher::ClassOver,stu,&Student::GoOutOfClass);emit tec->ClassOver();// emit关键字,发送信号delete stu;delete tec;return a.exec();
}

此时要注意一个关键字,即"emit",它的作用在于触发信号。
其他方面,无论是信号函数还是槽函数,都是可以带参数的。
这里再做一个实验,即让信号函数之间发生重载,槽函数之间发生重载:
#ifndef TEACHER_H
#define TEACHER_H#include <QObject>
#include <QString>
class Teacher : public QObject
{Q_OBJECT
public:explicit Teacher(QObject *parent = nullptr);signals:void ClassOver();// 下课,只声明不实现void ClassOver(QString str);
};#endif // TEACHER_H
#ifndef STUDENT_H
#define STUDENT_H#include <QObject>
#include <QString>
class Student : public QObject
{Q_OBJECT
public:explicit Student(QObject *parent = nullptr);void GoOutOfClass();// 要声明和实现void GoOutOfClass(QString str);
signals:};#endif // STUDENT_H
#include "student.h"
#include <QDebug>
Student::Student(QObject *parent): QObject{parent}
{}
void Student::GoOutOfClass()
{qDebug() << "学生走出教室!";
}void Student::GoOutOfClass(QString str)
{qDebug() << "老师说:" << str;
}
那么在主函数当中修改一下代码:
int main(int argc, char *argv[])
{QApplication a(argc, argv);Student *stu = new Student;Teacher *tec = new Teacher;QObject::connect(tec,&Teacher::ClassOver,stu,&Student::GoOutOfClass);emit tec->ClassOver("下课啦!");// emit关键字,发送信号delete stu;delete tec;return a.exec();
}

此时出现了编译报错!原因在于connect函数当中的参数,信号函数和槽函数根本没有明确唯一指向,导致编译器报错。解决方法是利用函数指针来明确唯一指向:
int main(int argc, char *argv[])
{QApplication a(argc, argv);Student *stu = new Student;Teacher *tec = new Teacher;void (Teacher::*tecSignal)(QString) = &Teacher::ClassOver;void (Student::*stuSlot)(QString) = &Student::GoOutOfClass;QObject::connect(tec,tecSignal,stu,stuSlot);emit tec->ClassOver("下课啦!");// emit关键字,发送信号delete stu;delete tec;return a.exec();
}
需要注意,在声明和定义槽函数时,它们的返回值都为void。
如果需要取消注册的话,可以使用disconnect函数,这里就不做示例了。
7.2信号连接信号
信号不一定非要与槽函数connect在一起,信号和信号之间也可以连接。
现在实现一个需求,即创建一个按钮,按钮点击后老师下课,学生走出教室。此时代码可以修改为:
int main(int argc, char *argv[])
{QApplication a(argc, argv);Widget w;Student *stu = new Student(&w);Teacher *tec = new Teacher(&w);void (Teacher::*tecSignal)() = &Teacher::ClassOver;void (Student::*stuSlot)() = &Student::GoOutOfClass;QObject::connect(tec,tecSignal,stu,stuSlot);QPushButton *btn = new QPushButton(&w);btn->setText("PushButton");btn->resize(200,100);QObject::connect(btn,&QPushButton::clicked,tec,tecSignal);w.resize(600,400);w.show();return a.exec();
}

此时点击按钮会让老师产生下课信号,随后学生走出教室。
但是注意这里有个非常局限的东西,那就是信号连接信号时,作为槽函数的信号函数无法传递参数!这个时候就要配合全局函数或者lambda表达式,即在它们内部触发信号!
7.3拓展
下面的规则是总结和拓展:
- 一个信号可以连接多个槽函数
- 多个信号可以连接同一个槽函数
- 信号和槽函数的参数类型必须一一对应
- 信号函数的参数个数可以多于槽函数的参数个数
对于规则3和4的解释为,槽函数和可以选择接收和不接受信号传递过来的参数,如果选择接收了信号的参数,那么类型必须一致。以一幅图来理解:

7.4Qt4版本以前的connect
上面介绍的都是Qt4版本之后的connect函数用法,在Qt4版本之前,写法是下面这样的:
QObject::connect(tec,SIGNAL(&Teacher::ClassOver(QString)),stu,SLOT(&Student::GoOutOfClass(QString)));
显然这样的方式是非常直观的,因为可以免除函数指针的繁琐写法。但是这种写法必然存在恶劣的缺点否则较新的Qt版本也不会出现较新的写法。
根本原因在于SIGNAL和SLOT关键字会将括号内容字符串化,并且这些字符串之间还不会做任何的比较,所以导致编译器无法报错,导致后续维护项目困难。
相关文章:
Qt入门日记1
目录 1.Qt简介和案例 2.第一个Qt程序 3.学会查看帮助文档 4.创建一个按钮 5.对象树简介 6.Qt的坐标系 7. 信号和槽 7.1自定义信号和槽 7.2信号连接信号 7.3拓展 7.4Qt4版本以前的connect 1.Qt简介和案例 Qt是一个跨平台的C图形用户界面应用程序框架(就是一个库吧…...
SpringBoot_第七章(读写分离)
这里列举了三种读写分离实现方案,分别是如下三种 1:MybatisPlus(读写分离) 1.1:首先创建三个数据库1主2从 表名是user表 1.2:代码实例 1:导入pom <!--MybatisPlus的jar 3.0基于jdk8--><depend…...
linux下mysql-8.2.0集群部署(python版本要在2.7以上)
目录 一、三台主机准备工作 1、mysql官方下载地址:https://dev.mysql.com/downloads/ 2、修改/etc/hosts 3、关闭防火墙 二、三台主机安装mysql-8.2.0 1、解压 2、下载相应配置 3、初始化mysql,启动myslq,设置开机自启 4、查看初始密…...
40 深度学习(四):卷积神经网络|深度可分离卷积|colab和kaggle的基础使用
文章目录 卷积神经网络为什么要卷积卷积的具体流程池化tensorflow代码 深度可分离卷积原理介绍计算量对比代码参数计算例子 colab 和 kagglecolabkaggle如何在colab上使用kaggle的数据 卷积神经网络 卷积神经网络的基本结构 1: (卷积层(可选)池化层) * N全连接层 *…...
Spring Boot面向切面加注解
一.项目pom.xml文件引入切面依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId> </dependency>二.定义注解类 import java.lang.annotation.*;/*** desc 错误日志注解* au…...
uniapp小程序授权统一处理
1.使用 1.将工具代码引入到utils中 const authorize (scope, isOne false, isMust false) > {if (!scope || !authorizeObj[scope]) {return console.error(请传输需要获取权限的 scope,详见,https://uniapp.dcloud.net.cn/api/other/authorize.html#scope-…...
光学仿真|优化汽车内部照明体验
当我们谈论优化人类感知的内部照明时,我们实际上指的是两个重点领域:安全性和驾驶员体验。如果内部照明可以提供尽可能最佳的体验,驾驶员则能够更好地应对颇具挑战性或意外的驾驶状况,并且减轻疲劳感。除了功能优势外,…...
Spring XML使用CASE WHEN处理SELECT字段
在日常开发中,经常会碰到需要导出的情况。而一些枚举值或者状态一般是定义成整型,这个时候需要对数据进行转换,转换成对应的文本再导出。 在XML中用CASE WHEN来根据不同的查询结果做不同的处理。 比如 SELECT name AS 姓名, age AS 年龄 C…...
关于C#中使用多线程的讨论
关于C#中使用多线程的讨论 C# 中 Thread 调用的函数有返回值 没有输入应该如何解决 如果你想在一个新的线程中调用一个带返回值但没有输入参数的函数,可以使用 Thread 类的委托 ThreadStart 来创建一个新的线程,并在其中调用该函数。然后,你可以使用 Thread 类的 Join 方法…...
工程机械数字孪生可视化平台建设,推动大型装备智能化数字化转型升级
随着信息技术的迅猛发展,数字孪生技术作为一项新兴技术应运而生。数字孪生是指通过数学模型和仿真技术,将实际物理对象与其数字化虚拟模型相互关联,实现对物理对象的全生命周期管理和智能优化。在工程机械领域,巨蟹数科从数字孪生…...
Linux 网络流量监控利器 iftop命令详解及实战
简介 iftop 是什么 在 Linux 系统下即时监控服务器的网络带宽使用情况,有很多工具,比如 iptraf、nethogs 等等,但是推荐使用小巧但功能很强大的 iftop 工具。 iftop 是 Linux 系统一个免费的网卡实时流量监控工具,类似于 top 命令…...
protected by SourceGuardian and requires a SourceGuardian loader ‘ixed.8解决方案
php相关问题 安装程序提示以下内容 遇到某些php程序的安装提示: PHP script ‘/www/wwwroot/zhengban.youyacao.com/install/index.php’ is protected by SourceGuardian and requires a SourceGuardian loader ‘ixed.8.1.lin’ to be installed. 1) Click her…...
KWin、libdrm、DRM从上到下全过程 —— drmModeAddFBxxx(14)
接前一篇文章:KWin、libdrm、DRM从上到下全过程 —— drmModeAddFBxxx(13) 上一回讲完了drivers/gpu/drm/drm_framebuffer.c中的framebuffer_check函数中的第一个for循环,本回继续讲解framebuffer_check()接下来的代码。为了便于理解,再次贴出其源码,如下所示: static …...
2023-macOS下安装anaconda,终端自动会出现(base)字样,如何取消
2023-macOS下安装anaconda,终端自动会出现(base)字样,如何取消 安装后,我们再打开终端,就会自动出现了(base) 就会出现这样子的,让人头大, 所以我们要解决它 具体原因是 安装了anac…...
Nginx搭载负载均衡及前端项目部署
目录 编辑 一.Nginx安装 1.安装所需依赖 2.下载并解压Nginx安装包 3.安装nginx 4.启动Nginx服务 二.Tomcat负载均衡 1.准备环境 1.1 准备两个Tomcat 1.2 修改端口号 1.3 配置Nginx服务器集群 2.效果展示 编辑三.前端项目打包 编辑四.前端项目部署 1.上传项目…...
深度学习——炼丹
学习率调整策略 自定义学习率调整策略 简单版 net MyNet()optimoptim.Adam(net.parameters(),lr0.05) for param_group in optim.param_groups: param_group["lr"] param_group["lr"]*0.5print(param_group["lr"]) #0.25复杂版&#…...
Matlab中的app设计
1.窗口焦点问题: 窗口焦点问题:确保你的应用程序窗口正常处于焦点状态。有时,其他窗口的弹出或焦点切换可能导致应用程序最小化。点击应用程序窗口以确保它处于焦点状态。 窗口管理:确保你的 MATLAB 或操作系统没有未处理的错误或…...
曾经遇到过的无法解释的问题
因为不能直接展示生产数据与生产数据结构,所以写一个简单的例子 class Stu{ private String name; private int age; getter setter constructor 略 } List<Stu> list new ArrayList(); list.add(new Stu("s1",16)); list.add(new Stu("…...
基于uniapp与uview做一个按拼音首字母排序的通讯录页面
效果图: 第一步导入pinyin库并应用,用于区分汉字的拼音首字母 npm i pinyin import pinyin from "pinyin" 完整算法: function getListByPinyinFirstLetter(data) {const newList {};for (const item of data) {let firstLett…...
网络工程师-入门基础课:华为HCIA认证课程介绍
【微/信/公/众/号:厦门微思网络】 华为HCIA试听课程:超级实用,华为VRP系统文件详解 华为HCIA试听课程:不会传输层协议,HCIA都考不过 华为HCIA试听课程:网络工程师的基本功:网络地址转换NAT 一…...
微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】
微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来,Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...
8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...
转转集团旗下首家二手多品类循环仓店“超级转转”开业
6月9日,国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解,“超级…...
Python爬虫(二):爬虫完整流程
爬虫完整流程详解(7大核心步骤实战技巧) 一、爬虫完整工作流程 以下是爬虫开发的完整流程,我将结合具体技术点和实战经验展开说明: 1. 目标分析与前期准备 网站技术分析: 使用浏览器开发者工具(F12&…...
ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...
Spring AI 入门:Java 开发者的生成式 AI 实践之路
一、Spring AI 简介 在人工智能技术快速迭代的今天,Spring AI 作为 Spring 生态系统的新生力量,正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务(如 OpenAI、Anthropic)的无缝对接&…...
Swagger和OpenApi的前世今生
Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章,二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑: 🔄 一、起源与初创期:Swagger的诞生(2010-2014) 核心…...
Java毕业设计:WML信息查询与后端信息发布系统开发
JAVAWML信息查询与后端信息发布系统实现 一、系统概述 本系统基于Java和WML(无线标记语言)技术开发,实现了移动设备上的信息查询与后端信息发布功能。系统采用B/S架构,服务器端使用Java Servlet处理请求,数据库采用MySQL存储信息࿰…...
Python+ZeroMQ实战:智能车辆状态监控与模拟模式自动切换
目录 关键点 技术实现1 技术实现2 摘要: 本文将介绍如何利用Python和ZeroMQ消息队列构建一个智能车辆状态监控系统。系统能够根据时间策略自动切换驾驶模式(自动驾驶、人工驾驶、远程驾驶、主动安全),并通过实时消息推送更新车…...
Qt 事件处理中 return 的深入解析
Qt 事件处理中 return 的深入解析 在 Qt 事件处理中,return 语句的使用是另一个关键概念,它与 event->accept()/event->ignore() 密切相关但作用不同。让我们详细分析一下它们之间的关系和工作原理。 核心区别:不同层级的事件处理 方…...
