# 【Qt編程】基于Qt的詞典開發系列自動補全功能
最近寫了一個查單詞的類似有道詞典的軟件,里面就有一個自動補全功能(即當你輸入一個字母時,就會出現幾個候選項)。這個自動補全功能十分常見,百度搜索關鍵詞時就會出現。不過它們這些補全功能都是與你輸入的進行首字匹配,有時也會不方便。例如,如果我輸入一個“好”,如果是首字匹配的話會出現下圖:

如果是句中匹配的話,則是這種情況:

你可以根據自己的要求進行選擇哪一種模式。
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();
}
~~~
最后放兩張查單詞軟件用到的自動補全功能的截圖:


- 前言
- <一>--詞典框架設計及成品展示
- <二>--本地詞典的設計
- <三>--開始菜單的設計
- <四>--無邊框窗口的縮放與拖動
- <五>--無邊框窗口的拖動
- <六>--界面美化設計
- <七>--調用網絡API
- <八>--用戶登錄及API調用的實現
- <九>--JSON數據解析
- <十>--國際音標的顯示
- <十一>系統托盤的顯示
- <十二>調用講述人
- <十三>音頻播放
- <十四>自動補全功能
- <十五>html特殊字符及正則表達式
- 后序