创建第一个Qt应用
初次接触Qt,安装之后发现他的官方文档内容详实,条理清晰,并且集成在开发工具Qt Creator之中,阅读非常方便。因此决定学习初期直接参阅官方文档,而这个系列的文章则是学习过程的一个记录。
1.1 Qt概述
Qt是一个同时支持桌面,嵌入式,移动端的跨平台应用程序开发框架,在Linux,Windows,OS X,iOS,Android等操作系统上都可以运行。
Qt本身并不是一个程序设计语言,而是通过C++编写的开发框架。在使用该框架进行开发的时候,我们采用支持Qt扩展的C++语法来编写源代码,这些代码经过Qt预编译器(MOC)处理之后变为标准的C++代码。此后应用程序本身及在此之上开发的其他程序都可以任何通过标准C++编译器(GCC,MSVC,MinGW等)完成编译。
除了必要的程序库以外,Qt还提供了很多开发工具,比如构建系统qmake和Qbs,集成开发环境Qt Creator等等。
1.2 环境搭建
Qt的安装方式分为在线安装,离线安装,源代码编译安装三种。其离线和在线安装包都可以在官网下载,安装过程中需要登录Qt账号来检测授权允许的模块,操作非常简单,这里略去。
1.3 基于Qt Widgets创建第一个Qt应用程序
可以通过两种方式来创建Qt应用程序,Qt Widgets用于创建经典桌面风格的GUI,而Qt Quick则为QML语言提供了类型和函数库,用于快速开发具有现代风格的GUI。
因为是初次接触,我计划从Qt Widgets通过C++开发的方式开始入门,后期再进一步学习Qt Quick开发方式。
首先是创建项目,打开Qt Creator,点击文件菜单的新建文件或项目菜单项(或者ctrl+N快捷键)会弹出相应的选项卡,在其中选择Application ---- Qt Widgets Application选项,然后点击Choose完成模板选择,操作如下图:
模板创建之后要输入项目名称和项目路径:
接下来选择已经安装的开发包,这个根据项目的目标平台自行选择即可:
接下来是创建类,输入类名并选择想要继承的基类,其他信息保持默认。此处我们创建TextFinder类并另其继承自QWidget,事实上QWidget类是一切用户接口对象的基类,QMainWindow和QDialog也都派生自QWidget。一般来说在创建对话框时我们使用QDialog;在创建主窗体时使用QMainWindow,基于QMainWindow创建的窗口具有标题栏,菜单栏和状态栏;在比较模糊的情况下我们使用通用的QWidget。当然这些规定并不是必须的。
这些都做完以后,就进入到信息汇总界面,如果确认项目信息无误,即可完成创建:
点击完成之后会直接进入项目的源代码编辑模式:
这个模式和其他的开发工具是一样的,我们可以看到开发工具为我们生成的项目目录并且编辑源码。也可以在最左面的工具栏中点击相应的图标切换开发模式,其中设计模式的图标起初是不可用的,我们可以在项目的Forms目录中双击textfinder.ui文件进入设计模式,在设计模式下可以通过拖动的方式设计界面并完成布局。
该应用程序的目标是将一个文本文件的内容展示在文本框内,然后根据用户输入的关键字在编辑框内查找该关键词是否存在。
在设计模式中,我们从左面的控件列表中拖动Label,Line Edit,PushButton,Text Edit四个控件到窗体中,然后给标签,单行文本编辑框,按钮三个控件添加水平布局(Horizontal Layout),接下来再给整个窗体应用垂直布局(Vertical Layout),这样整个应用程序的布局就设计完成了,效果如下图所示:
然后双击标签和按钮控件分别将他们的text属性修改为Keyword和Find,这个属性决定了控件上显示的文本,再修改按钮控件的objectName属性为findButton,这个属性的值就是程序中所创建按钮对象的名字。
为了实现打开文件的功能,现在给项目添加一个资源文件,方法和创建项目类似,点击文件菜单的创建文件或项目菜单项,如下图:
之后为该资源文件添加前缀:/,添加文件:input.txt,注意在添加文件之前先在项目根目录创建该文件。
这些做完了以后就要开始编辑代码了,为了实现在单击按钮的同时查找关键词就必须响应按钮的单击事件,在Qt中通过信号和槽机制来完成这一过程,信号在特定事件产生时发出,而槽函数则用来接受信号并响应。槽函数和普通的成员函数一样,可以被定义为private,protected,public三种访问级别的任何一种,可以具有参数也可以被直接调用,区别仅在于槽函数和信号关联后在信号发射时会自动被调用。Qt Creator中已经为我们预定义了常用的信号和槽,我们只需要通过图形化操作添加并关联信号和槽函数即可直接使用。
右键窗口上的按钮控件,在弹出菜单中点击转到槽,选择clicked信号后确定便会自动在程序中添加clicked信号的槽函数TextFinder::on_findButton_clicked。
这一过程应该在程序中添加三处代码,第一部分是窗体类头文件中添加槽函数声明:
private slots:
void on_findButton_clicked();//声明一个私有的无参槽函数
第二是在窗体类实现中添加槽函数定义:
void TextFinder::on_findButton_clicked()
{
//内部编写响应findButton按钮单击事件的代码
}
第三是让信号和槽关联的代码,这部分代码会由uic生成在ui_textfinder.h中:
QMetaObject::connectSlotsByName(TextFinder);//关联信号和槽
槽函数的具体定义稍后再写,我们先把程序的框架补充完整,现在程序已经可以正常创建用户界面,响应单击事件,还欠缺的功能是读取文本文件。下面给主窗口类添加loadTextFile成员函数:
首先是函数声明:
private:
void loadTextFile();
添加定义:
void textFinder::loadTextFile()
{
}
到此为止,这个应用程序的框架就彻底完成了,下面我们完整的分析一下源代码:
首先看一下程序的main函数,它位于main.cpp,是Qt程序的入口函数:
#include "textfinder.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);//创建应用程序
TextFinder w;//创建窗口
w.show();//显示窗口
return a.exec();//开始程序,进入消息循环
}
然后观察头文件textfinder.h,含义通过注释标注在代码中:
//保护性声明,防止重复包含
#ifndef TEXTFINDER_H
#define TEXTFINDER_H
//QWidget是窗体基类,于QWidtget中声明,Qt中头文件命名具有很强的规律性
#include <QWidget>
//Ui命名空间中声明了一个和窗口类同名的界面类,其实现由uic自动生成,和界面布局有关的冗长的代码都位于该类的成员函数setupUi中
namespace Ui {
class TextFinder;
}
//窗口类,继承自QWidget
class TextFinder : public QWidget
{
//Q_OBJECT宏用于引入信号槽机制,建议在定义槽函数的类声明中加入
Q_OBJECT
public:
//构造函数,explicit用于禁止单参数构造函数的默认隐式转换,单参数的构造函数加上explicit声明通常是没有坏处的
explicit TextFinder(QWidget *parent = nullptr);
//析构函数
~TextFinder();
//槽函数声明
private slots:
void on_findButton_clicked();
//私有成员
private:
//指向界面类的指针,在构造函数中初始化
Ui::TextFinder *ui;
//成员函数,用于加载文件内容,在构造函数中调用
void loadTextFile();
};
#endif // TEXTFINDER_H
最后阅读源文件textfinder.cpp,窗口类的实现位于该文件中:
#include "textfinder.h"
#include "ui_textfinder.h"
#include "QFile"
#include "QTextStream"
//窗口类构造函数定义,初始话列表中实例化了界面类,并另ui指针指向该对象
TextFinder::TextFinder(QWidget *parent) :
QWidget(parent),
ui(new Ui::TextFinder)
{
ui->setupUi(this);//调用界面类的setupUi方法,建立组件和布局
loadTextFile();//加载文件到文本框
}
TextFinder::~TextFinder()
{
delete ui;//释放界面类实例
}
void TextFinder::on_findButton_clicked()
{
//取得单行文本编辑框内的用户输入关键字
QString searchString = ui->lineEdit->text();
//在文本框中查找该关键字
ui->textEdit->find(searchString, QTextDocument::FindWholeWords);
}
void TextFinder::loadTextFile()
{
//创建文件对象
QFile file(":/input.txt");
//打开文件
file.open(QIODevice::ReadOnly);
//读入文件
QTextStream in(&file);
QString line = in.readAll();
//关闭文件
file.close();
//文本框中显示文件内容
ui->textEdit->setPlainText(line);
//获取鼠标指针
QTextCursor cursor = ui->textEdit->textCursor();
//移动指针到文本框开头
cursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor, 1);
}
在Ui命名空间中单独定义界面类的方法很好的实现了界面和业务逻辑的分离,日后应用程序界面布局可以很方便的随时修改,并重新通过uic生成代码,而这个过程并不会对程序业务逻辑造成影响。上面的应用程序中用到了一些没有接触过的类和方法,这些东西留待以后再叙。