求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
要资料
 
追随技术信仰

随时听讲座
每天看新闻
 
 
Qt 编程指南
第1章 Qt 开发环境
1.1 Qt 介绍
1.2 Qt 下载
1.3 Qt 在 Windows 下安装
1.4 Qt 在 Linux 下安装
1.5 认识开发工具
1.6 常见的名词术语
第2章 从Hello World开始
2.1 Hello World
2.2 Hello Qt
2.3 Hello Designer
2.4 Hello Creator
2.5 Qt程序调试
2.6 Qt帮助文档
第3章 字符串和字符编码
3.1 字符编码方式
3.2 Qt 程序字符编码
3.3 使用 QString
3.4 使用 QByteArray
第4章 信号和槽函数
4.1 元对象系统
4.2 使用原有的信号和槽
4.3 自定义信号和槽
4.4 系统属性
4.5 扩展阅读:ui_*.h代码
4.6 扩展阅读:moc_*.cpp代码
第5章 简单控件的使用
5.1 按钮类的控件
5.2 单行编辑控件
5.3 丰富文本编辑控件
 

 
目录
单行编辑控件
7 次浏览
1次  

在图形界面程序中,有很多的输入控件,其中以文本编辑控件最为常用,可以接收用户输入的各种文本,比如登录界面常见的用户名、密码,网络连接使用的 IP 和 端口等等,本节先大致介绍一下 Qt 里面的文本编辑和浏览控件,然后详细讲解单行编辑控件的用途,通过三个例子示范单行编辑控件的使用。对于多行丰富内容的文本编辑控件,留在下一节讲解。

1. 文本编辑控件概述

从 Qt 设计师界面可以看到常用的 Qt 文本编辑和浏览控件,包括四个:

edit

其中单行编辑控件 QLineEdit 和 普通文本编辑控件 QPlainTextEdit 都是针对最普通的 C++ 字符串编辑和显示,默认都是白底黑字,没有彩色字体。QLineEdit 按照名字,就是只接受单行普通文本输入,QPlainTextEdit 可以接收多行普通文本输入。

丰富文本编辑控件 QTextEdit 是升级版的编辑控件,支持 HTML 网页的丰富文本编辑,当然也可以利用它编辑普通文本。丰富文本浏览控件 QTextBrowser 是 QTextEdit 的只读版本,并能打开网页链接。

本节主要介绍 QLineEdit,下一节主要介绍 QTextEdit 和 QTextBrowser,学习这些内容之后,使用 QPlainTextEdit 就没什么技术难度了,查看 Qt 助手文档可以轻松学会,所以不专门讲解 QPlainTextEdit 了。

2. QLineEdit 类

在 Qt 助手索引里输入类名,就可以找到相应的帮助文档。QLineEdit 就如名字一样,接收一行文本输入,编辑器一般都有对文本的复制、粘贴、剪切、撤销、重做等功能,单行编辑控件原生自带这些功能,右击单行编辑控件或者使用 Ctrl+C、Ctrl+V、Ctrl+X 等快捷键都可以使用这些默认功能。

首先看看 QLineEdit 构造函数:

QLineEdit(QWidget * parent = 0)
QLineEdit(const QString & contents, QWidget * parent = 0)

parent 是父窗口指针,第二个构造函数的 contents 是初始化显示的文本。

单行编辑控件最重要的属性就是 text,获取或者修改文本是单行编辑控件最重要的功能。

获取文本的函数:

QString text() const 设置文本的函数:

void setText(const QString &) 默认情况下,单行编辑控件的文本长度限制为 32767,获取单行编辑控件的文本长度限定的函数为:

int maxLength() const 如果希望修改文本长度限定,可以通过函数:

void setMaxLength(int)

无论是用户从图形界面编辑文本,还是程序内部用代码修改文本,都会触发如下信号:

void textChanged(const QString & text) 关联这个信号,就可以实时跟踪文本的所有变化。

我们之前 4.2.1 节例子用过单行编辑控件和标签控件,使它们的文本同步显示,我们关联的是另一个信号:

void textEdited(const QString & text)

这个信号只根据用户在图形界面的编辑行为触发,如果程序代码里通过函数 setText() ,那么只会触发之前的 textChanged() 信号,不会触发文本编辑信号 textEdited()。所以如果希望追踪文本的所有变化,需要关联 textChanged() 信号,如果只希望跟踪用户在图形界面的编辑更改,那就关联 textEdited() 信号。

当用户从图形界面编辑文本的行为结束时,比如在单行编辑控件里按了回车键或者该控件失去输入焦点(用户转到其他控件操作),单行编辑控件会发出编辑完成信号:

void editingFinished()

另外,单行编辑控件既可以用上面 text() 函数获取全部的文本,也可以选取用户高亮选中的部分文本,通过函数:

QString selectedText() const

const 因为单行编辑控件文本相对简单,本来文本就不多,所以获取高亮的部分文本情况也比较少。

刚才提到单行编辑控件可以进行复制、粘贴、剪切、撤销、重做等操作,每个操作都有对应的槽函数实现,不过通过代码直接调用这些槽函数情况比较少,就不会一一列举 了,可以通过 Qt 助手查找相应文档。基本情况就介绍这么多,下面通过例子示范单行编辑控件的使用。

3.登录框示例

登录框主要就是接收用户输入的用户名和密码,在用户点击“登录”按钮之后,将用户名和密码的 Hash 值与软件配置文件或数据库里的值进行比较,然后决定是否允 许登录。

密码框通常是以一排 * 显示的,单行编辑控件可以通过设置属性 echoMode 来显示星号密码。属性 echoMode 是 EchoMode 枚举类型, 主要有四种显示模式:

① QLineEdit::Normal,普通模式,用户输入什么显示什么,这是默认的显示模式。

② QLineEdit::NoEcho,不显示任何东西,这是 Unix/Linux 常用的密码显示模式,用户敲密码时不显示任何文本,这样能隐藏密码的长 度,不被人从屏幕偷窥。

③ QLineEdit::Password,每一个密码字符都用星号显示,这是 Windows 常用的密码显示模式。

④ QLineEdit::PasswordEchoOnEdit,当输入一个密码字符时,短暂显示该字符,然后迅速将该字符显示为星号,方便提示用户当前输入了什么字符,类 似 Android 解锁密码的输入方式。

通过单行编辑控件的函数:

void setEchoMode(EchoMode)

可以设置密码显示模式,一般用 QLineEdit::Password 就可以了。

顺便说一下关于密码如何保存的问题,现在互联网上各种黑客、广告商盛行,所以密码是绝对不能明文存储的,一般都是将密码文本做 Hash 转换,存储 Hash 散列值作为密码比较的依据,这样避免用户明文密码泄漏。用户如果需要修改密码,那么直接换个 Hash 值就行了。

Qt 自带有计算密码学 Hash 值的类 QCryptographicHash,支持多种多样的散列 Hash 算法,这个类有一个静态函数可以快速计算各种算法的散列值:

QByteArray QCryptographicHash::​hash(const QByteArray & data, Algorithm method)

参数 data 就是输入的明文密码,method 是密码学 Hash 算法枚举,返回值就是求得的 Hash 值,用 QByteArray 存储返回值。Qt 支持所有主流的 Hash 算法,算法枚举很多,具体可以查 QCryptographicHash 类的文档,例子中我们使用 QCryptographicHash::Sha3_256 算法,下面开始本小节的例子。

打开 QtCreator,新建一个 Qt Widgets Application 项目,在新建项目的向导里填写:

①项目名称 login,创建路径 D:\QtProjects\ch05,点击下一步;

②套件选择里面选择全部套件,点击下一步;

③基类选择 QWidget,点击下一步;

④项目管理不修改,点击完成。

建好项目之后,打开窗体 widget.ui 文件,进入设计模式,按照下图拖入控件:

edit

界面里有两个标签控件、两个单行编辑控件、两个按压按钮。标签控件的文本如图上显示的:“用户名”、“密码”,标签高度调整为 20,这样与单行编辑控件的高度一样,方便与单行编辑控件对齐。

两个单行编辑控件,上面的 objectName 设为 lineEditUser,下面的 objectName 设为 lineEditPassword,并尽量与两个标签控件对齐。

对于下面两个按钮,左边的文本为“登录”,objectName 设为 pushButtonLogin,右边按钮文本为“退出”,objectName 设为 pushButtonExit。调整控件的位置,尽量看起来对齐。

例子的效果就是点击“登录”按钮时,获取用户名,计算密码的 Hash 值并弹窗显示出来。点击“退出”按钮时,窗口自动关闭。

在图形界面右击两个按钮,在右键菜单选择“转到槽...”,然后为按钮的 clicked() 信号添加槽函数:

edit

为两个按钮添加好槽函数之后,保存界面文件,然后回到代码编辑模式,打开头文件 widget.h,添加成员变量:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
Q_OBJECT

public:
explicit Widget(QWidget *parent = 0);
~Widget();

private slots:
void on_pushButtonLogin_clicked();

void on_pushButtonExit_clicked();

private:
Ui::Widget *ui;
//用户名字符串
QString m_strUser;
//不能明文保存密码,存储密码 hash 值
QByteArray m_passwordHash;
};

#endif // WIDGET_H

两个槽函数是刚才从图形界面添加的,这里新增了两个成员变量 m_strUser 保存用户名,m_passwordHash 保存密码的 Hash 值。头文件内容就这些,下面来编辑 widget.cpp ,首先添加头文件包含和构造函数里的代码:

#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QMessageBox>
#include <QCryptographicHash>

Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);

//设置密码框的显示模式
ui->lineEditPassword->setEchoMode(QLineEdit::Password);
}

Widget::~Widget()
{
delete ui;
}

头文件 <QCryptographicHash> 就是专门计算 Hash 值的类。在构造函数里,使用 lineEditPassword 的 setEchoMode() 函数,参数为 QLineEdit::Password ,这样就能轻松将该单行编辑控件变成真正的密码框了。

接下来是“登录”按钮的槽函数编写:

//登录按钮
void Widget::on_pushButtonLogin_clicked()
{
//判断用户名密码是否为空
if( ui->lineEditUser->text().isEmpty()
|| ui->lineEditPassword->text().isEmpty() )
{
QMessageBox::warning(this, tr("警告信息"), tr("用户名或密码为空,不能登录。"));
return;
}
//用户名
m_strUser = ui->lineEditUser->text();
//计算密码 Hash
m_passwordHash = QCryptographicHash::hash( ui->lineEditPassword->text().toUtf8(),
QCryptographicHash::Sha3_256 );
//构造消息
//添加用户名
QString strMsg = tr("用户名:") + m_strUser + tr("\r\n") + tr("密码 Hash:");
//把每个 Hash 字节转成一对十六进制字符显示
// 256 bit 对应 32 字节,变成 64 个十六进制字符打印
strMsg += m_passwordHash.toHex();

//打印消息
qDebug()<<strMsg;
//弹窗显示,注意:实际应用中会将用户名和密码 Hash 与数据库或配置文件里的做比较,而不是弹窗
QMessageBox::information(this, tr("用户信息"), strMsg);

}

在 on_pushButtonLogin_clicked() 槽函数开始的地方,先对用户名和密码的字符串进行判断,如果字符串是空的,说明没有输入用户名和密码,那就直接提示警告信息,而不做其他操作。 QMessageBox::warning() 静态函数与 QMessageBox::information() 静态函数其实是差不多的,只是一个用于显示 警告信息,另一个用于显示普通信息。

如果两个字符串都不是空的,那么继续后续代码,用单行编辑控件的 text() 函数提取用户名保存到 m_strUser 变量里面。

因为不能直接存储密码字符串,所以将密码字符串用 QCryptographicHash::hash() 计算 Hash 值存到 m_passwordHash 里面。 QCryptographicHash::hash() 第一个参数是 QByteArray 类型,所以需要将 QString 对象转换成 UTF-8 编码的 QByteArray 对象,利用 toUtf8() 函数即可,第二个参数是 Hash 算法类型,这里用的是 QCryptographicHash::Sha3_256 。该函数会将 QByteArray 对象数据全部计算,得到固定 256 bit (32 字节)的 Hash 值,这个 Hash 是二进制数据流,包含大量不可打印字符。

接下来我们构造要显示的消息 strMsg ,第一行是 "用户名:" 和用户名字符串,第二行是 "密码 Hash:" 和 Hash 值的十六进制字符串,因为 Hash 值是二进制数据,包含不可打印字符,因此使用 QByteArray 类的 toHex() 函数将每个字节转换成两个十六进制数的字符,比如字节数值 0x7f ,就转成 "7f" 两个字符,然后将这个十六进制字符串添加到 strMsg 。

最后是用 qDebug() 打印 strMsg ,并弹窗显示 strMsg 。实际应用中并不会弹窗显示用户名和密码 Hash,一般是将用户名与密码 Hash 值与数据库中存储的或配置文件中保存的值进行比较,这里因为还没涉及到数据库和配置文件,所以用弹窗作为示范。

剩下第二个“退出”按钮的槽函数内容比较简单,如下所示:

//退出按钮
void Widget::on_pushButtonExit_clicked()
{
this->close();
}

调用窗体的 close() 函数,关闭窗体,因为程序只有一个窗体,关闭之后程序自动退出。

例子代码就这么多,程序运行效果如下图所示:

edit

当用户名和密码都是 user 时,就会显示图上的 Hash 值的字符串,strMsg 只有两行文本,而 QMessageBox::information() 显示的却是三行,那是因为 Hash 值的字符串实在太宽了,所以消息框自动把 Hash 值的字符串放到第三行显示了。

如果用户名密码有一个为空,那么点击“登录”按钮会出现警告消息框:

edit

警告消息框里面的图标与普通信息消息框不一样,其他的功能都是差不多的。另外还有提示严重错误的消息框,函数为 QMessageBox::​critical() ,就是图标不一样,其他的功能和普通消息框差不多。

4 数据验证器和伙伴快捷键

在用户输入时,可能用到一个功能就是数据验证,限制用户输入非法的取值。比如限定 IPv4 的取值为 0.0.0.0 到 255.255.255.255 ,端口取值范围 0 到 65535,而网卡 MAC 地址限定为 48 bit 数值对应的十六进制字符串,比如 AA:BB:CC:DD:EE:FF 。

Qt 针对单行编辑控件,提供三种方式来使用数据验证器:

(1)单行编辑控件自带的输入模板 inputMask:

通过函数设置输入模板,这个输入模板字符串是 QLineEdit 自定义的,应用范围比较局限,功能也相对简单,设置函数为:

void setInputMask(const QString & inputMask)

具体的 inputMask 字符串格式可以查询 QLineEdit 的文档,我们举 MAC 地址的例子,"H" 表示所有的十六进制字符,包括大小写的十六进制字符,而且 "H" 占位的字符不能省略。小写的 "h" 也代表所有十六进制字符,但 "h" 占位是可以省略的字符.

对于 MAC 地址,输入模板为 "HH:HH:HH:HH:HH:HH" 。

(2)整型数值和浮点数值验证器

针对整数数值,可以用 QIntValidator 类作为验证器,该类常用构造函数为:

QIntValidator(int minimum, int maximum, QObject * parent = 0)

parent 是父对象指针,minimum 是整数下限,maximum 是整数上限,允许的数值是包含两个边界值的,边界之外的数值都不允许输入。QIntValidator 类还有一个 用于修改上下限的函数:

void QIntValidator::​setRange(int bottom, int top)

bottom 是下限数值,top 是上限数值。

一般用 new 新建一个整数验证器之后,就可以把验证器设置给单行编辑控件:

void QLineEdit::​setValidator(const QValidator * v)

针对浮点数校验,由 QDoubleValidator 类实现,它常用的构造函数为:

QDoubleValidator(double bottom, double top, int decimals, QObject * parent = 0)

bottom 是双精度浮点数下限,top 是上限,decimals 是指小数点后的数字位数限定(精度),parent 是父对象指针。修改 浮点数验证器上 下限和精 度的函数为:

virtual void setRange(double minimum, double maximum, int decimals = 0)

minimum 是下限,maximum 是上限,decimals 是小数点后精度位数。

新建浮点数验证器之后,也是通过 QLineEdit::​setValidator() 函数设置给单行编辑控件。

设置好单行编辑控件的数据验证器之后,在用户输入数据时,单行编辑控件自动按照验证器要求,只允许用户输入合法的数据,自动限制不合法的输入。

(3)正则表达式验证器

正则表达式是最为强大的数据验证和数据筛选武器,正则表达式作为大杀器,几乎无所不能。关于正则表达式的内容有专门的书籍介绍,这里没法介绍这个大杀器。各种编程 语言一般都有支持正则表达式的类库,Qt 提供 QRegExp 类支持正则表达式,正则表达式的验证器类为 QRegExpValidator。一般是先通过字符串构建一个正则表达式:

QRegExp(const QString & pattern, Qt::CaseSensitivity cs = Qt::CaseSensitive, PatternSyntax syntax = RegExp)

pattern 是正则表达式字符串,cs 指是否大小写敏感,默认是敏感的,syntax 是语法格式,用默认的 RegExp,这是类似 Perl 语言风格的正则表达式。一般可以搜索 IPv4 格式的 Perl 或其他语言的正则表达式,拿过来用即可。

然后根据 QRegExp 构建一个正则表达式验证器:

QRegExpValidator(const QRegExp & rx, QObject * parent = 0)

最后将 QRegExpValidator 对象通过函数 QLineEdit::​setValidator() 函数设置给单行编辑控件就行了。

网上查找关于 IPv4 格式的正则表达式为:

"^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}"
"(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"

上面正则表达式原本是一行,实在太长,拆为两行来写了。C++ 可以自动拼接字符串,因此可以按上面两行来写。

注意其他脚本语言里面 "\" 就是反斜杠,不是转义字符,我们把这个正则表达式变成 C++ 代码中的字符串时,原本的反斜杠字符要用 "\\" 来替换。

说实话,这个正则挺复杂,IPv6 的更复杂,我们这小节的例子还是用 IPv4 的吧。

介绍完三类数据验证器,下面本小节的例子就是围绕 MAC、IP、Port 输入来展开的,顺便给三个单行编辑控件设置伙伴快捷键。

重新打开 QtCreator ,新建一个 Qt Widgets Application 项目,在新建项目的向导里填写:

①项目名称 netparas,创建路径 D:\QtProjects\ch05,点击下一步;

②套件选择里面选择全部套件,点击下一步;

③基类选择 QWidget,点击下一步;

④项目管理不修改,点击完成。

建好项目之后,打开窗体 widget.ui 文件,进入设计模式,按照下图拖入控件:

edit

第一行的标签文本为 "&MAC" ,单行编辑控件 objectName 为 lineEditMAC;

第二行的标签文本为 "&IP" ,单行编辑控件 objectName 为 lineEditIP;

第三行的标签文本为 "&Port" ,单行编辑控件 objectName 为 lineEditPort。

将标签控件高度都设置为 20,这样与单行编辑控件一样高,就可以按上图调整位置,对齐所有控件了。这个例子就不弹窗了,我们跟踪三个单行编辑控件的 textChanged() 信号来打印实时的用户输入。

标签控件里的 "&" 用于设置伙伴快捷键,因为单行编辑控件没法显示自己的快捷键,所以需要通过伙伴标签控件来设置快捷键。"&MAC" 意味着伙伴快捷键为 Alt+M ,"&IP" 快捷键就是 Alt+I ,"&Port" 快捷键是 Alt+P 。当然,快捷键能实现的前提是设置伙伴,我们点击设计模式上面的带有橙色小块的图标,进入伙伴编辑模式:

edit

在伙伴编辑模式,编辑伙伴关系类似在画图板画线的操作,从标签控件画线到右边的单行编辑控件即可。将三行的标签都设置为对应的单行编辑控件伙伴。设置为伙伴之后, 标签控件就不再显示 "&" ,而是将 "&" 右边第一个字母添加下划线显示,这样伙伴快捷键就设置成功了。

然后我们点击窗体上方第一个“普通部件编辑模式”图标,回到普通的部件编辑模式,右击每个单行编辑控件,选择右键菜单“转到槽...”,然后为每个单行编辑控件添 加接收 textChanged(QString) 信号的槽函数:

edit

添加三个单行编辑控件的槽函数之后,保存界面文件。回到代码编辑模式,这时候 widget.h 头文件已被自动修改,这个头文件不用手动编辑的,下面只是把它的代码贴出来:

#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
    Q_OBJECT
public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();
private slots:
    void on_lineEditMAC_textChanged(const QString &arg1);
    void on_lineEditIP_textChanged(const QString &arg1);
    void on_lineEditPort_textChanged(const QString &arg1);
private:
    Ui::Widget *ui;
};
#endif // WIDGET_H

下面我们编辑 widget.cpp 文件的内容,添加例子的功能代码,首先是包含头文件,编辑构造函数:

#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QIntValidator>
#include <QRegExp>
#include <QRegExpValidator>
Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    //设置 MAC 输入模板
    ui->lineEditMAC->setInputMask("HH:HH:HH:HH:HH:HH");
    //定义 IPv4 正则表达式,注意 "\\" 就是一个反斜杠字符
    QRegExp re("^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}"
               "(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$");
    //新建正则表达式验证器
    QRegExpValidator *reVali = new QRegExpValidator(re);
    //设置给 lineEditIP
    ui->lineEditIP->setValidator(reVali);
    //新建整数验证器
    QIntValidator *intVali = new QIntValidator(0, 65535);
    //设置给 lineEditPort
    ui->lineEditPort->setValidator(intVali);
}
Widget::~Widget()
{
    delete ui;
}

<QIntValidator> 是整数验证器的头文件,<QRegExp> 和 <QRegExpValidator> 是正则表达式验证器用到的头文件。

在构造函数里,我们先用 MAC 地址单行编辑控件的 setInputMask 函数,将输入模板设置为 "HH:HH:HH:HH:HH:HH" ,这样单行编辑控件就会自动根据这个模板来判断输入数据是否合法,并且会自动为用户填充冒号字符,不需要用户自己敲冒号字符了。

然后定义了 IPv4 正则表达式 re,只用了一个参数,就是正则表达式的字符串形式,其他参数用默认的。

有了正则表达式 re,然后根据 re 新建一个 reVali 验证器。

再将正则表达式验证器设置给 ui->lineEditIP 就行了。setValidator() 函数会自动将验证器的父对象设置为该单行编辑控件,在 单行编辑控件销毁时,该验证器也是随之销毁,所以不用手动 delete。

在构造函数末尾,端口编辑控件使用整数验证器,这个比较简单,新建一个整数验证器 intVali ,然后把验证器设置给 ui->lineEditPort 就搞定了。

接下来是三个槽函数的代码,功能都比较简单:

void Widget::on_lineEditMAC_textChanged(const QString &arg1)
{
    qDebug()<<"MAC: "<<arg1;
}
void Widget::on_lineEditIP_textChanged(const QString &arg1)
{
    qDebug()<<"IP: "<<arg1;
}
void Widget::on_lineEditPort_textChanged(const QString &arg1)
{
    qDebug()<<"Port: "<<arg1;
}

槽函数就是打印当前实时的文本字符串 arg1,对于不同编辑控件,加了相应的前缀用于区分。

这个例子代码就这么多,下面生成并运行例子看看效果:

edit

程序运行时,伙伴快捷键自动生效:

按 Alt+M ,自动切换到 MAC 地址编辑控件;

按 Alt+I ,自动切换到 IP 地址编辑控件;

按 Alt+P ,自动切换到端口编辑控件。

示范的例子标签文本都是英文的,如果是中文文本,以端口为例,可以设置为 "端口(&P)" ,这样快捷键也是 Alt+P。

三个单行编辑控件的验证器功能大家自行测试,看看非法数据能否输入。

(正常情况下,非法数据是无法输入的,也不会触发 textChanged 信号。)

5. 单词补全

在进行文本编辑时,编辑器常用的一个功能就是单词补全,比如 Linux 系统命令行里面输入命令或文件名头几个字符,然后按 Tab 键就会实现命令或文件名的补 全。单行编辑控件也有类似功能,通过设置单词补全器 QCompleter 实现。

QCompleter 常用构造函数为:

QCompleter(QAbstractItemModel * model, QObject * parent = 0)
QCompleter(const QStringList & list, QObject * parent = 0)

parent 是父对象指针,第一个构造函数的 model 是指数据条目的模型,这个在后续章节才会学习。第二个构造函数是本小节使用的,根据一个字符串列表来生成单词补全器。

单词补全器可以设置单词是否大小写敏感,默认是敏感的,区分大小写:

void setCaseSensitivity(Qt::CaseSensitivity caseSensitivity)

Qt::CaseSensitivity 枚举类型有两个枚举常量,Qt::CaseInsensitive 是大小写不敏感,Qt::CaseSensitive 是敏感。

在用户输入单词头几个字符时,单行编辑控件可以根据单词补全器匹配相似的单词并显示出来,补全匹配的单词显示模式(CompletionMode)有三种:

① QCompleter::PopupCompletion,是指正常的弹出单词列表显示。

② QCompleter::InlineCompletion,不弹出列表,将最接近的一个单词显示到编辑框里,补全的后半截字符用选中的高亮显示。

③ QCompleter::UnfilteredPopupCompletion,如名字一样,把单词补全器里所有可能的单词都列出来,不做匹配筛选。

默认情况下都是第一个 QCompleter::PopupCompletion,显示匹配筛选后的简短列表。可以通过如下函数改变补全单词的显示模式:

void setCompletionMode(CompletionMode mode)

弹出的单词补全列表默认不排序的,如果希望字符串列表是有序的,可以提前调用 QStringList 排序函数:

void QStringList::​sort(Qt::CaseSensitivity cs = Qt::CaseSensitive)

参数 cs 指定排序时大小写是否敏感。

关于单词补全器的内容先介绍这么多,以后用到模型的时候再讲关于模型的部分。生成单词补全器之后,就可以通过如下函数把补全器设置给单行编辑控件:

void QLineEdit::​setCompleter(QCompleter * c) 如果 c 是存在的补全器,那么 c 就会设置给单行编辑控件;如果 c 是 NULL,那么将会取消单行编辑控件之前的补全器,就没有单词补全了。

下面开始单词补全的例子,重新打开 QtCreator,新建一个 Qt Widgets Application 项目,在新建项目的向导里填写:

①项目名称 completer,创建路径 D:\QtProjects\ch05,点击下一步;

②套件选择里面选择全部套件,点击下一步;

③基类选择 QWidget,点击下一步;

④项目管理不修改,点击完成。

建好项目之后,打开窗体 widget.ui 文件,进入设计模式,按照下图拖入控件:

edit

也是三个标签控件和三个单行编辑控件,标签控件的大小设置为宽度 80,高度 20,这样能足够显示文本,并利于对齐。

第一行的标签文本为 "&DayOfWeek",单行编辑控件的 objectName 为 lineEditDayOfWeek。

第二行的标签文本为 "&Year",单行编辑控件的 objectName 为 lineEditYear。

第三行的标签文本为 "何夕(&H)",单行编辑控件的 objectName 为 lineEditHeXi。

调整控件位置,尽量让行、列都对齐。

标签文本的 "&" 都是用于设置伙伴快捷键的,可以按上面小节一样的操作,设置伙伴关系:

edit

伙伴设置好之后,"&" 就变成快捷键字母的下划线,比如第三行单行编辑控件的快捷键就是 Alt+H 。

然后回到普通部件编辑模式,为三个单行编辑控件添加接收 textChanged(QString) 信号的槽函数:

edit

三个槽函数都添加好之后,保存界面文件,回到代码编辑模式。头文件 widget.h 不需要手动修改的,按照自动生成的就可以了,下面只是把代码贴出来:

#define WIDGET_H
#include <QWidget>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
    Q_OBJECT
public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();
private slots:
    void on_lineEditDayOfWeek_textChanged(const QString &arg1);
    void on_lineEditYear_textChanged(const QString &arg1);
    void on_lineEditHeXi_textChanged(const QString &arg1);
private:
    Ui::Widget *ui;
};
#endif // WIDGET_H

我们打开 widget.cpp 文件,添加需要的功能代码,首先是头文件包含和构造函数(代码里的字符串没用 tr 函数包裹,是因为都不做翻译,就用固定的字符串):

#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QCompleter>
Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    //星期单词列表
    QStringList listDayOfWeek;
    listDayOfWeek<<"Monday"<<"Tuesday"<<"Wednesday"
                <<"Thursday"<<"Friday"<<"Saturday"<<"Sunday";
    //构建补全器
    QCompleter *cpDayOfWeek = new QCompleter(listDayOfWeek);
    //大小写不敏感
    cpDayOfWeek->setCaseSensitivity(Qt::CaseInsensitive);
    //设置给 lineEditDayOfWeek
    ui->lineEditDayOfWeek->setCompleter(cpDayOfWeek);
    //年份列表
    QStringList listYear;
    listYear<<"2016"<<"2015"
            <<"2008"<<"2006"
            <<"1999"<<"1991";
    //重新排序,默认是大小写敏感排序,对数字字符没影响
    listYear.sort();
    //构建补全器
    QCompleter *cpYear = new QCompleter(listYear);
    //设置给 lineEditYear
    ui->lineEditYear->setCompleter(cpYear);
    //何夕名字列表
    QStringList listHeXi;
    listHeXi<<"何百夕"<<"何千夕"<<"何万夕"<<"何亿夕";
    //中文没有大小写敏感,也不要排序
    //构建补全器
    QCompleter *cpHexi = new QCompleter(listHeXi);
    //设置给 lineEditHeXi
    ui->lineEditHeXi->setCompleter(cpHexi);
}
Widget::~Widget()
{
    delete ui;
}

<QCompleter> 是单词补全器的头文件。然后构造函数里针对三个单行编辑器有三段代码。

首选是星期的单行编辑控件,定义字符串列表 listDayOfWeek,因为 QStringList 类重载了 << 运算符,所以能利用 << 运算符将多个字符串添加到列表里面。listDayOfWeek 列表就是星期一到星期日的英文字符串。

然后根据 listDayOfWeek 构建单词补全器 cpDayOfWeek ,并设置为大小写不敏感,这样用户输入小写的星期单词时也能提示相应的字符串。

接着将 cpDayOfWeek 设置给 ui->lineEditDayOfWeek。

单行编辑控件的 setCompleter() 函数会自动把补全器的父对象设置为该编辑控件,在编辑控件销毁时,子对象 cpDayOfWeek 会随之销毁,所以不用手动 delete。

第二个单行编辑控件是关于年份的,定义 listYear 字符串列表之后,为它添加了 6 个年份字符串,

然后调用 listYear 的排序函数,将散乱的年份按照字母序进行排列,默认是大小写敏感排序。

listYear 排序好之后,根据 listYear 构建单词补全器 cpYear ,然后把补全器设置给 ui->lineEditYear 即可。

第三个单行编辑控件是接收汉字输入的,何夕是人名,先定义人名列表 listHeXi,

将 4 个人名填充到 listHeXi 里面。中文与英文不一样,没有大小写敏感,英文排序对中文也没多大意义。

直接根据 listHeXi 构建补全器 cpHexi ,然后把补全器设置给 ui->lineEditHeXi,这样构造函数代码就完整了。

widget.cpp 文件接下来是三个槽函数的内容,我们编写打印调试输出的代码即可:

void Widget::on_lineEditDayOfWeek_textChanged(const QString &arg1)
{
    qDebug()<<"DayOfWeek: "<<arg1;
}
void Widget::on_lineEditYear_textChanged(const QString &arg1)
{
    qDebug()<<"Year: "<<arg1;
}
void Widget::on_lineEditHeXi_textChanged(const QString &arg1)
{
    qDebug()<<"何夕:"<<arg1;
}

这三个槽函数会跟踪三个编辑控件的内容变化,打印实时的字符串。

例子代码就这些,下面生成运行例子,看看效果:

edit

从图上可以看到除了英文和数字字符,中文字符也是可以补全的,所以 QCompleter 是相当好使的。等以后学到模型章节,还会介绍更多关于补全器的内容,本节内容就到这。


您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码: 验证码,看不清楚?请点击刷新验证码 必填



7 次浏览
1次