5.2 单行编辑控件
在图形界面程序中,有很多的输入控件,其中以文本编辑控件最为常用,可以接收用户输入的各种文本,比如登录界面常见的用户名、密码,网络连接使用的 IP 和
端口等等,本节先大致介绍一下 Qt
里面的文本编辑和浏览控件,然后详细讲解单行编辑控件的用途,通过三个例子示范单行编辑控件的使用。对于多行丰富内容的文本编辑控件,留在下一节讲解。
5.2.1 文本编辑控件概述
从 Qt 设计师界面可以看到常用的 Qt 文本编辑和浏览控件,包括四个:
其中单行编辑控件 QLineEdit 和 普通文本编辑控件 QPlainTextEdit 都是针对最普通的 C++
字符串编辑和显示,默认都是白底黑字,没有彩色字体。QLineEdit 按照名字,就是只接受单行普通文本输入,QPlainTextEdit
可以接收多行普通文本输入。
丰富文本编辑控件 QTextEdit 是升级版的编辑控件,支持 HTML 网页的丰富文本编辑,当然也可以利用它编辑普通文本。丰富文本浏览控件
QTextBrowser 是 QTextEdit 的只读版本,并能打开网页链接。
本节主要介绍 QLineEdit,下一节主要介绍 QTextEdit 和 QTextBrowser,学习这些内容之后,使用 QPlainTextEdit
就没什么技术难度了,查看 Qt 助手文档可以轻松学会,所以不专门讲解 QPlainTextEdit 了。
5.2.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
因为单行编辑控件文本相对简单,本来文本就不多,所以获取高亮的部分文本情况也比较少。
刚才提到单行编辑控件可以进行复制、粘贴、剪切、撤销、重做等操作,每个操作都有对应的槽函数实现,不过通过代码直接调用这些槽函数情况比较少,就不会一一列举
了,可以通过 Qt 助手查找相应文档。基本情况就介绍这么多,下面通过例子示范单行编辑控件的使用。
5.2.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 文件,进入设计模式,按照下图拖入控件:
界面里有两个标签控件、两个单行编辑控件、两个按压按钮。标签控件的文本如图上显示的:“用户名”、“密码”,标签高度调整为
20,这样与单行编辑控件的高度一样,方便与单行编辑控件对齐。
两个单行编辑控件,上面的 objectName 设为 lineEditUser,下面的 objectName 设为
lineEditPassword,并尽量与两个标签控件对齐。
对于下面两个按钮,左边的文本为“登录”,objectName 设为 pushButtonLogin,右边按钮文本为“退出”,objectName 设为
pushButtonExit。调整控件的位置,尽量看起来对齐。
例子的效果就是点击“登录”按钮时,获取用户名,计算密码的 Hash 值并弹窗显示出来。点击“退出”按钮时,窗口自动关闭。
在图形界面右击两个按钮,在右键菜单选择“转到槽...”,然后为按钮的 clicked() 信号添加槽函数:
为两个按钮添加好槽函数之后,保存界面文件,然后回到代码编辑模式,打开头文件 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() 函数,关闭窗体,因为程序只有一个窗体,关闭之后程序自动退出。
例子代码就这么多,程序运行效果如下图所示:
当用户名和密码都是 user 时,就会显示图上的 Hash 值的字符串,strMsg 只有两行文本,而
QMessageBox::information() 显示的却是三行,那是因为 Hash 值的字符串实在太宽了,所以消息框自动把 Hash
值的字符串放到第三行显示了。
如果用户名密码有一个为空,那么点击“登录”按钮会出现警告消息框:
警告消息框里面的图标与普通信息消息框不一样,其他的功能都是差不多的。另外还有提示严重错误的消息框,函数为
QMessageBox::critical() ,就是图标不一样,其他的功能和普通消息框差不多。
5.2.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 的吧。介绍这个 IPv4 正则表达式的网页链接如下:
https://www.safaribooksonline.com/library/view/regular-expressions-cookbook/9780596802837/ch07s16.html
介绍完三类数据验证器,下面本小节的例子就是围绕 MAC、IP、Port 输入来展开的,顺便给三个单行编辑控件设置伙伴快捷键。
重新打开 QtCreator ,新建一个 Qt Widgets Application 项目,在新建项目的向导里填写:
①项目名称 netparas,创建路径 D:\QtProjects\ch05,点击下一步;
②套件选择里面选择全部套件,点击下一步;
③基类选择 QWidget,点击下一步;
④项目管理不修改,点击完成。
建好项目之后,打开窗体 widget.ui 文件,进入设计模式,按照下图拖入控件:
第一行的标签文本为 "&MAC" ,单行编辑控件 objectName 为 lineEditMAC;
第二行的标签文本为 "&IP" ,单行编辑控件 objectName 为 lineEditIP;
第三行的标签文本为 "&Port" ,单行编辑控件 objectName 为 lineEditPort。
将标签控件高度都设置为 20,这样与单行编辑控件一样高,就可以按上图调整位置,对齐所有控件了。这个例子就不弹窗了,我们跟踪三个单行编辑控件的
textChanged() 信号来打印实时的用户输入。
标签控件里的 "&" 用于设置伙伴快捷键,因为单行编辑控件没法显示自己的快捷键,所以需要通过伙伴标签控件来设置快捷键。"&MAC"
意味着伙伴快捷键为 Alt+M ,"&IP" 快捷键就是 Alt+I ,"&Port" 快捷键是 Alt+P
。当然,快捷键能实现的前提是设置伙伴,我们点击设计模式上面的带有橙色小块的图标,进入伙伴编辑模式:
在伙伴编辑模式,编辑伙伴关系类似在画图板画线的操作,从标签控件画线到右边的单行编辑控件即可。将三行的标签都设置为对应的单行编辑控件伙伴。设置为伙伴之后,
标签控件就不再显示 "&" ,而是将 "&" 右边第一个字母添加下划线显示,这样伙伴快捷键就设置成功了。
然后我们点击窗体上方第一个“普通部件编辑模式”图标,回到普通的部件编辑模式,右击每个单行编辑控件,选择右键菜单“转到槽...”,然后为每个单行编辑控件添
加接收 textChanged(QString) 信号的槽函数:
添加三个单行编辑控件的槽函数之后,保存界面文件。回到代码编辑模式,这时候 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,对于不同编辑控件,加了相应的前缀用于区分。
这个例子代码就这么多,下面生成并运行例子看看效果:
程序运行时,伙伴快捷键自动生效:
按 Alt+M ,自动切换到 MAC 地址编辑控件;
按 Alt+I ,自动切换到 IP 地址编辑控件;
按 Alt+P ,自动切换到端口编辑控件。
示范的例子标签文本都是英文的,如果是中文文本,以端口为例,可以设置为 "端口(&P)" ,这样快捷键也是 Alt+P。
三个单行编辑控件的验证器功能大家自行测试,看看非法数据能否输入。
(正常情况下,非法数据是无法输入的,也不会触发 textChanged 信号。)
5.2.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 文件,进入设计模式,按照下图拖入控件:
也是三个标签控件和三个单行编辑控件,标签控件的大小设置为宽度 80,高度 20,这样能足够显示文本,并利于对齐。
第一行的标签文本为 "&DayOfWeek",单行编辑控件的 objectName 为 lineEditDayOfWeek。
第二行的标签文本为 "&Year",单行编辑控件的 objectName 为 lineEditYear。
第三行的标签文本为 "何夕(&H)",单行编辑控件的 objectName 为 lineEditHeXi。
调整控件位置,尽量让行、列都对齐。
标签文本的 "&" 都是用于设置伙伴快捷键的,可以按上面小节一样的操作,设置伙伴关系:
伙伴设置好之后,"&" 就变成快捷键字母的下划线,比如第三行单行编辑控件的快捷键就是 Alt+H 。
然后回到普通部件编辑模式,为三个单行编辑控件添加接收 textChanged(QString) 信号的槽函数:
三个槽函数都添加好之后,保存界面文件,回到代码编辑模式。头文件 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;
}
这三个槽函数会跟踪三个编辑控件的内容变化,打印实时的字符串。
例子代码就这些,下面生成运行例子,看看效果:
从图上可以看到除了英文和数字字符,中文字符也是可以补全的,所以 QCompleter
是相当好使的。等以后学到模型章节,还会介绍更多关于补全器的内容,本节内容就到这。