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 一…...

玻色量子成功研制光量子计算专用光纤恒温控制设备——“量晷”
近日,北京玻色量子科技有限公司(以下简称“玻色量子”)成功研制出一款高精度量子计算专用光纤恒温控制设备——“量晷”,该设备能将光纤的温度变化稳定在千分之一摄氏度量级,即能够做到0.001C的温度稳定维持…...

力扣:147. 对链表进行插入排序(Python3)
题目: 给定单个链表的头 head ,使用 插入排序 对链表进行排序,并返回 排序后链表的头 。 插入排序 算法的步骤: 插入排序是迭代的,每次只移动一个元素,直到所有元素可以形成一个有序的输出列表。每次迭代中,…...

OpenCV4(C++)——形态学(腐蚀、膨胀)
文章目录 一、腐蚀(erode)二、膨胀(dilate)三、形态学操作四、总结 一、腐蚀(erode) OpenCV 4提供了用于图像腐蚀的erode()函数。 void cv::erode(src, dst, kernel, anchor, iterations, borderType, bo…...

C++设计模式_24_Visitor 访问器
Visitor 访问器也是属于“行为变化”模式。 文章目录 1. 动机( Motivation)2. 代码演示Visitor 访问器3. 模式定义4. 结构(Structure)5. 要点总结6. 其他参考1. 动机( Motivation) 在软件构建过程中,由于需求的改变,某些类层次结构中常常需要增加新的行为(方法),如果直接在基…...

el-tabel表格加个多选框
<template><div><el-checkbox v-model"checked" :disabled"checkedDis" change"onAllSelectChange">多选框</el-checkbox>点击多选框,禁用列表复选框<el-table ref"multipleTable" :data"…...

Go语言集成开发环境(IDE):GoLand 2023中文
GoLand 2023是一款由JetBrains开发的现代化、功能丰富的Go语言集成开发环境(IDE)。它提供了智能代码提示和自动完成、强大的内置调试器以及代码重构工具,帮助开发者提高编码效率并确保代码质量。GoLand 2023还支持多种版本控制系统࿰…...

opencv c++ canny 实现 以及与halcon canny的对比
Opencv和C实现canny边缘检测_opencv边缘增强-CSDN博客 一、canny实现步骤 1、图像必须是单通道的,也就是说必须是灰度图像 2、图像进行高斯滤波,去掉噪点 3、sobel 算子过程的实现,计算x y方向 、梯度(用不到,但是…...

阿里云无影升级2.0 云电脑解决方案时代到来
10月31日,杭州云栖大会上,阿里云宣布无影全新升级2.0:从云电脑到云上解决方案,帮助中小企业更便捷地构建云上办公,并开放无影产品及解决方案能力,为生态合作伙伴提供企业云平台,帮助其打造定制化…...

【案例展示】多物理场仿真软件介绍
本期案例来自上海交通大学微波与射频研究中心团队,团队针对集成电路技术发展过程中面临的挑战,通过FastCAE平台对自研多物理场仿真求解器进行功能拓展与集成,使用户可基于不同的电、热、应力场景和仿真需求,灵活设定参数进行快速准…...

k8s的RBAC中,clusterrole, rolebinding 是什么关系谁先谁后
在Kubernetes中,Role-Based Access Control(RBAC)用于控制集群中不同用户、服务账号或组的访问权限。ClusterRole 和 RoleBinding 是两个关键的 RBAC 组件,它们之间的关系是 ClusterRole 定义了一组权限规则,而 RoleBi…...