# 【Qt编程】基于Qt的词典开发系列自动补全功能
最近写了一个查单词的类似有道词典的软件,里面就有一个自动补全功能(即当你输入一个字母时,就会出现几个候选项)。这个自动补全功能十分常见,百度搜索关键词时就会出现。不过它们这些补全功能都是与你输入的进行首字匹配,有时也会不方便。例如,如果我输入一个“好”,如果是首字匹配的话会出现下图:
![](https://box.kancloud.cn/2016-01-15_5698c1c956712.jpg)
如果是句中匹配的话,则是这种情况:
![](https://box.kancloud.cn/2016-01-15_5698c1c968ab2.jpg)
你可以根据自己的要求进行选择哪一种模式。
Qt中自带QCompleter类来实现上面的自动补全功能,读者可以在Qt自带的demo中很容易的学会该类的使用。**下面我要讲的是自己构造一个比QCompleter更强大的类**。有人会说,为什么有现成的不用,要自己写一个类呢?因为,我用QCompleter类的时候发现,它只有句首匹配模式(可能是我没仔细看文档,不知道可以改变模式),其次,当我的词库非常大的时候,有的时候就不会出现下拉自动补全列表,具体原因也不清楚。所以自己写了一个类,来实现QCompleter类所没有功能。废话不多说,直接见代码(代码注解比较详细,就不仔细讲解了,widget.ui文件也不给出了,就是一个空的界面):
1、widget.h
~~~
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include<QMouseEvent>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
void mousePressEvent(QMouseEvent *event);
private:
Ui::Widget *ui;
signals:
void movesignal();
};
#endif // WIDGET_H
~~~
2.completelineedit.h
~~~
#ifndef COMPLETELINEEDIT_H
#define COMPLETELINEEDIT_H
#include <QLineEdit>
#include <QStringList>
#include<QFile>
#include<QTextCodec>
#include<QDebug>
class QListView;
class QStringListModel;
class QModelIndex;
class CompleteLineEdit : public QLineEdit {
Q_OBJECT
public:
CompleteLineEdit(QStringList words, QWidget *parent = 0);
public slots:
void setCompleter(const QString &text); // 动态的显示完成列表
void completeText(const QModelIndex &index); // 点击完成列表中的项,使用此项自动完成输入的单词
protected:
virtual void keyPressEvent(QKeyEvent *e);
virtual void focusOutEvent(QFocusEvent *e);
private slots:
void replyMoveSignal();
private:
QStringList words; // 整个完成列表的单词
QListView *listView; // 完成列表
QStringListModel *model; // 完成列表的model
};
#endif // COMPLETELINEEDIT_H
~~~
3.widget.cpp
~~~
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::mousePressEvent(QMouseEvent *event)
{
emit movesignal();
}
~~~
4.completelineedit.cpp
~~~
#include "CompleteLineEdit.h"
#include <QKeyEvent>
#include <QListView>
#include <QStringListModel>
#include <QDebug>
CompleteLineEdit::CompleteLineEdit(QStringList words, QWidget *parent)
: QLineEdit(parent), words(words)
{
listView = new QListView(this);//用于显示下拉列表
model = new QStringListModel(this);
listView->setWindowFlags(Qt::ToolTip);//设置下拉列表的样式
connect(this, SIGNAL(textChanged(const QString &)), this, SLOT(setCompleter(const QString &)));
connect(listView, SIGNAL(clicked(const QModelIndex &)), this, SLOT(completeText(const QModelIndex &)));
}
void CompleteLineEdit::focusOutEvent(QFocusEvent *e)
{
// listView->hide();//当输入行不是焦点时,隐藏自动补全的下拉列表
}
void CompleteLineEdit::replyMoveSignal()
{
listView->hide();//当输入行不是焦点时,隐藏自动补全的下拉列表
}
void CompleteLineEdit::keyPressEvent(QKeyEvent *e)
{
if (!listView->isHidden())
{
int key = e->key();
int count = listView->model()->rowCount();
QModelIndex currentIndex = listView->currentIndex();
if (Qt::Key_Down == key)
{
// 按向下方向键时
int row = currentIndex.row() + 1;
if (row >= count)
{
row = 0;
}
QModelIndex index = listView->model()->index(row, 0);
listView->setCurrentIndex(index);
} else if (Qt::Key_Up == key)
{
// 按向下方向键时
int row = currentIndex.row() - 1;
if (row < 0)
{
row = count - 1;
}
QModelIndex index = listView->model()->index(row, 0);
listView->setCurrentIndex(index);
} else if (Qt::Key_Escape == key)
{
// 按下Esc键时隐藏完成列表
listView->hide();
} else if (Qt::Key_Enter == key || Qt::Key_Return == key)
{
// 按下回车键时,使用完成列表中选中的项,并隐藏完成列表
if (currentIndex.isValid())
{
QString text = listView->currentIndex().data().toString();
setText(text);
}
listView->hide();
} else
{
// 其他情况,隐藏完成列表,并使用QLineEdit的键盘按下事件
listView->hide();
QLineEdit::keyPressEvent(e);
}
} else
{
QLineEdit::keyPressEvent(e);
}
}
void CompleteLineEdit::setCompleter(const QString &text)
{
if (text.isEmpty())//没有输入内容的情况
{
listView->hide();
return;
}
if ((text.length() > 1) && (!listView->isHidden()))
{
return;
}
// 如果完整的完成列表中的某个单词包含输入的文本,则加入要显示的完成列表串中
QStringList sl;
foreach(QString word, words)
{
//填充模式一
if (word.contains(text))//只要包含该输入内容就显示,这里也可以设置大小写不敏感
{
sl << word;
}
//填充模式二
// if(word.indexOf(text,0,Qt::CaseInsensitive)==0)//必需与句首内容相同
// sl<<word;
}
model->setStringList(sl);
listView->setModel(model);
if (model->rowCount() == 0)
{
return;
}
// 设置列表的显示位置及大小
listView->setMinimumWidth(width());
listView->setMaximumWidth(width());
QPoint p(0, height());
int x = mapToGlobal(p).x();
int y = mapToGlobal(p).y() + 1;
listView->move(x, y);
listView->show();
}
void CompleteLineEdit::completeText(const QModelIndex &index)
{
QString text = index.data().toString();
setText(text);
listView->hide();
}
~~~
5.main.cpp
~~~
#include <QApplication>
#include "CompleteLineEdit.h"
#include"widget.h"
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
QStringList sl;
QFile *inFile=new QFile ("input.txt");//这个是你自己的词库
if(!inFile->open(QIODevice::ReadOnly|QIODevice::Text))
{
qDebug()<<"cannot read!";
}
while(!inFile->atEnd())
{
QByteArray line = inFile->readLine();
QTextCodec* gbk_codec = QTextCodec::codecForName("GBK");
QString gbk_string = gbk_codec->toUnicode(line);
if (!line.isEmpty())
sl << gbk_string.trimmed();//将文件中的词汇输入到sl中
}
inFile->close();//关闭文件
sl<< "你好" << "好的" << "好吗" << "你的" << "真好啊" << "天真" << "你好吗";
Widget *w= new Widget();
CompleteLineEdit * edit= new CompleteLineEdit(sl,w);
w->show();
// QObject::connect(w,SIGNAL(movesignal()),edit,SLOT(replyMoveSignal()));
return a.exec();
}
~~~
最后放两张查单词软件用到的自动补全功能的截图:
![](https://box.kancloud.cn/2016-01-15_5698c1c36e518.jpg)
![](https://box.kancloud.cn/2016-01-15_5698c1c97de69.jpg)
- 前言
- <一>--词典框架设计及成品展示
- <二>--本地词典的设计
- <三>--开始菜单的设计
- <四>--无边框窗口的缩放与拖动
- <五>--无边框窗口的拖动
- <六>--界面美化设计
- <七>--调用网络API
- <八>--用户登录及API调用的实现
- <九>--JSON数据解析
- <十>--国际音标的显示
- <十一>系统托盘的显示
- <十二>调用讲述人
- <十三>音频播放
- <十四>自动补全功能
- <十五>html特殊字符及正则表达式
- 后序