Qt 学习笔记
- 构建系统
- 最小系统
- 信号和槽机制
- Qt 对象系统
- Qt 窗口体系
- 消息/事件机制
- 绘图和绘图设备
- 文件系统
- Socket 通信
- 多线程
- 数据库操作
- Qt 程序打包
概述
仅记录基本概念
细节可以在文档中搜索文中出现的各种类慢慢看
参考资料:
- 传智播客的 Qt5 教程
- Qt 文档
同时 Qt 可以结合 VS 和 vcpkg 系统一块儿使用,简直完美
1. 构建系统
Qt 的以qmake
为构建系统,其配置文件为xxx.pro
,相当于CMake
的CmakeLists.txt
其基本语法为
1 | # 单行注释 |
2. 最小程序
1 |
|
3. 信号和槽机制
3.1 基本概念
信号
信号指某个控件在接收到某个事件后自行发出的一个通知信号
这个信号不指向任何目标,仅仅只是一个信号
对这个信号感兴趣的控件可以通过连接函数把这个信号和其他处理函数进行连接槽
通过连接函数与信号连接的特殊函数就是槽函数
在信号触发的时候,槽函数会被通过预设的方式调用
3.2 使用方法
槽函数的参数和顺序必须和信号的参数保持一致
允许槽函数的参数少于信号的参数
使用函数指针的方式连接
1
2
3
4
5// sender:发出信号的对象
// signal:发送对象发出的信号
// receiver:接收信号的对象
// slot:接收对象在接收到信号之后所需要调用的函数
connect(sender, signal, receiver, slot);使用字符串的方式连接
1
2
3// 其中SIGNAL和SLOT是两个宏,会把函数转换成字符串
// 同时这种方式不会产生编译错误,只会产生运行期错误...
connect(button, SIGNAL(clicked()), &a, SLOT(quit()));3.3 自定义信号槽
首先为了自定义信号槽,必须继承QObject
类
然后在类的第一行使用Q_OBJECT
宏(这个宏提供信号槽机制,国际化机制,以及 Qt 特有的反射能力)
信号
Qt 增加了signal
块,其中的函数即是信号函数
信号函数返回值必须为void
,同时也不需要提供具体实现
emit 是一个 Qt 扩展的关键字,后面接调用信号函数的语句即可触发信号
槽函数
Qt5 中任何函数(成员函数,static 函数,全局函数和 Lambda 表达式)都可以作为槽函数
- 一个信号可以连接多个槽,但是槽被调用的顺序不确定
- 多个信号可以连接一个槽,任何一个信号发出都会调用槽函数
- 信号可以连接其他信号,即触发其他信号
- 槽可以被取消连接,当一个对象 delete 后,Qt 自动取消连接到这个对象上的槽
- 如果信号发射者和接收者不在同一个线程中,则在信号发出后,再次轮到接收者线程执行时才执行槽函数
4. Qt 对象体系
Qt 中的对象
Qt 中的根对象为QObject
而所有窗口及窗口控件的根对象为QWiget
(也继承自 QObject)
Qt 中的内存管理
在 Qt 中,不同的对象以树的形式组织起来
一个对象在创建时可以指定一个父对象指针(也可以后面再设置)
对象会自动添加到父对象的children
列表
父对象析构的时候会自动析构其所有子对象
当对象在堆上创建时,Qt 中的对象树可以很可靠地把对象析构
当对象在栈上创建时,需要遵守一些规定,以保证对象不会被作用域结束和对象树析构两次
1 | { |
5. Qt 窗口体系
首先 Qt 中的坐标以左上角为原点,横向为 x 坐标,纵向为 y 坐标,向右向下正向
同时窗口坐标以控件父组件坐标为基准
5.1 QMainWindows
QMainWindow 类是 Qt 中的主窗口类
提供一个菜单栏(menu bar), 多个工具栏(tool bars)、多个锚接部件(dock widgets)、一个状态栏(status bar) 及一个中心部件(central widget)
5.2 资源文件
Qt 内部资源文件可以以二进制的方式编译到可执行文件中
可以在 Qt 内部给文件起别名,通过别名来使用文件
5.3 控件
Qt 中的控件派生自QWidget
内置控件可以参考 Qt Designer 中各种控件的使用方法(Qt 本身文档有介绍)
或者参考 Zeal 中的 Qt 文档
5.4 对话框
对话框常用来进行一些交互任务(如浏览资源管理器并打开文件),QDialog
为 Qt 中的对话框基类
对话框对父对象有不同的使用方法:
指定父对象时,对话框为父组件的子对话框,在任务栏共享父对象的位置
不指定父对象,为顶层窗口,在任务栏占用你单独的位置
同时对话框有三种类型:
应用程序级别的模态对话框:
完成对话前阻塞同一程序其他窗口的输入(必须优先完成此对话框)
使用QDialog::exec()
执行窗口级别的模态对话框:
阻塞与窗口关联的输入操作,允许与其他窗口互动
使用QDialog::open()
执行非模态对话框:
可以切换到其他窗口进行操作
使用QDialog::show()
执行
Qt 内置了一系列通用的对话框
对话框 | 作用 |
---|---|
QColorDialog | 选择颜色 |
QFileDialog | 选择文件/目录 |
QFontDialog | 选择字体 |
QInputDialog | 获取输入 |
QMessageBox | 显示消息 |
QPageSetupDialog | 打印纸张操作 |
QPrintDialog | 打印机操作 |
QPrintPreviewDialog | 打印预览 |
QProgressDialog | 显示操作 |
使用方法
1 | // 应用程序模态窗口 |
5.5 布局管理器
Qt 提供两种布局方式:绝对定位布局,布局管理器布局
布局管理器 | 作用 |
---|---|
QHBoxLayout | 从左到右布局 |
QVBoxLayout | 从上到下布局 |
QGridLayout | 网格布局 |
6. 消息/事件机制
Qt 中的组件基类QWidget
有一系列虚函数用于作为事件处理函数
但事件传递给组件后,会由event()
函数处理,并分发给不同的事件处理函数
event()函数负责进行事件的分发,如果传入的事件已被识别并且处理,则需要返回 true,否则返回 false
如果返回值是 true,那么 Qt 会认为这个事件已经处理完毕,不会再将这个事件发送给其它对象,而是会继续处理事件队列中的下一事件
在 event()函数中,调用事件对象的 accept()和 ignore()函数是没有作用的,不会影响到事件的传播
- keyPressEvent()
- keyReleaseEvent()
- mouseDoubleClickEvent()
- mouseMoveEvent()
- mousePressEvent()
- mouseReleaseEvent()
如果想要屏蔽特定某个事件,可以重写event()
函数(这样有点复杂),也可以使用事件过滤器
事件过滤器会在event()
函数处理之前过滤所有事件,通过返回true
指定事件已处理,不需要继续传播
1 | // 函数原型 |
7. 绘图和绘图设备
Qt 绘图的过程:
使用QPainter
在QPaintDevice
上进行绘制
即QPainter
发出指令,经过QPaintEngine
进行传递,由QPaintDevice
进行转换和显示
如果不编写自定义的QPaintDevice
的话,无需关心QPaintEngine
Qt 内有 4 个常用的绘图设备
绘图设备 | 用途 |
---|---|
QPixmap | 针对屏幕显示进行了优化,与操作系统相关,在不同操作系统上效果不同 |
QBitmap | QPixmap 的子类,特化为只有黑白两色,性能较好,可用于特殊场景使用 |
QImage | 专用于图片 IO 等操作,可对图片进行像素级访问 |
QPicture | 可以存储和重现各种 QPainter 的操作 |
8. 文件系统
Qt 提供了一个跨平台的文件操作系统
QIODevice:所有 I/O 设备类的父类,提供了字节块读写的通用操作以及基 本接口; QFileDevice:Qt5 新增加的类,提供了有关文件操作的通用实现。 QFlie:访问本地文件或者嵌入资源; QTemporaryFile:创建和访问本地文件系统的临时文件; QBuffer:读写 QbyteArray, 内存文件; QProcess:运行外部程序,处理进程间通讯; QAbstractSocket:所有套接字类的父类; QTcpSocket:TCP 协议网络数据传输; QUdpSocket:传输 UDP 报文; QSslSocket:使用 SSL/TLS 传输数据;
类 | 作用 | 属性 |
---|---|---|
QIODevice | 提供了字节块读写的通用操作及基本接口 | |
QFileDevice | 文件操作的通用操作实现 | |
QFile | 访问本地文件操作 | 随机访问 |
QTemporaryFile | 本地临时文件操作 | 随机访问 |
QBuffer | 读写 QbyteArray 内存文件 | 随机访问 |
QProcess | 运行外部程序,处理进程间通信 | 顺序访问 |
QAbstractSocket | 套接字类基类 | |
QTcpSocket | TCP 协议数据传输 | 顺序访问 |
QUdpSocket | UDP 协议数据传输 | 顺序访问 |
QSslSocket | SSL/TLS 传输数据 |
9. Socket 通信
Qt 中的 Socket 类都是非阻塞的
QTcpSocket
和QUdpSocket
分别提供 TCP 和 UDP 通信
其中 UDP 可以提供局域网广播
详细内容可以参考这两个类的文档
10. 多线程
Qt 提供QThread
类来实现多线程,继承并实现run()
虚函数即可
外部通过start()
来进行启动(类似 Java 中的多线程)
把我们的线程类中的finished()
信号与deleteLater()
函数连接,即可让线程完成时自动清理线程实例
还有一种多线程操作方式
1 | class Worker : public QObject { Q |
非主线程不可操纵 UI 对象,要被移动到子线程中处理的模块类在创建时不可以指定父对象
同时如果该模块类对象被析构,则信号槽连接自动断开
11. 数据库操作
Qt 提供一系列类进行数据库操作(仅限关系型数据库)
详见 Qt 自带文档
12. Qt 程序打包
单独编译出来的 Qt 可执行文件还需要一系列的动态链接库等才能工作
Qt 提供了windeployqt
工具,可以根据编译得到的可执行文件自动找到所有依赖的包并拷贝到当前目录
详细内容可以参考 Qt 自带文档