在QTextEdit中加入自动提示功能——加入QCompleter功能

1,序

   通常我们使用QLineEdit的时候,可以使用自动补全提示,如pic-1所示,通过键入关键字,QLineEdit可以为我们提示相关内容。但是Qt 4.x没有为QTextEdit提供设置QCompleter的接口。网络上已经给出大量范例代码,基本上出自qt官方文档。
   本文章将做对以下几个方面进行剖析:
    1. 在获取QTextEdit中,光标所在位置对应的单词时,不使用QTextCursor::WordUnderCursor。因为该模式会忽略”$、%”等特殊符号。
    2. 裁剪官方代码,祭出关键框架代码和主要逻辑关系介绍。
    3. 讲解相关知识细节。

// 如下所示,为QLineEdit添加自动提示功能
#include <QApplication>
#include <QCompleter>
#include <QLineEdit>
#include <QStringList>

int main(int ac, char **av) {
    QApplication app(ac, av);

    QLineEdit *lineEdit = new QLineEdit;
    QStringList indicator;
    for (int index = 0; index < 3; ++index) {
        indicator << QString("%1").arg(index);
    }
    QCompleter *completer = new QCompleter(indicator, lineEdit);
    lineEdit->setCompleter(completer);
    lineEdit->show();

    return app.exec();
}

pic-1 : 自动提示
自动提示

2,文件组织和代码详解

   为了更加清晰的说明设计的总框架和内在的勾连,工程只涉及3个文件,如图pic-2所示。其中,main.cpp为入口函数main()文件,其他两个为类CompleterTextEdit对应的header文件和source文件。

pic-2
pic-2:工程中文件组成

   下面将分别按照(1)原理解释;(2)构思框架;(3)代码解读。三个步骤,整体设计进行剖析。

1, 原理解释

   在Qt中,我们每次敲击键盘,都会引起QTextEdit中的事件接收函数keyPressEvent(QKeyEvent *e)的调用。因此,该函数作为着手点,可以对每次敲击键盘进行响应的操作。
   从QTextEdit中,我们看到有光标在闪动,这个闪动的光标在Qt中称作cursor。这个cursor对应到QTextEdit中,就是QTextCursor类对象,我们可以通过QTextEdit来获取QTextCursor类型的对象。我们可以操纵cursor(也就是光标)来进行各种动作,比如移动光标,在光标处插入文本,选中文本。其中,本文内容与Qt官方示例最大的不同,在于,当获得cursor所在位置对应的单词时,不采用WordUnderCursor进行控制。下面代码将显示出这点。
   QCompleter类只有在调用complete()函数后,才会弹出提示下拉列表,提示下拉列表如上面图pic-1所示。为了让QCompleter能够根据关键字进行搜索,我们还需要在调用complete()之前,调用setCompletionPrefix()函数设置关键字前缀。当我们选中下拉列表中的某个选项后,按enter键,QCompleter会发出activated()信号,通过接收并处理该信号,就可以插入文本并对QTextEdit中的cursor进行移动操作。

2, 构思框架

   本设计有三个关键点:
(1)获取当前光标所在位置处,对应的单词。因为WordUnderCursor会忽略掉单次内的特殊符号,如$等,因此我们不能使用该方法。
(2)涉及到一个事件(QKeyEvent)和一个信号(activated())的处理。
(3)如何将QCompleter选中的字符串在QTextEdit中补全。

3, 代码解读

   下面将祭出代码。其中,外部main()函数将自定义的QCompleter放入自定义的CompleterTextEdit中。CompleterTextEdit类内部处理自动提示功能。

// completertextedit.h
#ifndef COMPLETERTEXTEDIT_H
#define COMPLETERTEXTEDIT_H

#include <QTextEdit>
#include <QString>

QT_BEGIN_NAMESPACE

class QCompleter;

QT_END_NAMESPACE

class CompleterTextEdit : public QTextEdit
{
    Q_OBJECT
public:
    explicit CompleterTextEdit(QWidget *parent = 0);
    void setCompleter(QCompleter *completer);

protected:
    void keyPressEvent(QKeyEvent *e); // 响应按键盘事件

private slots:
    void onCompleterActivated(const QString &completion); // 响应选中QCompleter中的选项后,QCompleter发出的activated()信号

private:
    QString wordUnderCursor(); // 获取当前光标所在的单词

private:
    QCompleter *m_completer;

};

#endif // COMPLETERTEXTEDIT_H
// completertextedit.cpp
#include "completertextedit.h"

#include <QAbstractItemView>
#include <QCompleter>
#include <QKeyEvent>
#include <QString>
#include <QTextCursor>

CompleterTextEdit::CompleterTextEdit(QWidget *parent) :
    QTextEdit(parent), m_completer(NULL) {
}

void CompleterTextEdit::setCompleter(QCompleter *completer) {
    m_completer = completer;
    if (m_completer) {
        m_completer->setWidget(this);
        connect(m_completer, SIGNAL(activated(QString)),
                this, SLOT(onCompleterActivated(QString)));
    }
}

void CompleterTextEdit::keyPressEvent(QKeyEvent *e) {
    if (m_completer) {
        if (m_completer->popup()->isVisible()) {
            switch(e->key()) {
            case Qt::Key_Escape:
            case Qt::Key_Enter:
            case Qt::Key_Return:
            case Qt::Key_Tab:
                e->ignore();
                return;
            default:
                break;
            }
        }
        QTextEdit::keyPressEvent(e);
        QString completerPrefix = this->wordUnderCursor();
        m_completer->setCompletionPrefix(completerPrefix); // 通过设置QCompleter的前缀,来让Completer寻找关键词
        m_completer->complete();
    }
}

void CompleterTextEdit::onCompleterActivated(const QString &completion) {
    QString completionPrefix = wordUnderCursor(),
            shouldInertText = completion;
    QTextCursor cursor = this->textCursor();
    if (!completion.contains(completionPrefix)) {// delete the previously typed.
        cursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor, completionPrefix.size());
        cursor.clearSelection();
    } else { // 补全相应的字符
        shouldInertText = shouldInertText.replace(
            shouldInertText.indexOf(completionPrefix), completionPrefix.size(), "");
    }
    cursor.insertText(shouldInertText);
}

QString CompleterTextEdit::wordUnderCursor() { //不断向左移动cursor,并选中字符,并查看选中的单词中是否含有空格——空格作为单词的分隔符
    QTextCursor cursor = this->textCursor();
    while (cursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor)) {
        if (cursor.selectedText().contains(" ")) {
            break;
        }
    }
    return cursor.selectedText().remove(" ");
}
// main.cpp
#include <QApplication>
#include <QCompleter>
#include <QLineEdit>
#include <QStringList>

#include "completertextedit.h"

int main(int ac, char **av) {
    QApplication app(ac, av);

    CompleterTextEdit *textEdit = new CompleterTextEdit;
    QStringList stringList;
    for (int index = 0; index < 4; ++index) {
        stringList << QString("%1%1%1%1").arg(index);
    }
    QCompleter *completer = new QCompleter(stringList, textEdit);
    textEdit->setCompleter(completer);
    textEdit->show();


    return app.exec();
}

   以上为所有代码。转载请注明出自本博客,代码遵循GPLv3版权许可。

猜你喜欢

转载自blog.csdn.net/zhizifengxiang/article/details/80589936