初识Qt
本章将介绍什么是Qt,同时与大家一起安装Qt,根据不同用户的编程习惯,这里我们介绍在Windows 安装和在Ubuntu 下安装。教程重点是以在Ubuntu 环境下编写Qt 作讲解。配置Ubuntu下的Qt Creator 的中文输入法,讲解Qt Creator 的界面组成与设置。新建一个简单的“Hello world”例程带大家一起学习Qt 应用程序的建立、编译及调试步骤。
我们重点关注3.4 小节,搭建Linux 下Qt 的开发环境,因为搭建环境是很关键的一步。有些朋友可能在搭建环境这一步就卡住,走不下去了,后面的开发还有很长的路要走。所以我们需要有足够的耐心,不要浮躁,细心的按教程的步骤走,遇到错误,学会随机应变,找解决方法。如果对Ubuntu 的使用不熟悉,建议多多练习。
Qt 是什么
Qt 是一个跨平台的C++开发库。主要用来开发图形用户界面(Graphical User Interface,简称GUI)程序。
Qt 虽然经常被当做一个GUI 库,用来开发图形界面应用程序,但这并不是Qt 的全部;
Qt 除了可以绘制漂亮的界面(包括控件、布局、交互),还包含很多其它功能,比如多线程、访问数据库、图像处理、音频视频处理、网络通信、文件操作等,这些Qt 都已经内置了。
Qt 还存在Python、Ruby、Perl 等脚本语言的绑定,也就是说可以使用脚本语言开发基于Qt 的程序。开源社区就是这样,好东西就会被派生扩展,到处使用,越来越壮大。
Qt 支持的操作系统有很多,例如通用操作系统Windows、Linux、Unix,智能手机系统Android、iOS、WinPhone,嵌入式系统QNX、VxWorks 等等。
简单的来说,Qt 可以做很多东西,好比如Windows 下的软件也有很多是Qt 开发的,这里我很喜欢它的一个特性就是跨平台使用,“跨平台”代表一份代码可以无需任何修改或者小修改就可以在其他平台上运行。
笔者不再介绍Qt 的发展史了,这些网上可以查到。写太多感觉也没有什么用,反正Qt 就是您做界面需要选择的一个好工具!
Qt 能做什么
Qt 能做什么?理论上来说,您能想到的App,Qt 它都基本能做。像WPS 这种大型的桌面应用,Maya(3D 建模),一些游戏的内核都可以使用Qt 来实现。甚至您能快速使用Qt 来开发一款像酷狗,网易云音乐这样的音乐播放器。在嵌入式里,使用Qt 来开发界面已经是无可替代的一种趋势。工控界面最常用,一些移动端的界面也开始使用Qt。像点菜机,温度采集数据显示,
汽车仪表,速度显示界面等。Qt 能做优美的界面,所以推出了QML(一种描述性的语言)。每次Qt 更新都有在Quick(QML 使用的模块)上优化或者添加功能,看来Qt 打算在移动设备端的开发市场端进军!呵呵,其实Qt 早已经在这块发展了!
下面是本书开发的一些界面小例子,请欣赏。要想开发好的界面关键是要有想法。可以说如果您是做界面其实大多数时间花在美化界面上,功能都是比较容易实现的。最好读者需要有一定的美工基础,例如会基本的PS,在开发过程中就不用去麻烦公司的美工了,开发界面和程序一起做。
暗黑主题音乐播放器界面UI 设计。
简约的视频界面设计。
炫酷车载音乐APP 主界面开发。
这些设计都尽在第二篇提高篇里的第十二章多媒体章节和项目实战篇里。笔者水平有限,但是爱设计UI,尽量把UI 设计得好看与实用。至少比其他Qt 书籍设计的好看。Qt 设计本来就应该在界面上多下功夫,功能都是不难实现的,日后在大家会发现,设计界面占用的时间可能是70%,写程序可能占30%。大家应该在设计界面上多多下功夫!!!
Qt/C++与QML
QML 是Qt C++基础上拓展的。也就是说Qt C++上所使用界面的类,基本都是可以拓展到QML 上。如果不能拓展的,也可以直接QML 与C++一起使用。实际上Qt C++效率更高。所以本教程都是Qt C++为基础做教程入门教程,QML 可以说是一个加分项。Qt QML 模块提供QML 语言开发应用程序和库提供了一个框架。QML 本身不涉及显示部分的内容,所以Qt Quick模块充当了这部分的作用。至于他们三者的关系,我们就这样简单的了解一下就行了。
Qt C++在底层编写功能更突出,若要做好看的界面且功能强大,Qt C++与QML 语言结合是必不可少的。QML 语言负责界面层,而底层调用Qt C++。比如文件的读写、图像处理、多线程通信等这些都是需要C++来编写完成的。
在目前来说,Qt C++更成熟,QML 目前也比较成熟了,因为每次更新版本QML 也有更新。
不过在低版本的Qt(如Qt5.6 以下),QML 还不够成熟,QML 在高版本的Qt(如Qt5.7 以上)更成熟。C++依旧是Qt 的主要编程语言,Qt 5 也并没有忽略它,Qt 5 添加了很多新的C++ API,而且会持续更新。引入QML 应该是Qt 向移动端市场的一个突破,还有python 和javascript 都可以在Qt 里用。
说了C++又说QML,但是Qt 基础教程还是主打Qt C++,有了基础学Qt 里其他的东西就不难!
如何选择Qt 版本
初学者应该要如何选择Qt 的版本?截至2020 年最新的Qt5 版本是Qt5.15。可以参考这个网址https://doc.qt.io/qt-5/qt5-intro.html 来看看各个Qt 版本的更新说明。Qt6 已经出了,我们有必要直接上最新版本的Qt6 吗?不!Qt6 理论上与Qt5 能兼容,但是许多公司在用Qt5 甚至Qt4。学习了Qt5,Qt6 等稳定版本出来,等市场上用的多,我们用Qt6 才是最明智的选择,新出的事物往往要有一定的时间去适应!我们主要是简单对整个Qt 版本说明一下。首先,Qt 版本的升级肯定是对上一版本进行优化。比如Qt4 与Qt5 的的语法区别,Qt5 的信号槽写法与Qt4的信号槽写法有区别。除了Qt5.10 以上的版本,Qt5.6 与Qt5.9 是Qt 长期支持的版本LTS(Lo
ng Term Support,简称LTS)。但是2020 年3 月最近Qt 官方将Q5.2~Qt5.8、Qt5.11 归档到http://download.qt.io/new_archive/qt/。所以很多小伙伴们想用老版本的Qt 要到上面这个链接去下载了。毕竟有些时候我们开发板程序处于某个Qt 版本下开发的,如果要迁移到新版本的Qt 可能还要考虑到兼容问题!不过变化不会很大的,请放心!
下面贴出Qt 官方下载链接http://download.qt.io/archive/qt/。我们直接进入这个链接可以看到下图。下图和上图加起来才是Qt 的完整版本。那么多版本任君选择。还可以看到Qt 版本在不断的更新,更多的功能将会注入到Qt 这个GUI 库里,使我们开发变得更简单和有趣!Qt6的时代即将到来!
理论上我们选择Qt 的版本越新越好,这是当然的,不过我们还是要确定一个版本是必须的,因为日后写好的程序要长期运行在一个确定的版本里,避免随意升级带来其他兼容性问题,或者重复重复移植等工作。Qt 还会不断的更新,Qt5.9 及Qt5.12 是两个LTS 长期支持的版本,本教程以的Qt5.12.9 版本进行开发及实验说明。
Windows 下安装Qt
在上一小节里,我们已经知道Qt 的下载地址。在Windows 按如下方法下载。进入地址,此节只介绍Windows 下的Qt 安装。
进入http://download.qt.io/archive/qt/5.12/5.12.9/,(注意如果找不到下载链接,我们就进行http://download.qt.io 这个顶层目录一个个目录找,因为Qt 下载链接会变动)。从Qt5.12 版本里选择Qt5.12 的第九个版本。“.dmg”是Mac 系统版本的安装包,此处不作安装讲解。下图为Windows 和Linux 下的安装包格式。
鼠标右键选中Winodws 下载安装包,选择使用迅雷下载,如果没有安装迅雷就直接点击链接下载吧,下载速度可能会比较慢。使用迅雷下载,速度会比较快。
下载完成后,找到安装包文件,双击该安装包文件。开始安装。
弹出开始安装界面,点击Next。
登录Qt 帐号,如果您还没有帐号及密码,请到https://www.qt.io/自行注册一个。
Qt 安装程序,开源版本的Qt 遵循GPLv 2,GPL v3 或者LGPL v3 协议。勾选同意使用开源版本Qt,填写公司/个人的名字。
Qt 的欢迎安装界面
修改安装目录
选择需要安装的组件,Windows 选择安装的组件如下。MSVC 是微软的VC 编译器,需要安装VC 相关库才能使用。MinGW 是指是Minimalist GNU on Windows 的缩写,允许您在GNU/Linux 和Windows 平台生成本地的Windows 程序而不需要第三方C 运行时库。除了安卓选项arm 等选项,我们只需要勾选MinGW 的即可。Sources 是Qt 源码,Source 以下的是Qt的一些模块,可选择安装或者不装。这里我们选择全装。
选择同意许可协议,再点击下一步。
点击下一步。
准备安装Qt,可以看到我们安装的组件越多占用的安装空间就会越大。接近10G。
正在安装Qt。
安装完成,勾选启动Qt Creator,点击完成。
安装完成打开Qt Creator 的界面如下。
Linux 下安装Qt
安装Qt
同样地,进入http://download.qt.io/archive/qt/5.12/5.12.9/下载页面(注意如果找不到下载链接,我们就进行http://download.qt.io 这个顶层目录一个个目录找,因为Qt 下载链接会变动),选择Linux 的安装包下载。使用迅雷下载再拷贝过去Ubuntu 虚拟机或者直接复制链接地址到Ubuntu 虚拟机下载。
如下图,复制链接下载地址到Ubuntu 虚拟机终端下使用指令wget 下载,http://download.qt.io/archive/qt/5.12/5.12.9/qt-opensource-linux-x64-5.12.9.run 。
wget http://download.qt.io/archive/qt/5.12/5.12.9/qt-opensource-linux-x64-5.12.9.run
赋予可执行权限,加上sudo 权限进入安装,这样会安装在/opt 目录下。
chmod +x qt-opensource-linux-x64-5.12.9.run
sudo ./qt-opensource-linux-x64-5.12.9.run
执行安装指令后,将会弹出Qt 的安装界面,这与Windows 下的Qt 安装步骤一样,请参阅3.3 小节,安装选择目录时,默认安装目录即可。安装组件选择如下。
安装完成,在左下角应用程序中心找到Qt Creator,点击打开Qt Creator。
打开Qt Creator 的界面如下,安装完成。
配置Qt Creator 输入中文
配置Ubuntu 中文环境
Ubuntu 的默认安装环境是使用的语言是英文,很多同学不习惯。现在我们将Ubuntu 的系统语言设置成中文。在Ubuntu 右上角,点击设置图标如下图第①步。
按如下图设置,点击(install/Remove Languages …)安装或者移除语言,在安装语言处选择简体中文,点击Apply 应用即可。
配置完成后,点击重启(或者注销Ubuntu),重启后,因为我们已经更新了系统的语言,Ubuntu 询问我们需不需要将系统文件夹的名称也改成中文。这里作者选择否,保留旧的名称。
保留旧的名称有一定的好处,就是我们进入这些访目录时,直接使用英文,不用切换到中文输入法。严格来说,最好是统一用英文环境开发了。这里为了初学者或者有强迫中文者,所以我们这里需要配置中文的环境,及后期开发Qt 需要写中文注释,方便理解与给后人看。
配置中文输入法
在上面我们已经配置好中文环境,并有拼音输入法ibus,但ibus 并不好用,Qt Creator 不支持ibus 输入中文。好的生产工具决定好的生产力,下面我们介绍一下Fcitx 输入法。
Fcitx (Flexible Input Method Framework) ──即小企鹅输入法,它是一个以GPL 方式发布的输入法平台,可以通过安装引擎支持多种输入法,支持简入繁出,是在Linux 操作系统中常用的中文输入法。它的优点是:短小精悍、跟程序的兼容性比较好。
Fcitx 内置的输入法支持中文拼音和基于字符表的输入(例如五笔),根据语言的不同,有不同的输入法引擎可以选择。
在Fcitx 支持的拼音输入法中,内置拼音响应速度最快。Fcitx 同样支持流行的第三方拼音输入法以提供更好的整句输入效果.
fcitx-sunpinyin 在输入速度和输入精度之间有较好的平衡。
fcitx-libpinyin 算法比sunpinyin 先进。
fcitx-rime, 即著名中文输入法Rime IME 的Fcitx 版本。但它不支持Fcitx 本身的#特殊符号和#快速输入功能,自定义设置请参见官方(http://rime.im/)。
fcitx-googlepinyin, Google 拼音输入法for Android.fcitx-sogoupinyin(AUR,)搜狗输入法for linux—支持全拼、简拼、模糊音、云输入、皮肤、中英混输入。官方网址(http://pinyin.sogou.com/linux/)。
fcitx-cloudpinyin 可以提供云拼音输入的支持,支持Fcitx 下的所有拼音输入法,Fcitx-rime除外。
fcitx-chewing 为Fcitx 添加chewing (繁体中文注音) 输入引擎支持。依赖libchewing。
fcitx-table-extra adds Cangjie,Zhengma,Boshiamy support。
安装Fcitx 输入法,下面主要介绍两种输入法(五笔输入法与拼音输入),本次以安装五笔输入法为例。
sudo apt-get install fcitx-table-wubi // 五笔输入法
sudo apt-get install fcitx-sunpinyin // 拼音输入法请装sunpinyin 或者fcitx-googlepinyin
再到右上角点击系统设置,找到语言支持,将键盘输入方式系统点击下拉复选框选择为fcitx,然后点击应用到整个系统,再关闭。如下图步骤。
为了确保刚配置的环境生效,完成以上步骤后重启Ubuntu 系统。
按如下步骤,在弹出的“输入法配置”对话框中,如果没有出现五笔字形输入法,点击左下角的“+”号按钮将五笔字形添加到输入法列表中,再点击第8 步的“^”将五笔字型添加至最上方。
打开Qt Creator,在左下角“显示应用程序”,找到Qt Creator 图标单击打开Qt Creator。
此时右上角的输入法标志还是英文输入法,无法在④处输入中文,按Ctrl+Shift 也切换不到中文输入法状态下。
作者为解决Linux Qt Creator 不能输入中文的问题,已经编译过fcitx 插件,已经上传到Gitee仓库里,大家可以下载直接使用。
如果您没有安装Git,请先使用下面的指令安装Git。
sudo apt-get install git
使用下面的指令下载Qt Creator 插件,本插件支持Qt5,Qt4 没有测试过。本插件曾经在Qt5.5.1 上测试过,后来升级了Qt 版本到Qt5.12.9。Qt5.5.1 与Qt5.12.9 这两个Qt 版本的安装目录插件路径有变动(指版本相差大,安装路径有变动)。从Gitee 下载的插件适用于Qt5.12 版本左右版本。如果使用其他版本请自行修改fcitx-install.sh 脚本里的安装路径!
git clone https://gitee.com/QQ1252699831/fcitx-qt5-1.1.1.git
cd fcitx-qt5-1.1.1
直接执行脚本安装,原理是拷贝fcitx-qt5-1.1.1 文件夹下里面的插件到Qt Creator 的安装目录下。请注意个人安装的Qt Creator 路径,一定要与作者安装的路径一样,否则需要自己修改fcitx-install.sh 脚本里的路径!
./fcitx-install.sh
此时再重新打开Qt Creator 方可输入中文!如下图。需要按Ctrl+Space(空格键)激活输入法,再按Ctrl+Shfit 切换输入法到五笔输入法。
Qt Creator 简单使用
Qt Creator 界面组成
启动Qt Creator 后,Qt Creator 的主界面如下图,默认打开的是欢迎页面。可以看到Qt Creator里自带很多示例。在Ubuntu 里,由于Qt Creator 安装在/opt 目录下,这个目录普通用户是没有权限写的,只能够读。如果要打开示例先点击后选择“复制项目并打开”。作者比较喜欢Qt Creator界面设计的。十分简洁,还自带示例,不会写也会参考!最重要的是Qt Creator 里左侧栏的“帮
助”按钮,有很多使用说明,总结的非常好,对学习Qt 的类有很大帮助,如果学习到某个方法或者类不会用,可以打开这个“帮助”来搜索这个类或者方法的用法。没有任何一本教程能有Qt 帮助文档这么详细了,可惜是英文的,初学者学起来还是会一定难度。多多参考这个Qt 帮助文档,因为那里能学到很多,例子说明也特别多,每个方法、类、信号与槽等都有详细的解释。
单击顶部工具栏的“帮助”菜单,点击“UI Tour”会出现介绍Qt Creator 各组件的说明。
这个大家一定要看看,虽然是英文的说明,但是大体有个了解。这里就不再截图说明了。后面编写第一个Qt 程序也会说明整个项目编写及编译运行的流程。界面组成我们只需要了解这么多。
Qt Creator 设置
Qt Creator 里的设置比较重要。在Qt Creator 的设置里我们可以做些什么呢?我们一般会设置字体的大小、颜色和背景颜色等这样看起来比较炫,或者比较符合自己的风格。Qt Creator里也有很多主题,拥有好多种搭配,相信有一种是您喜欢的,如果不喜欢可以自定义里面的字体大小、颜色和背景颜色等。点击顶部的菜单栏的“工具”》“选项”。
打开选项后可以看到如下图的界面。
下面我们主要介绍常用的几项:
- Kits:主要显示的是编译工具。我们在Ubuntu 安装Qt Creator 时,在安装选项里已经勾选了Desktop Qt5.12.9 GCC 64bit 这个选项。所以在Kits 这个页面就能检测到安装的编译工具。
还有Qt Versions 等项都可以自由查看。重要的是我们可以在这个Kits 里配置ARM 平台的编译工具(后面以某个ARM 板卡平台为教程时有讲到如何配置),之所以Qt 能够跨平台,是因为Qt 有不同平台的编译工具。 - 环境:在这个项里可以设置不同的主题和语言等。Qt Creator 默认的主题和系统语言即可。
- 文本编辑器:可以设置文本编辑器的字体大小、颜色等。还可以设置某些类型的字体颜色,如关键字、字符串和注释等。
- 构建和运行:常用的是设置项目的目录。其他一般不用修改,默认即可。
第一个Qt 程序
“hello world ”的起源要追溯到1972 年,贝尔实验室著名研究员Brian Kernighan 在撰写“B 语言教程与指导(Tutorial Introduction to the Language B)”时初次使用(程序),这是目前已知最早的在计算机著作中将hello 和world 一起使用的记录。第一个程序都是以“hello,world”作为默认的第一个程序,我们延续这种经典的做法吧,感受程序的伟大。同时可以让我们初步了解Qt 项目搭建的基础流程。
新建一个项目
在Ubuntu18 里打开Qt Creator,也就是左下角软件中心处点击后,找到Qt Creator 的图标后点击打开。单击文件Qt Creator 的文件,选择新建文件或者项目。注意有快捷键Ctrl + N。直接在Qt Creator 激活状态和英文状态的输入法下使用“Ctrl + N”也可以快速打开新建项目。
弹出的新建项目如下图,这里我们可以看到有很多模板(包括项目模板和文件和类模板)可以使用,包括Qt,Qt Quick,Qt for Python,…,C++等等。作为初学者我们选择第一个Application(Qt)和Qt Widgets Application,所谓的模板就是Qt 为了方便开发程序,在新建工程时可以让用户基于一种模板来编写程序,包括cpp 文件,ui 文件都已经快速的创建,而不用用户手动创建这些文件。这样对用户的开发带来极大的便捷。当然用户可以可以自己手动创建项目,一个一个往里面加也是可以的。但是初学者不建议这么做,我们的目的是先体验一遍项目搭建的流程,等日后有空再自己回头尝试这么做吧!
如果您的输入法现在处于中文输入法,先按Ctrl 再按Space(空格)切换成英文输入法,在名称处输入项目为“01_hello_world”,这里的项目路径为笔者个人的家目录路径下的Qt 目录下,勾选设为默认的项目路径,这样以后做项目实验时都是默认选择这个目录作为项目路径而不用自己手动选择路径了。选择下一步。
默认已经是选择qmake 编译,主要用qmake 生成Makefile 用于项目的编译。点击下一步即可。
这里默认选择的基类为QMainWindow。在Base class 一项中我们还可以看到还有QWidget和QWialog 这样的基类可以选择。在C++篇我们已经学习什么叫基类,简单的来说,我们创建的这个项目是基于QMainWindow 类去开发的。默认勾选“Generate form”,意思是生成ui 窗体文件mainwindow.ui。为了学习方便,我们统一默认基类为QMainWindow,但是注意,在嵌入
式里一般不需要标题栏,状态栏等,所以常用的是QWidget 基类。
QMainWindow:主窗口类,主窗口具有主菜单栏、工具栏和状态栏。类似于一般的应用程序的主窗口。如果您想做个嵌套的窗口程序开发的软件,不妨选择这个QMainWindow。
QWidget:是可视界面类的基类,也就是说QMainWindow 类也是由QWidget 继承封装而来。所以QWidget 要比QMainWindow 功能少一些。
QDialog:对话框类,建立一个对话框界面。比较少使用此项作为基类。一般以QMainWindow和QWidget 作为基类的居多。注因为QWidget 不带窗口标题栏等,嵌入式里最好QWidget。
在高版本的Qt Creator 里,有Translation 这一项,Qt 提供了一个(.ts)的文件给您,.ts 是可读的翻译文件,使用简单的XML 格式。我们暂时只需要知道这个东西是个可读的翻译文件即可。极少需要使用到。点击下一步。
勾选编译器,这个编译器是我们在安装组件时选择的,使用这个编译器可以编译出Ubuntu版本上跑的可执行程序。这么一说是不是觉得还能编译出其他平台的可执行程序?没错,假若我们现在有ARM 平台的Qt 编译器,那么选择ARM 平台的Qt 编译器即可编译出Qt 在ARM平台上的可执行文件(这里说的可执行文件类似window 的exe 程序文件一样,直接能够运行)。点击下一步。
到这里,Qt Creator 询问是否使用版本控制,如果您学习过像Git 这样的版本控制工具,可以选择Git,将您的项目使用版本控制。默认选择是无版本控制,直接点击完成即可。
新建的“01_hello_world”项目如下。
项目文件介绍
其中,左侧有上下两个子窗口,上面的窗口显示了项目的文件结构,显示当前的项目为“01_hello_world”,细心的还会发现“01_hello_world”是用粗体黑色标明。说明此项目是活动项目,活动项目的项目根节点都是用粗体字体表示的。如果打开了多个项目,那么我们只需要观察哪个是加粗的项目名就表示当前活动项目。
Qt Creator 和其他IDE 开发软件一样。都是分组管理项目内的各种源文件,下面是项目内的文件简介。
01_hello_world.pro 是项目管理文件,这个项目管理文件十分重要,当您加入了文件或者删除了文件,Qt Creator 会自动修改这个*.pro 文件。有时候需要打开这个*.pro 文件添加我们
的设置项。
Header 分组,这个节点下存放的是项目内所有的头文件*.h。
Source 分组,这个节点下存放的是项目内的所有C++源码文件*.cpp。
Forms 分组,这个节点下是存放项目内所有界面文件*.ui。*.ui 文件由XML 语言描述组成,
编译时会生成相应的cpp 文件,这样交叉编译器就可以编译它了。
项目文件*.pro
01_hello_world.pro 文件如下所示。此pro 文件在高版本的Qt 与低版本的Qt 文件有些区别,且在Windows 与Linux 系统下生成的pro 也有些区别,但是变化不大。
01_hello_world.pro文件内容
1 QT += core gui
2
3 greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
4
5 CONFIG += c++11
6
7 # The following define makes your compiler emit warnings if you use
8 # any Qt feature that has been marked deprecated (the exact warnings
9 # depend on your compiler). Please consult the documentation of the
10 # deprecated API in order to know how to port your code away from it.
11 DEFINES += QT_DEPRECATED_WARNINGS
12
13 # You can also make your code fail to compile if it uses deprecated APIs.
14 # In order to do so, uncomment the following line.
15 # You can also select to disable deprecated APIs only up to a certain
version of Qt.
16 #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the
APIs deprecated before Qt 6.0.0
17
18 SOURCES += \
19 main.cpp \
20 mainwindow.cpp
21
22 HEADERS += \
23 mainwindow.h
24
25 FORMS += \
26 mainwindow.ui
27
28 # Default rules for deployment.
29 qnx: target.path = /tmp/$${TARGET}/bin
30 else: unix:!android: target.path = /opt/$${TARGET}/bin
31 !isEmpty(target.path): INSTALLS += target
第1 行,添加了Qt 的支持的模块,core 与gui 库是Qt 的默认设置。
第3 行,比较Qt5 版本,如果是Qt5 版本,在main.cpp 中application 是在QtWidgets 中的,因此要包含这个库。
第5 行和第11 行,分别配置的是使用c++11 和添加QT_DEPRECATED_WARNINGS 定义。
第18 行,SOURCES 下的是源文件。
第22 行,HEADERS 下是头文件。
第25 行,FORMS 下是ui 界面文件。
第28 行,部署默认的规则。
第29 行,qnx:判断是不是qnx 操作系统,赋值target.path = /temp/
T
A
R
G
E
T
/
b
i
n
。第
30
行,如果是
u
n
i
x
系统但不是安卓,赋值
t
a
r
g
e
t
.
p
a
t
h
=
/
o
p
t
/
{TARGET}/bin。 第30 行,如果是unix 系统但不是安卓,赋值target.path = /opt/
TARGET/bin。第30行,如果是unix系统但不是安卓,赋值target.path=/opt/{TARGET}/bin。
第31 行,如果target.path 为空目录,赋值INSTALLS += target。
如果需要修改生成目标的可执行程序名字,可赋值TARGET = xxx。否则TARGET 将默认取值为项目的名字。
样式文件*.ui
mainwindow.ui 是一个xml 类型的文件,它的xml 内容如下。这个文件是生成的不能手动编辑。只能够通过图形界面修改其属性。
1 <?xml version="1.0" encoding="UTF-8"?>
2 <ui version="4.0">
3 <class>MainWindow</class>
4 <widget class="QMainWindow" name="MainWindow">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>800</width>
10 <height>600</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>MainWindow</string>
15 </property>
16 <widget class="QWidget" name="centralwidget"/>
17 <widget class="QMenuBar" name="menubar">
18 <property name="geometry">
19 <rect>
20 <x>0</x>
21 <y>0</y>
22 <width>800</width>
23 <height>28</height>
24 </rect>
25 </property>
26 </widget>
27 <widget class="QStatusBar" name="statusbar"/>
28 </widget>
29 <resources/>
30 <connections/>
31 </ui
双击mainwindow.ui 后可以跳转到设计界面,如下图。下面主要介绍主体部分。
- ①是控件栏,有各种各样的控件,上方的Filter 是过滤器,输入首写字母就可以快速定到我们想要找的控件。
- ②显示的是我们的窗口程序了,上面已经带有MainWindow 对象及其几个子对象,默认MainWindow 就带有菜单栏和状态栏。
- ③是对象栏,②处用到的对象都在③处显示。
- ④是属性栏,点击③处对象栏的某个对象,就可以在④属性栏里编辑它的属性了。属性项有很多,包括位置,大小,文字,颜色,字体等等。
头文件*.h
点击左边栏的编辑,回到项目的编辑工作窗口。点击项目下的Headers 下的mainwindow.h,mainwindow.h 一般有与之对应的一个cpp 文件叫mianwindow.cpp。其中mainwindow.h 包含类的声明,mianwindow.cpp 包含类的实现。这与我们前面学习的c++是一样的。mainwindow.h 的内容如下。
1 # ifndef MAINWINDOW_H
2 # define MAINWINDOW_H
3
4 # include < QMainWindow >
5
6 QT_BEGIN_NAMESPACE
7 namespace Ui { class MainWindow; }
8 QT_END_NAMESPACE
9
10 class MainWindow : public QMainWindow
11 {
12 Q_OBJECT
13
14 public:
15 MainWindow( QWidget *parent = nullptr );
16 ~MainWindow();
17
18 private:
19 Ui::MainWindow * ui;
20
};
21 # endif /* MAINWINDOW_ */
第7 行,定义名称空间Ui ,里面有一个类MainWindow ,这个MainWindow 和第10 行里的MainWindow 不是同一个对象。实际上,这个文件里的MainWindow 类有一个成员*ui 就是指向第19 行的Ui::MainWindow 的指针。这都是为了使用.ui 文件设计界面的。
第12 行,MainWindow 的声明中第一行是Q_OBJECT,这是一个宏,由Qt 进行处理,这也是Qt 针对C++扩展的地方,所有用到信号的类都要加这个宏。
源文件*.cpp
点击项目下的Sources 下的mainwindow.cpp。可以看到它的内容如下。
1 # include "mainwindow.h"
2 # include "ui_mainwindow.h"
3
4 MainWindow::MainWindow( QWidget *parent )
5 : QMainWindow( parent )
6, ui( new Ui::MainWindow )
7
{
8 ui->setupUi( this );
9
}
10
11 MainWindow::~MainWindow()
12
{
13 delete ui;
14
}
第2 行,MainWindow 的实现类中,第2 行include 了一个文件ui_mainwindow.h 这个文件是Qt 根据.ui 文件自动生成的,也就是说ui_mainwindow.h 要点击编构建后才生成,我们才能看到这个文件。构建/编译后可以在debug/release 的目录找到这个文件。
第6 行,在MainWindow 构造函数中用“,”隔开,new 一个Ui 中的MainWindow。这里是一种初始化成员的方法。
第8 行,ui->setupUi(this);这句话是进行界面初始化,将this(指的是MainWindow 类的本身),作为参数传到setupUi 里,ui 界面文件的对象会以this 为父对象,所有子对象都将显示在MainWindow 里。我们要想使用ui 里的对象,必须将代码写在ui->setupUi(this)这句话之后,因为ui->setupUi(this)会先初始化里面的对象,只有初始化里面的对象我们才能使用这个对象。
第13 行,析构函数里delete 掉ui。在Qt 里我们需要在析构函数里delete 的对象一般是new创建的并且没有父对象的对象。
小提示:this 在成员函数的开始执行前构造的,在成员的执行结束后清除。
点击项目下的Sources 下的main.cpp。可以看到它的内容如下。
1 # include "mainwindow.h"
2
3 # include < QApplication >
4
5 int main( int argc, char *argv[] )
6
{
7 QApplication a( argc, argv );
8 MainWindow w;
9 w.show();
10 return(a.exec() );
11
}
第3 行,包含QApplication 类的定义。在每一个使用Qt 的应用程序中都必须使用一个QApplication 对象。QApplication 管理了各种各样的应用程序的广泛资源,比如默认的字体和光标。
第5 行,main()函数是程序的入口。几乎在使用Qt 的所有情况下,main()只需要在把控制转交给Qt 库之前执行一些初始化,然后Qt 库通过事件来向程序告知用户的行为。argc 是命令行变量的数量,argv 是命令行变量的数组。
第7 行,a 是这个程序的QApplication。它在这里被创建并且处理这些命令行变量。
第8 行,创建一个对象w,这个对象就是MainWindow。
第9 行,调用方法show()。这样程序界面才能显示。
第10 行,这里就是main()把控制转交给Qt,并且当应用程序退出的时候exec()就会返回。
在exec()中,Qt 接受并处理用户和系统的事件并且把它们传递给适当的窗口部件。
修改ui 文件显示hello world
双击mainwindow.ui,进入“Ui 设计器”页面如下。
可以看到中间的要设计的主窗体目前是空的,但是窗体上已经有好几个成员了,可以看到右边的对象栏,已经有3 个对象成员在里面了,只是我们看不出来而已,效果不太明显。
我们要在窗体里显示“Hello World!”,那么需要使用左边的控件栏,目前左边的控件栏的控件有很多,也分很多类别。这些对于我们初学者来说,暂时不用知道它们是怎么用的。后面教程其中的类别进行说明它们是如何使用的。现在我们主要是想要显示“Hello World!”。要明白我们的主要目的,切勿看到左边的控件栏就想一个个的去试,又不知道怎么去用。这样的学习方式是不对的。要显示“Hello World!”,那么我们需要用常用的文本显示控件,常用的就是Label 文本控件了,当然PushButon 按钮类也是可以显示文本的。但是我们不需要按钮的功能,只需要显示文本。所以用Label 文本控件就可以了。
如下图,在左边的控件图,找到Label 控件,Label 控件在“Display Widgets”(翻译为显示小部件)下,选中Label 控件,将其拖至②处中间的设计窗体里。然后在③处选中label 对象,再在④处label 对象的属性栏找到text 文件属性,修改里面的文本为“Hello World!”。这样我们就已经显示“Hello World”在窗口里了。当然双击中间的Label 控件也是可以修改其文本属性的,对初学者来说还是按步骤修改,这样学习“Ui 设计器”是如何使用的。这里我们已经完成显示“Hello World!”的设计了。
项目编译&调试&运行
完成上面的“Hello World!”后,我们肯定下一步是想编译运行我们的程序了,看看效果如何!
按如下步骤,先返回编辑页面,再点击②处左下角的绿色三角符号(或者按Ctrl + R 快捷键编译且运行),编辑时会输出编译信息,点击③处的编译输出窗口里,可以看到编译时的编译信息,能看到编译警告、编译错误、编译链接、相关编译器编译过程等。
在Ubuntu 下刚装的Qt Creator,毫无意外,第一次编译时会报“Cannot find -lgL”的错误。
由于Qt5.0 的库默认会链接到OpenGL,但是在Ubuntu 机器上没有安装OpenGL,所以们需要在Ubuntu 下安装OpenGL Library。在Ubuntu 终端下输入如下指令。
sudo apt-get install libglu1-mesa-dev
安装完成后,我们再点击左下角的绿色三角形试试,可以看到如下结果!在下图②处已经看到没有报错信息了。如果还有其他报错,建议回到第一章C++环境设置里寻找C++环境配置的相关指令安装好相应的库。可能初学者跳过了C++就直接到Qt 这部分了,Qt 也依赖C++的环境的!
运行的结果如③处,弹出一个窗口,中间就是我们设计的Hello World!
如果需要调试,请先关闭上面的“Hello World!”窗口,点击右上角的“x”关闭即可!再按如下步骤点击,在程序里右键设置断点,再点击左下角③处的Debug 绿色三角符号按钮。不过我们很少使用调试,这种简单的程序完全是可以预知它的运行过程的,如果我们想要知道它的运行过程,是可以使用这个调试的功能去了解Qt 程序的运行流程的!
使用Qt Designer 开发
本章将简介使用Qt Creator 里自带的Qt Designer,使用Qt Designer 比较方便的构造UI 界面。特点是方便布局,比较形象。
使用UI 设计器开发程序
在这小节里我们继续学习如何使用Qt Designer 开发程序,Qt Designer 是属于Qt Creator 的一个功能而已,大家不要搞混了。Qt Designer 也叫UI 设计师或者UI 设计器,这都是指的同一个东西而已。下面就简单介绍使用UI 设计器开发程序,以连接信号与槽为例,简单的介绍这个开发流程。最后我们思考一下这种开发方式的好处以及不便之处。
在UI 文件添加一个按钮
新建一个项目为02_designer_example。如果还不会新建工程,请回到3.6 小节,步骤一样,这里不再详细说项目的建立过程了。新建的项目如下,为了方便截图,笔者已经把3.6 小节的项目“01_hello_world”关闭了。剩下的就是我们这个新建的项目02_designer_example。
添加按钮的方法与3.6.3 小节添加Label 的方法一样,在左边找到Push Button,然后拖拽到中间的显示窗体里,如下图。
并将这个PushButton 的text 属性(文本属性)改为“关闭程序”。我们在4.1.2 小节要设计点击这个按钮将关闭这个窗口,关闭这个程序。
在UI 文件里连接信号与槽
在UI 设计器里有两种方法可以连接信号与槽。初学者可能不了解什么是Qt 的信号与槽。
这里先简单的介绍一下信号与槽的功能。所谓信号即是一个对象发出的信号,槽即是当这个对象发出这个信号时,对应连接的槽就发被执行或者触发。以上为非专业术语解释,在下一章节可以学到Qt 信号与槽机制。现在我们只要了基本的去了解一下这个信号与槽即可。可以说信号与槽在Qt 里是必不可少的。要想事件做出对应的动作就必须用到信号与槽。
UI 设计器里信号与槽的连接方法一:
在主窗体的上面部分,我们可以看到一些小小的按钮,如下图框框部分。用鼠标放在这些按钮上面可以查看这个按钮是什么作用。信号槽连接的按钮也在上面。
点击信号槽连接的按钮如下,如下图①处,点击进入信号槽连接模式(若想退出信号槽连接模式,则点击①处左边的按钮),进入信号与槽的连接模式后,将鼠标选中我们的“关闭程序”按钮,按住按钮,然后用鼠标向外拖动,如②处。此时就会出现信号槽连接的符号。
之后按如下图步骤选择,左边的“关闭程序”pushButton 按钮的信号,可以看到一个对象的信号可以有多种。右边的QMainWindow 的槽函数,如果有其他对象,右边不一定只有MainWidnow 的槽函数(槽),也有可能是其他对象的槽。我们选择按钮的clicked()信号,将其连接MainWindow 对象的close()槽。这样就完成了信号与槽的连接,非常简单。我们也可以预知这个信号与槽的功能,当“关闭程序”pushButton 发出了clicked()信号(也就是单击信号)。
这个信号由“关闭程序”pushButton 被单击时发出。它就会触发MainWindow 的close()。进而使整个程序关闭。MainWindow 的close()就是退出关闭程序,退出程序的意思。
完成信号槽连接,如下图。要想返回编辑部件模式点击如下图标注位置的按钮。下图就是信号与槽连接的图示了。在编辑部件模式下我们是看不见的,只有信号槽模式才能看见这样的图示。
UI 设计器里信号与槽的连接方法二:
选中“关闭程序”pushButton 按钮,然后右键,如下图。选择“转到槽”。
点击“转到槽”后,弹出下面的窗口,这一步是先让我们选择信号。按如下图选择。如果细心的同学,我们还发现这个clicked()信号并不是pushButton 的,而是QAbstactButton 的。只是pusbButton 继承了QAbstracButton,同时把这个信号也继承了下来。除此之外我们还看到其他信号也是不是属于pushButton 的,也是被继承下来了。所以我们在C++基础部分学过的继承。
在Qt 里的作用表现的淋漓尽致!根本不用重写pushButton 的clicked()事件。pushButton 只需要继承父类的clicked()事件即可!
点击OK 后,就会跳转到槽函数里,这个代码由Qt Creator 自动生成。
同时在mainwindow.h 里声明了这个槽函数。
如果我们学过C#,这就好像C#里的跳转到事件一样。其实这种便捷的编程方式很多编程的IDE 都非常类似。只要我们对这种IDE 有一定的了解,学习起来就不会觉得难。
返回到mainwindow.cpp 找到on_pushButton_clicked 这个槽函数里。在这个槽数里写上this->close();调用close()方法关闭整个程序。
编译及运行创建的UI 项目
直接按Ctrl + r 或者点击左下角的第一个绿色三角符号,编译且运行这个项目。运行的结果如下。点击“关闭程序”按钮,看看是不是整个程序关闭退出了呢?如果细心的同学还会思考,我们在上面用了两种方法连接信号与槽。那他们是如何连接的呢?他们两者同时连接会有影响吗?在哪里连接的呢?这些都是我们需要探讨的问题?难道第二种方法写上槽函数就会自动被触发?下一节我们继续学习Qt 信号与槽了解信号与槽的写法就能解开这个谜了!
Qt 信号与槽
在这章节里,我们学习Qt 的信号与槽,这里分一个章节来学习这个Qt 的信号与槽,可见这个信号与槽有多么重要。在学习Qt 的过程中,信号与槽是必不可少的部分,也是Qt 编程的基础,是Qt 编程的一大创新(其实与C#的事件很相似,编程都是类似的),Qt 的信号与槽在Qt4 时或者更早前已经出现,并不是属于哪个版本的。只是Qt4 与Qt5 的信号槽连接的写法有些区别。本教程对Qt4 的信号与槽连接写法不做讲解。
同时,由此章节开始,我们将不使用Qt Designer 的方式进行开发,也可以在新建项目时把*ui 文件不勾选。为什么不用Qt Designer 方式开发程序了呢?Qt Designer 方式开发程序简单,优点就是方便快捷,ui 文件的优点就是能比较直观快捷的看到整体的布局。
但是缺点也很明显!简单的一两个部件还是可以的,但是控件多了就不好管理,每次都要打开ui 文件添加新控件,不好用代码管理!读者也无从知道笔者是如进行界面布局的,且信号槽的连接方式是生成代码之后才能在setupUi 函数里才能看到,或者需要进入Ui 设计器里的信号槽模式里才能看到信号槽的连接,这样十分之不方便!没有代码那么直观!所以统一用代码绘界面,可以锻炼我们的布局能力,和代码逻辑能力!
Qt 信号与槽机制
信号与槽(Signal & Slot)是Qt 编程的基础,也是Qt 的一大创新。因为有了信号与槽的编程机制,在Qt 中处理界面各个组件的交互操作时变得更加直观和简单。
信号(Signal)就是在特定情况下被发射的事件,例如PushButton 最常见的信号就是鼠标单击时发射的clicked() 信号,一个ComboBox 最常见的信号是选择的列表项变化时发射的CurrentIndexChanged() 信号。
GUI 程序设计的主要内容就是对界面上各组件的信号的响应,只需要知道什么情况下发射哪些信号,合理地去响应和处理这些信号就可以了。
槽(Slot)就是对信号响应的函数。槽就是一个函数,与一般的C++函数是一样的,可以定义在类的任何部分(public、private 或protected),可以具有任何参数,也可以被直接调用。
槽函数与一般的函数不同的是:槽函数可以与一个信号关联,当信号被发射时,关联的槽函数被自动执行。
信号与槽关联是用QObject::connect() 函数实现的,其基本格式是:
QObject::connect(sender, SIGNAL(signal()), receiver, SLOT(slot()));
connect() 是QObject 类的一个静态函数,而QObject 是所有Qt 类的基类,在实际调用时可以忽略前面的限定符,所以可以直接写为:
connect(sender, SIGNAL(signal()), receiver, SLOT(slot()));
其中,sender 是发射信号的对象的名称,signal() 是信号名称。信号可以看做是特殊的函数,需要带括号,有参数时还需要指明参数。receiver 是接收信号的对象名称,slot() 是槽函数的名称,需要带括号,有参数时还需要指明参数。
SIGNAL 和SLOT 是Qt 的宏,用于指明信号和槽,并将它们的参数转换为相应的字符串。例如,在4.1 小节的项目02_designer_example 的ui_mainwindow.h 文件(ui_mainwindow.h文件是编译后产生的,位于build-02_designer_example-Desktop_Qt_5_12_9_GCC_64bit-Debug文件夹目录下)中,在setupUi() 函数中有如下的语句:
QObject::connect(pushButton, SIGNAL(clicked()), MainWindow, SLOT(close()));
其作用就是将pushButton 按钮的clicked() 信号与窗体(MainWindow)的槽函数close() 相关联,这样,当单击pushButton 按钮(就是界面上的“X”按钮)时,就会执行MainWindow的close() 槽函数。在这里我们就可以解决4.1.3 小节的中部分疑问了。我们使用第一种方法没看到信号与槽是怎么连接的,实际上它在UI 文件编译后在生成的代码ui_mainwindow.h 里!
关于信号与槽的使用,有以下一些规则需要注意:
一个信号可以连接多个槽,例如:
connect(pushButton, SIGNAL(clicked()), this, SLOT(hide());
connect(pushButton, SIGNAL(clicked()), this, SLOT(close());
这是当一个对象pushButton 的被单击时,所在窗体有两个槽进行响应,一个hide()用于隐藏主窗体,一个close 用于关闭主窗体。这里只是举个例子,实际上当执行close()后,hide()这个槽已经没有什么意义了,因为已经程序退出了,但是实际它有执行,因为它连接在close()的前面。当一个信号与多个槽函数关联时,槽函数按照建立连接时的顺序依次执行。
当信号和槽函数带有参数时,在connect()函数里,要写明参数的类型,但可以不写参数名称。
多个信号可以连接同一个槽,例如在02_designer_example 里,我们再拖2 个pushButton 到设计的窗体里。同时也让它按4.1 小节项目里的第一种方法连接信号与槽,把三个按钮的clicked()信号都连接到close()槽函数里。编译后可以在ui_mainwindow.h 这个文件里的setupUi()函数里,有如下的信号槽连接语句。
connect(pushButton,SIGNAL(clicked()),this,SLOT(close()));
connect(pushButton_2,SIGNAL(clicked()),this,SLOT(close()));
connect(pushButton_3,SIGNAL(clicked()),this,SLOT(close()));
这样,当任何一个pushButton 被单击时,都会执行close()函数,进而关闭或者退出程序。一个信号可以连接另外一个信号(说明了connect 万物皆可连,非常好用!),例如:
connect(pushButton, SIGNAL(objectNameChanged(QString)),this, SIGNAL(windowTitelChan
ged(QString)));
这样,当一个信号发射时,也会发射另外一个信号,实现某些特殊的功能。严格的情况下,信号与槽的参数个数和类型需要一致,至少信号的参数不能少于槽的参数。如果不匹配,会出现编译错误或运行错误。在使用信号与槽的类中,必须在类的定义中加入宏Q_OBJECT(特别重要)。
当一个信号被发射时,与其关联的槽函数通常被立即执行,就像正常调用一个函数一样。
只有当信号关联的所有槽函数执行完毕后,才会执行发射信号处后面的代码。
总结如下图,可以看到发送者与发送的信号是在一起的,接收者与接收的信号/槽是在一起的。它们不能在connect()方法里写乱顺序!由发送者发送出信号到接收者用信号/槽接收。
信号槽连接的方法已经讲解了。这里只是稍稍提一下,断开连接的方法,初学者基本用不到断开连接的操作。使用disconnect()。disconnect(),这个方法重载了好几个函数,解开格式如
下。
bool QObject::disconnect(const QObject *sender, const char *signal, const QObject *receiver, const
char *method)
断开一切与myObject 连接的信号或槽。
disconnect(myObject, 0, 0, 0);
相当于非静态重载函数:
myObject->disconnect();
断开所有连接到特定信号的东西。
disconnect(myObject, SIGNAL(mySignal()), 0, 0);
相当于非静态重载函数:
myObject->disconnect(SIGNAL(mySignal()));
与指定的接收者断开连接。
disconnect(myObject, 0, myReceiver, 0);
相当于非静态重载函数:
myObject->disconnect(myReceiver);
信号与槽机制是Qt GUI 编程的基础,使用信号与槽机制可以比较容易地将信号与响应代码关联起来。
如何在项目里创建信号
我们先新建一个项目,并把项目命名为03_signal_slot_example,如果还不会新建项目,请回到3.6 小节查看项目如何建立的。只是新建项目命名为03_signal_slot_example,同时取消勾选*ui 文件(为什么取消?可以在第五章章节开头处找理由),其他步骤不变。
由于信号只需声明,无需定义。所以我们只需要在mianwindow.h 里声明信号即可。代码如下,如下图黑色加粗部分代码就是创建的信号。这里创建一个void pushButtonTextChanged();信号,定义信号最好是贴合信号本身的含义。笔者定义这个信号的意思是按钮的文本发生改变后的signal。
1 # ifndef MAINWINDOW_H
2 # define MAINWINDOW_H
3
4 # include < QMainWindow >
5 /* 引入QPushButton */
6 # include < QPushButton >
7
8 class MainWindow : public QMainWindow
9 {
10 Q_OBJECT
11
12 public:
13 MainWindow( QWidget *parent = nullptr );
14 ~MainWindow();
15
16 signals:
17 /* 声明一个信号,只需声明,无需定义*/
18 void pushButtonTextChanged();
19
20
};
21 # endif /* MAINWINDOW_H */
第16 至18 行,声明一个信号void pushButtonTextChanged();,可以看到信号无需public 等关键字修饰。
如何在项目中创建槽
创建槽的方法也很简单,也是直接在mianwindow.h 里直接声明槽,在mianwindow.cpp 里实现槽的定义,声明槽必须写槽的定义(定义指函数体的实现),否则编译器编译时将会报错。
槽有以下特点:
- 槽可以是任何成员函数、普通全局函数、静态函数
- 槽函数和信号的参数和返回值要一致根据上面的槽特点,由于我们在5.2 小节里声明了信号void pushButtonTextChanged();
所以我们声明的槽函数必须是无返回值类型void,和无需参数。所以声明槽的代码如下。此外我们还声明一个QPushButton 对象pushButton。对象pushButton 可以写成简写btn。这个根据个人习惯即可!简写的名称建议不要让人看不懂即可!同时还声明一个按钮点击的槽。
1 # ifndef MAINWINDOW_H
2 # define MAINWINDOW_H
3
4 # include < QMainWindow >
5 /* 引入QPushButton */
6 # include < QPushButton >
7
8 class MainWindow : public QMainWindow
9 {
10 Q_OBJECT
11
12 public:
13 MainWindow( QWidget *parent = nullptr );
14 ~MainWindow();
15
16
17 signals:
18 /* 声明一个信号,只需声明,无需定义*/
19 void pushButtonTextChanged();
20
21 public slots:
22 /* 声明一个槽函数*/
23 void changeButtonText();
24
25 /* 声明按钮点击的槽函数*/
26 void pushButtonClicked();
27
28 private:
28 /* 声明一个对象pushButton */
30 QPushButton * pushButton;
31
};
32 # endif /* MAINWINDOW_H */
第23 行,声明一个槽函数void changeButtonText();。
第26 行,声明了一个槽函数void pushButtonClicked();。
第31 行,声明了一个QPushButton 对象pushButton。
在mainwindow.cpp 里实现声明的槽函数void changeButtonText();和void
pushButtonClicked();。同时还实例化了pushButton 对象。代码如下。
1 # include "mainwindow.h"
2
3 MainWindow::MainWindow( QWidget *parent )
4 : QMainWindow( parent )
5
{
6 /* 设置窗体的宽为800,高为480 */
7 this->resize( 800, 480 );
8
9 /* 实例化pushButton对象*/
10 pushButton = new QPushButton( this );
11
12 /* 调用setText()方法设定按钮的文本*/
13 pushButton->setText( "我是一个按钮" );
14
}
15
16 MainWindow::~MainWindow()
17
{
18
19
}
20
21 /* 实现按钮点击槽函数*/
22 void MainWindow::pushButtonClicked()
23
{
24 /* 使用emit发送信号*/
25 emit pushButtonTextChanged();
26
}
27
28 /* 实现按钮文本改变的槽函数*/
29 void MainWindow::changeButtonText()
30
{
31 /* 在槽函数里改变按钮的文本*/
32 pushButton->setText( "被点击了!" );
33
}
第7 行,设置程序窗体的大小,假若不设置,窗体为最小分辨率。这里设置宽为800,高为480。刚好是正点原子RGB LCD 屏里其中一个分辨率。实际开发需要以硬件的实际分辨率开发即可。本教程都是以800*480 分辨率开发。不建议使用低于这个分辨率开发,分辨率太低,显示的内容过少。
第10 行,实例化pushButton 对象,在堆中实例化,并指定父对象为this。因为我们在mainwindow.h 里声明的是指针类型的对象。实际上QPushButton(this)的原型是QPushButton(QWidget *parent = nullptr)。在C++基础里第2.2.3 小节里就已经学过重载,实际上QPushButton 类中还有下面两种初始化的方法。这也是C++在Qt 里重载的体现。
QPushButton(const QString &text, QWidget *parent = nullptr)
QPushButton(const QIcon &icon, const QString &text, QWidget *parent = nullptr)
在22 行和29 行里,实现了槽方法。
如何在项目中连接信号与槽
5.2 和5.3 小节只是声明了信号和定义槽,要在项目中连接信号与槽。完成连接的代码如下。
信号槽连接的代码如下。
connect(pushButton, SIGNAL(clicked()), this, SLOT(pushButtonClicked()));
connect(this, SIGNAL(pushButtonTextChanged()), this, SLOT(changeButtonText()));
注意,发送信号的对象,和接收的信号的对象。因为我们pushButtonClicked()是本类里定义的槽,所以用this 来接收。同理,pushButtonTextChanged()也是本类定义的信号。所以发送者写成this。changeButtonText()也是本类的槽函数,所以接收槽的对象也是this。很多初学者不了解这里的信号的发送者和槽的接收者,究竟该写谁。我们要细读上面的代码和5.1 小节的信号槽机制的定义慢慢理解。
在mainwindow.cpp 中信号槽连接的代码如下。
1 #include "mainwindow.h"
2
3 MainWindow::MainWindow(QWidget *parent)
4 : QMainWindow(parent)
5 {
6 /* 设置窗体的宽为800,高为480 */
7 this->resize(800,480);
8
9 /* 实例化pushButton对象*/
10 pushButton = new QPushButton(this);
11
12 /* 调用setText()方法设定按钮的文本*/
13 pushButton->setText("我是一个按钮");
14
15 /* 信号与槽连接*/
16 connect(pushButton, SIGNAL(clicked()), this,
SLOT(pushButtonClicked()));
17 connect(this, SIGNAL(pushButtonTextChanged()), this, SLOT(changeButtonText()));
18 }
19
20 MainWindow::~MainWindow()
21 {
22
23 }
24
25 /* 实现按钮点击槽函数*/
26 void MainWindow::pushButtonClicked()
27 {
28 /* 使用emit发送信号*/
29 emit pushButtonTextChanged();
30 }
31
32 /* 实现按钮文本改变的槽函数*/
33 void MainWindow::changeButtonText()
34 {
35 /* 在槽函数里改变按钮的文本*/
36 pushButton->setText("被点击了!");
37 }
第16、17 行,连接信号与槽,整个流程就是当点击了按钮,然后触发了pushButtonClicked(),pushButtonClicked()槽里发送pushButtonTextChanged()信号,changeButtonText()槽响应pushButtonTextChanged()信号,我们在changeButtonText()槽实现
响应的动作(事件)。最终的实现效果是按钮的文本由“我是一个按钮”被点击时变成“被点击了!”。
编译程序及运行后,未点击按钮前如下。
点击按钮后。
上面的程序虽然简单,但是我们也最终实现了自定义的信号与槽。
学会使用Qt 类的信号与槽
Qt 里有大量的信号与槽,都是Qt 自定义好的。基本够我们使用,如果没有找到想要的信号与槽,我们就可以按5.2~5.4 小节自定义信号与槽的方法去定义自己的信号和槽了。那么我们该如何使用Qt 信号与槽呢?
要想使用Qt 的信号与槽,那么我们必须知道有哪些信号与槽。在5.4 小节的代码里。
connect(pushButton, SIGNAL(clicked()), this, SLOT(pushButtonClicked()));
如下图示,按住Ctrl 键,再点击clicked(),进入clicked()这个信号的定义处。
进入QPushButton 的定义处,我们看到QPushButton 不止clicked 信号,还有其他信号,也有QPushButton 的槽函数(返回上一步按Alt + 方向左键)。在这里我们只是简单的看了如何在已知信号和槽里查找其他信号与槽。实际上在开发中我们经常需要使用Qt 帮助文档来查看Qt定义的信号与槽。我们将在下一章里讲解Qt 帮助文档的使用,帮助文档里面就有如何查看Qt的信号与槽等等。
Qt Creator 的使用技巧
在任何一款编程的IDE 软件里,都有相应的编程技巧。在这章里我们主要这两种最常用的技巧——Qt Creator 的快捷键的使用和Qt 的帮助文档的使用。其中最重要的就是Qt 的帮助文档里。可以说Qt 帮助文档其实就是一本教程,任何其他的Qt 教程都没有Qt 文档写的详细,只不过是英文版本的而已。这样的对我们初学者来说入门还是有些吃力的。下面由笔者和大家的学习下Qt Creator 的快捷键和Qt 的帮助文档的使用方法。
6.1 Qt Creator 的快捷键
6.2 Qt 帮助文档的使用
Qt Creator 的快捷键
在Qt Creator 里,假若自己不知道某些功能按钮的快捷键是什么,可以将鼠标移至该按钮上面就可以知道它的快捷键了。如下图,想知道运行的快捷键是什么,那么我们将鼠标移至QtCreator 的左下角的运行的按钮上,此时会提示运行快捷键是“Ctrl + R”。当然需要在英文输入法的前提下才能生效。
Qt 快捷键有很多,我们不可能能记住那么多快捷键。我们只需要使用常用的快捷键即可。
像简单的Ctrl + C 和Ctrl + V 复制粘贴等这些我们就不说了,这些都与Windows 下的操作一样。
常用的快捷键有如下表格。
上面的快捷键可能会与Ubuntu 或者Windows 系统的快捷键冲突,或者是有些功能没有定义快捷键,我们也可以在Qt Creator 里自定义快捷键或者修改原有的快捷键,步骤如下。
在“环境”项下找到键盘,如图,⑤处,切换书签的快捷键,“Ctrl + M”显示红色,说明
是与系统的快捷键冲突了。我们可以选中这项,按⑥处“Record”重新记录切换书签的快捷键。
再点击“Apply”和“OK”即可。有些命令还没有定义快捷键,如果我们希望使用这个命令使用快捷键,那么我们也可以按如下步骤记录自定义我们的快捷键即可,注意不要与系统的快捷键重复。
以上Qt Creator 的快捷键仅供参考,在编程里实际上常用的也不会很多,只需要使用常用的即可!
Qt 帮助文档的使用
在任何一款IDE 编程软件里,都离不开参考帮助文档。有些IDE 编程软件需要手动下载这些帮助文档。Qt Creator 则不需要,在我们安装Qt 时,帮助文档已经安装在我们的“安装目录/Qt5.12.9/Docs/”下,使用的是html 文本的方式,我们可以使用浏览器打开这种html 文本。
在Ubuntu 下的安装目录下的帮助文档如下图,下图是QtCore(Qt 的核心模块)下的帮助文档。
我们也可以在Qt Creator 下直接搜索相应的关键字。如下图步骤,比如我们要查看QPushButton 类,先点击①处的帮助再点击②处的下拉选为索引,在③处输入“QPushButton”,也可以只需要输入“QPush”匹配索引值即可。点击匹配的选项,如下图⑤处,就是我们探索出来的帮助文档。
如何使用搜索出来的帮助文档呢?比如要查看QPushButton 类有哪些属性和函数可以用可以查看“Properties”和“Public Functions”这个标题下的属性和函数,如下图。可以看到QPushButton 类有如下属性和函数可以使用。
再比如,假若我们不知道这个QPushButton 类有哪些可用的槽,我们可以继续往下查看,找到Public Slots 即可!我们查看某个方法用法时,直接单击进入查看某个方法的用法即可!别忘记使用上一小节的快捷键(Alt + ←(方向左键))返回到上一级,或者进入下一级(Alt + →(方向右键))了。
Qt 的帮助文档使用方法流程基本如上了。初学者可以阅读这些的英文文档会感到吃力,可能会问,有没有中文的帮助文档?我们要知道这种都是国外软件,因为帮助文档可能会更新变动,且翻译起来要人力和时间,况且翻译的原意有可能是不贴近原来的Qt 帮助文档。我们还是老老实实看Qt 最权威最全面的帮助文档即可!要想学好Qt,帮助文档是少看不了的!其实Qt
的帮助文档就是一本十分庞大且非常好的教程了,我们这个教程只是引领大家入门Qt,引领大家在Qt 这个非常庞大的文档里学习常用的控件和方法。Qt 自带有非常多的例子可以参考,这点Qt 是做的很好的。看帮助文档,我们还需要多多练习,并不是看帮助文档就会了,实践起来才知道,基础都是靠多练的。