牛骨文教育服务平台(让学习变的简单)

【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();  
}  

最后放两张查单词软件用到的自动补全功能的截图: