昨天快速入门了一下在VS上开发QT,主要参考的Qt入门教程:1天玩转Qt。本文也算是这个简单教程的摘要。
1. 背景
- Qt 不仅仅是一个GUI库,它除了可以创建漂亮的界面,还有很多其他组件,例如,你不再需要研究STL,不再需要C++的头文件,不再需要去找解析XML、连接数据库、Socket 的各种第三方库,这些 Qt 都已经内置了。
- 独立安装:Qt 程序最终会编译为本地代码,不需要其他库的支撑,而 Java 要安装虚拟机,C#要安装 .NET Framework。
- 但是,由于Android本身支持Java,iOS本身支持Objective-C和Swift,所以Qt 在移动端的贡献寥寥无几。总起来说,Qt 主要用于桌面程序开发和嵌入式开发。
2. 构成
安装后的 Qt 主要包含以下几部分:
- Qt Library:也就是 Qt 的库,这是 Qt 的核心。
- Qt Creator:基于 Qt 开发的一款轻量级 IDE。虽然也可以用 VS 等其他IDE 来开发Qt程序,但我们依然推荐使用 Qt Creator,它是官方专门为 Qt 开发打造的一款 IDE。
- Qt Designer:Qt 程序的 UI 设计器。借助 Qt Designer,即使不编写代码,拖拖拽拽也可以开发简单的 GUI 程序,并且可以及时预览程序界面(无需编译)。
- Qt Assistant:Qt 帮助工具,包含了Qt教程、示例、类参考手册、模块介绍等,是 Qt 的官方资料,类似 MSDN。学习 Qt,一定要会使用 Assistant 查找资料。
- Qt Linguist:Qt 语言家,是 Qt 的国际化工具,借助它可以很方便的将界面上的文本翻译成其他语言,从而让程序支持多种语言,面向全球用户。
3. 纯C++构建一个界面
创建工程的方法如链接所示:https://blog.csdn.net/qq_39945321/article/details/105664469
在主类构造函数中添加上面代码即可有下面效果:
这是传统的GUI程序开发方式,只使用C++代码,C++既负责设计界面,也负责处理业务逻辑。
在网页中,我们能看到的各种文字、颜色、图片、布局、按钮、菜单、列表等界面元素都是使用HTML+CSS(专门用来设计界面的声明式语言)创建的,而后台逻辑才需要JavaScript、Python、PHP、Java等编程语言来处理。
现代GUI程序的设计,或许也借鉴了Web开发的思想,可以使用 XML 来设计界面,使用C++来处理后台逻辑,在 Qt 中可以轻而易举的做到前端和后台分离,从而让代码逻辑更加清晰,开发更加高效。这就是QT Designer的作用。
4. 使用QT Designer构建一个界面
新建工程的时候module选中XML组件,这样新建的工程中就会出现.ui文件。也可以按照“VS+Qt modules项目后期勾选Network、XML等”中的方案新加。双击.ui文件就可以打开Qt Designer.
然后就是开始拖动各种组件到界面中。可以在 窗体->预览 中查看效果;可以在 窗体->查看代码 中看该UI界面的代码。但是并没有看到编辑模式和设计模式的切换,也就没有看到具体的xml文件的内容。
XML是一种标记语言(也称声明式语言),由一个一个地节点组成,每个节点还可以包含多个属性,HTML 就是 XML 的一种具体化。
5. 解析一个简单的Qt程序
- 第1~3行代码引入对应的头文件。在Qt中,头文件设计的非常规范,大部分情况下一个类对应一个头文件,类名和头文件名相同,以字母“Q”开头。一个控件对应一个类,要想使用控件,必须包含对应的头文件。
- Qt 程序的入口函数和 C++ Console 程序一样,都是 main() 函数;参数也是一样的。
- QApplication 类主要用来管理程序的生命周期(包括初始化设置和销毁工作),开启事件循环。对于任何一个 Qt GUI 程序,必须有且仅有一个 QApplication 对象,但可以存在零个或多个窗口。
- QMainWindow 表示主窗口,它是一个顶级窗口。在 Qt 中,一个控件可以有父辈,这个父辈可以是一个窗口,也可以是一个容器控件。父辈有管理控件的作用,当父辈被销毁时,子控件也都被销毁,释放内存。
- setGeometry(100, 50, 160, 30) 设置 Label 控件的几何尺寸,100 为左边距,50 为上边距,160 为控件宽度,30为控件高度,单位都是像素(px)。边距是相对父辈 w 来说的。
- 控件被创建后只是分配了内存,并不可见,还需要调用 show() 函数显示出来。w 是 label 的父辈,父辈调用 show() 函数也会显示所有子控件,所以 label 就不需要再调用 show() 了。
- exec() 使程序进入消息循环,等待可能发生的事件,例如鼠标点击、键盘输入等。这里 main() 把控制权转交给 Qt,由 Qt 完成事件处理工作,程序退出时 exec() 的值就会返回。在 exec() 中,Qt 接受并处理用户和系统的事件,把它们传递给适当的窗口控件。
实际使用中,工程会自动新建主窗口类(新建工程的时候指定命名),类的构造函数中用来初始化各种控件,绑定槽函数等,如下所示:
SetupUi函数定义在ui_xxx.h中,就是在QT Designer中设计的UI所生成的文件。
6. QT中的类
首先放出一个类继承图:
https://blog.csdn.net/Faith_yu/article/details/53025582
可以看到所有的控件/窗口的基类都是Qwidget.
在 Qt 中,我们将窗口和控件统称为部件(Widget)。窗口是指程序的整体界面,可以包含标题栏、菜单栏、工具栏、关闭按钮、最小化按钮、最大化按钮等;控件是指按钮、复选框、文本框、表格、进度条等这些组成程序的基本元素。
需要注意的是,窗口和控件都继承自 QWidget,如果不为控件指定父对象,它就会被作为窗口处理,这时 setWindowTitle() 和 setWindowIcon() 函数就会生效。
7. 信号与槽
GUI 程序除了要绘制控件,还要响应系统和用户事件,例如重绘、绘制完成、点击鼠标、敲击键盘等。Qt 独创了信号和槽机制。
信号是QT在捕获事件后发出的,可以是鼠标点击,鼠标移动等,需要注意的是,信号不是事件。当用户点击“取消”按钮时,Qt 会捕获该点击事件,进行预处理,然后发射 clicked() 信号;
槽则可以认为是回调函数,就是接受到信号以后执行的动作。
信号与槽的连接使用connect函数。connect() 是 QObject 类的静态成员函数,它有多个原型:
Q_OBJECT 是 Qt 中的宏,借助它才能实现信号和槽机制,继承 Qt 类时都要在类声明的开头添加 Q_OBJECT。
观察上面的原型,除了最后一个有3个参数,其他都有4个参数,其中:
- sender 为信号发送者,receiver 为信号接收者,它们都是对象指针。
- 第1个原型中,signal 为信号,method 为槽函数,它们都是字符串,必须借助 SIGNAL() 和 SLOT() 将函数形式转换为字符串形式。SIGNAL() 和 SLOT() 是宏,而非函数。
- 第2个原型中,PointerToMemberFunction 为指向成员函数的指针。
这是 Qt 5 新增的原型,可以在编译期间进行检查,如果信号和槽不存在或者不匹配,则会报错。而第1种原型是从 Qt 诞生以来一直支持的,不能在编译期进行检测,如果信号和槽有误,只会在程序运行期间给出警告并返回 false,不容易发现问题,这是它的一个缺陷。所以在 Qt 5 中鼓励使用第2种原型。
关于信号和槽的实例,可以参考http://c.biancheng.net/cpp/html/3066.html
自定义槽的方式可以参见“QT入门问题点:QT Designer 没有“转到槽”项”。
8. 使用中遇到的问题
- VS+Qt modules项目后期勾选Network、XML等:
https://blog.csdn.net/yanchenyu365/article/details/90055024 - QT入门问题点:QT Designer 没有“转到槽”项:
https://blog.csdn.net/zcm_bobo/article/details/104658045 - 重新编译VTK库,编译的方法可以参考下面链接的7.1之前的部分,之后的部分不用理会。
https://blog.csdn.net/numit/article/details/9983495?utm_source=distribute.pc_relevant.none-task
将build目录下的QVTKWidgetPlugin.dll,QVTKWidgetPlugin.exp,QVTKWidgetPlugin.lib复制到QT安装目录Qt\plugins\designer。打开Qt Designer可以发现多一个组件。