目录
- 1.Qt命名规范
- 2.Qt窗口坐标体系
- 3.Qt文件
- 4.容器:QT VS STL
- 5.基类
- 6.认识对象模型(对象树)
- 1.是什么?
- 2.解决内存问题
- 1.如何解决?
- 2.如何做?
- 3.举例说明
- 4.总结
- 7.Qt父子对象
- 8.Qt中乱码的处理
- 9.注意 && 提要
1.Qt命名规范
- 类名:⾸字⺟⼤写,单词和单词之间⾸字⺟⼤写
- 函数名及变量名:⾸字⺟⼩写,单词和单词之间⾸字⺟⼤写
2.Qt窗口坐标体系
-
坐标体系:以左上⻆为原点 ( 0 , 0 ) (0,0) (0,0),X向右增加,Y向下增加
-
对于嵌套窗⼝/控件,其坐标是相对于⽗窗⼝/控件来说的
3.Qt文件
- Qt系统提供的标准类名声明头⽂件没有
.h
后缀 - Qt⼀个类对应⼀个头⽂件,类名就是头⽂件名
QApplication
为应⽤程序类,一个程序有且仅有一个QApplication
管理图形⽤⼾界⾯应⽤程序的控制流和主要设置QApplication
是Qt的整个后台管理的命脉,它包含:- 主事件循环,在其中来⾃窗⼝系统和其它资源的所有事件处理和调度
- 它也处理应⽤程序的初始化和结束,并且提供对话管理
- 对于任何⼀个使⽤Qt的图形⽤⼾界⾯应⽤程序,都正好存在⼀个
QApplication
对象,⽽不论这个应⽤程序在同⼀时间内是不是有0、1、2或更多个窗⼝
4.容器:QT VS STL
- Qt在早些年为了开发顺畅,自己造轮子搞了一系列的基础类,来支持Qt开发
- 例如:
QString, QVector, QList, QMap
- 例如:
- 但是现如今的STL和QT的基础类都打磨的很好了,所以在进行Qt开发的时候,如果需要用到上述容器类,可以使用STL容器,也可以使用Qt自己搞得这一套容器
- 但是Qt原生的API中,涉及到的接口,用的都是Qt自己的这一套容器
- 后续代码中,还经常会见到
QString
这样的一些东西,而很少见到std::string
- 注意:
QString
和std::string
之间能很方便的相互转换
- 注意:
- 实际上,
QString
用起来要比std::string
香一些- 因为
QString
内部已经对于字符编码做了处理,而std::string
啥都没干
- 因为
5.基类
-
QObject
是Qt内置的⽗类.Qt中提供的很多类都是直接或者间接继承⾃QObject
-
对于基类的选择,⽬前有三种基类
基类 说明 QMainWindow
主窗⼝类,⼀般⽤于较为复杂的应⽤程序,除了中央客⼾区界⾯,还包括菜单栏、⼯具栏、 状态栏以及多个可停靠的⼯具对话框等 QWidget
最简单、最基本的窗体程序,⾥⾯可以放置多个控件实现程序功能 QDialog
基于对话框的程序,对话框⼀般⽤于弹窗,也可以⽤于主界⾯显⽰。对话框是从QWidget继 承⽽来的,并丰富了⼀些功能,如模态显⽰和返回值等 -
基类之间的关系
6.认识对象模型(对象树)
1.是什么?
- 在Qt中创建很多对象的时候会提供⼀个
Parent
对象指针 QObject
是以对象树的形式组织起来的- 当创建⼀个
QObject
对象时,会看到QObject
的构造函数接收⼀个QObject
指针作为参数,这个参数就是parent
,也就是⽗对象指针- 这相当于,在创建
QObject
对象时,可以提供⼀个其⽗对象,创建的这个QObject
对象会⾃动添加到其⽗对象的children()
列表 - 当⽗对象析构的时候,这个列表中的所有对象也会被析构(这⾥的⽗对象并不是继承意义上的⽗类)
- 这相当于,在创建
- 当创建⼀个
- 使用对象树,把内容组织起来,最主要的目的是为了能够在合适的时机(窗口关闭/销毁),把这些对象统一进行释放
- Qt对象图
2.解决内存问题
1.如何解决?
- Qt引⼊对象树的概念,在⼀定程度上解决了内存问题
- 当⼀个
QObject
对象在堆上创建的时候,Qt会同时为其创建⼀个对象树- 不过,对象树中对象的顺序是没有定义的,这意味着,销毁这些对象的顺序也是未定义的
- 任何对象树中的
QObject
对象delete
的时候- 如果这个对象有
parent
,则⾃动将其从parent
的children()
列表中删除 - 如果有孩⼦,则⾃动
delete
每⼀个孩⼦
- 如果这个对象有
- Qt保证没有
QObject
会被delete
两次,这是由析构顺序决定的
- 当⼀个
2.如何做?
- 创建对象的时候,在构造函数中,指定父对象,此时该对象才会被挂到对象树上
- 如果该对象没有被挂到对象树上,就必须要记得手动释放
- 尽量避免在栈上创建Qt对象,可能存在提前释放的问题
- 如果
QObject
在栈上创建,Qt保持同样的⾏为,正常情况下,这也不会发⽣什么问题 - 如下代码,没问题
- 作为⽗组件的
window
和作为⼦组件的quit
都是QObject
的⼦类 quit
的析构函数不会被调⽤两次,因为标准C++要求,局部对象的析构顺序应该按照其创建顺序的相反过程- 因此,这段代码在超出作⽤域时,会先调⽤
quit
的析构函数,将其从⽗对象window
的⼦对象列表中删除,然后才会再调⽤ window的析构函数
QWidget window; QPushButton quit("Quit", &window);
- 作为⽗组件的
- 如下代码,有问题
- 情况有所不同,析构顺序出现了问题,作为⽗对象的
window
会⾸先被析构,因为它是最后⼀个创建的对象 - 在析构过程中,它会调⽤⼦对象列表中每⼀个对象的析构函数
- 也就是说,
quit
此时就被析构了
- 也就是说,
- 然后,代码继续执⾏,在
window
析构之后,quit
也会被析构- 因为
quit
也是⼀个局部变量,在超出作⽤域的时候当然也需要析构 - 但是,此时已经是第⼆次调⽤
quit
的析构函数了- C++不允许调⽤两次析构函数,因此,程序崩溃了
- 因为
QPushButton quit("Quit"); QWidget window; quit.setParent(&window);
- 情况有所不同,析构顺序出现了问题,作为⽗对象的
- 如果
- 在Qt中,尽量在构造的时候就指定parent对象,并且⼤胆在堆上创建
- 通过
new
的方式创建对象,是为了把这个对象的生命周期交给Qt的对象树来统一管理
- 通过
3.举例说明
QWidget
是能够在屏幕上显⽰的⼀切组件的⽗类QWidget
继承⾃QObject
,因此也继承了这种对象树关系,⼀个孩⼦⾃动地成为⽗组件的⼀个⼦组件- 因此,它会显⽰在⽗组件的坐标系统中,被⽗组件的边界剪裁
- 例如:当⽤⼾关闭⼀个对话框的时候,应⽤程序将其删除,属于这个对话框的按钮、图标等应该⼀起被删除,因为这些都是对话框的⼦组件
- 当然,也可以⾃⼰删除⼦对象,它们会⾃动从其⽗对象列表中删除
- 例如:当删除了 ⼀个⼯具栏时,其所在的主窗⼝会⾃动将该⼯具栏从其⼦对象列表中删除,并且⾃动调整屏幕显⽰
4.总结
- Qt的对象树机制虽然在⼀定程度上解决了内存问题,但是也引⼊了⼀些值得注意的事情
- 这些细节在开发过程中很可能时不时跳出来烦扰⼀下,所以,最好从开始就养成良好习惯
7.Qt父子对象
- 一般通过代码来构造界面的时候,通常会把构造界面的代码放到
Widget/MainWindow
的构造函数中 - 一般会给当前这个对象设置一个父对象,即将该对象加入到父对象的对象树中
- Qt中使⽤⽗⼦关系决定该控件"在哪⾥"
8.Qt中乱码的处理
- Qt提供了一个
qDebug()
工具,可以帮助用户自动处理编码方式- 正常当作
std::cout
一样使用即可 - 用法:
qDebug() << "SnowK";
- 另一个好处:打印的调试日志,是可以通过编译开关统一进行关闭的
- 正常当作
9.注意 && 提要
- Qt中规定,任何对于GUI上内容的操作,必须在主线程中完成
- 当尝试在⾃⼰的线程中对界⾯元素进⾏修改时,Qt程序往往会直接崩溃
- 这样的约定主要是因为GUI中的状态往往是牵⼀发动全⾝的,修改⼀个地⽅,就需要同步的对其他内容进⾏调整
- 例如:调整了某个元素的尺⼨,就可能影响到内部的⽂字位置,或者其他元素的位置,这⾥⼀连串的修改,都是需要按照⼀定的顺序来完成的
- 由于多线程执⾏的顺序⽆法保障,因此Qt从根本上禁⽌了其他线程修改GUI状态,避免后续的⼀系列问题
QByteArray
⽤于表⽰⼀个字节数组,可以很⽅便的和QString
进⾏相互转换- 使⽤
QString
的构造函数即可把QByteArray
转成QString
- 使⽤
QString
的toUtf8()
即可把QString
转成QByteArray
- 使⽤