Beispiel für einen Custom Completer

Das Beispiel für einen benutzerdefinierten Vervollständiger zeigt, wie ein Eingabe-Widget auf der Grundlage von Daten, die von einem Modell zur Verfügung gestellt werden, die Vervollständigung von Zeichenketten ermöglicht. Der Completer zeigt Vorschläge für mögliche Wörter an, die auf den ersten drei vom Benutzer eingegebenen Zeichen basieren, und das vom Benutzer gewählte Wort wird mit Hilfe von QTextCursor in TextEdit eingefügt.

Einrichten der Ressourcendatei

Das Beispiel für den Custom Completer erfordert eine Ressourcendatei, wordlist.txt, die eine Liste von Wörtern enthält, die QCompleter bei der Vervollständigung von Wörtern helfen. Diese Datei enthält das Folgende:

<!DOCTYPE RCC><RCC version="1.0">
<qresource prefix="/">
   <file>resources/wordlist.txt</file>
</qresource>
</RCC>

TextEdit-Klassendefinition

Die Klasse TextEdit ist eine Unterklasse von QTextEdit mit einem benutzerdefinierten Steckplatz insertCompletion() und implementiert die Funktionen keyPressEvent() und focusInEvent() neu. TextEdit enthält auch eine private Funktion textUnderCursor() und eine private Instanz von QCompleter, c.

class TextEdit : public QTextEdit
{
    Q_OBJECT

public:
    TextEdit(QWidget *parent = nullptr);
    ~TextEdit();

    void setCompleter(QCompleter *c);
    QCompleter *completer() const;

protected:
    void keyPressEvent(QKeyEvent *e) override;
    void focusInEvent(QFocusEvent *e) override;

private slots:
    void insertCompletion(const QString &completion);

private:
    QString textUnderCursor() const;

private:
    QCompleter *c = nullptr;
};

Implementierung der TextEdit-Klasse

Der Konstruktor für TextEdit erstellt ein TextEdit mit einem Parent und initialisiert c. Die Anweisungen zur Verwendung der Vervollständigung werden auf dem Objekt TextEdit mit der Funktion setPlainText() angezeigt.

TextEdit::TextEdit(QWidget *parent)
    : QTextEdit(parent)
{
    setPlainText(tr("This TextEdit provides autocompletions for words that have more than"
                    " 3 characters. You can trigger autocompletion using ") +
                    QKeySequence("Ctrl+E").toString(QKeySequence::NativeText));
}

Darüber hinaus enthält TextEdit auch einen Standard-Destruktor:

TextEdit::~TextEdit()
{
}

Die Funktion setCompleter() nimmt ein completer entgegen und richtet es ein. Wir verwenden if (c), um zu prüfen, ob c initialisiert wurde. Wenn es initialisiert wurde, wird die Funktion QObject::disconnect() aufgerufen, um das Signal vom Slot zu trennen. Damit soll sichergestellt werden, dass kein vorheriges Completter-Objekt noch mit dem Slot verbunden ist.

void TextEdit::setCompleter(QCompleter *completer)
{
    if (c)
        c->disconnect(this);

    c = completer;

    if (!c)
        return;

    c->setWidget(this);
    c->setCompletionMode(QCompleter::PopupCompletion);
    c->setCaseSensitivity(Qt::CaseInsensitive);
    QObject::connect(c, QOverload<const QString &>::of(&QCompleter::activated),
                     this, &TextEdit::insertCompletion);
}

Anschließend wird c mit completer instanziiert und als Widget von TextEdit festgelegt. Der Vervollständigungsmodus und die Groß-/Kleinschreibung werden ebenfalls eingestellt und dann verbinden wir das Signal activated() mit dem Slot insertCompletion().

Die Funktion completer() ist eine Getter-Funktion, die c zurückgibt.

QCompleter *TextEdit::completer() const
{
    return c;
}

Die Vervollständigungsfunktion zeigt die verfügbaren Optionen an, die auf dem Inhalt von wordlist.txt basieren, aber der Textcursor ist dafür verantwortlich, die fehlenden Zeichen entsprechend der Wortwahl des Benutzers auszufüllen.

Angenommen, der Benutzer gibt "ACT" ein und akzeptiert den Vorschlag des Vervollständigers "ACTUAL". Die Zeichenfolge completion wird dann durch das Signal activated() des Vervollständigers an insertCompletion() gesendet.

Die Funktion insertCompletion() ist für die Vervollständigung des Wortes mit einem QTextCursor Objekt, tc, zuständig. Sie überprüft, ob das Widget des Vervollständigers TextEdit ist, bevor sie tc verwendet, um die zusätzlichen Zeichen zur Vervollständigung des Wortes einzufügen.

void TextEdit::insertCompletion(const QString &completion)
{
    if (c->widget() != this)
        return;
    QTextCursor tc = textCursor();
    int extra = completion.length() - c->completionPrefix().length();
    tc.movePosition(QTextCursor::Left);
    tc.movePosition(QTextCursor::EndOfWord);
    tc.insertText(completion.right(extra));
    setTextCursor(tc);
}

Die folgende Abbildung veranschaulicht diesen Vorgang:

completion.length() = 6

c->completionPrefix().length()=3

Die Differenz zwischen diesen beiden Werten ist extra, die 3 beträgt. Das bedeutet, dass die letzten drei Zeichen von rechts, "U", "A" und "L", von tc eingefügt werden.

Die Funktion textUnderCursor() verwendet eine QTextCursor, tc, um ein Wort unter dem Cursor auszuwählen und es zurückzugeben.

QString TextEdit::textUnderCursor() const
{
    QTextCursor tc = textCursor();
    tc.select(QTextCursor::WordUnderCursor);
    return tc.selectedText();
}

Die Klasse TextEdit reimplementiert die Funktion focusInEvent(), die als Event-Handler verwendet wird, um Tastatur-Fokus-Ereignisse für das Widget zu empfangen.

void TextEdit::focusInEvent(QFocusEvent *e)
{
    if (c)
        c->setWidget(this);
    QTextEdit::focusInEvent(e);
}

Die Funktion keyPressEvent() wurde neu implementiert, um Tastenereignisse wie Qt::Key_Enter, Qt::Key_Return, Qt::Key_Escape, Qt::Key_Tab und Qt::Key_Backtab zu ignorieren, so dass der Vervollständiger sie verarbeiten kann.

Wenn es ein aktives Vervollständigungsprogramm gibt, können wir die Tastenkombination Strg+E nicht verarbeiten.

void TextEdit::keyPressEvent(QKeyEvent *e)
{
    if (c && c->popup()->isVisible()) {
        // The following keys are forwarded by the completer to the widget
       switch (e->key()) {
       case Qt::Key_Enter:
       case Qt::Key_Return:
       case Qt::Key_Escape:
       case Qt::Key_Tab:
       case Qt::Key_Backtab:
            e->ignore();
            return; // let the completer do default behavior
       default:
           break;
       }
    }

    const bool isShortcut = (e->modifiers().testFlag(Qt::ControlModifier) && e->key() == Qt::Key_E); // CTRL+E
    if (!c || !isShortcut) // do not process the shortcut when we have a completer
        QTextEdit::keyPressEvent(e);

Wir behandeln auch andere Modifikatoren und Tastenkombinationen, auf die das Kompilierprogramm nicht reagieren soll.

    const bool ctrlOrShift = e->modifiers().testFlag(Qt::ControlModifier) ||
                             e->modifiers().testFlag(Qt::ShiftModifier);
    if (!c || (ctrlOrShift && e->text().isEmpty()))
        return;

    static QString eow("~!@#$%^&*()_+{}|:\"<>?,./;'[]\\-="); // end of word
    const bool hasModifier = (e->modifiers() != Qt::NoModifier) && !ctrlOrShift;
    QString completionPrefix = textUnderCursor();

    if (!isShortcut && (hasModifier || e->text().isEmpty()|| completionPrefix.length() < 3
                      || eow.contains(e->text().right(1)))) {
        c->popup()->hide();
        return;
    }

    if (completionPrefix != c->completionPrefix()) {
        c->setCompletionPrefix(completionPrefix);
        c->popup()->setCurrentIndex(c->completionModel()->index(0, 0));
    }
    QRect cr = cursorRect();
    cr.setWidth(c->popup()->sizeHintForColumn(0)
                + c->popup()->verticalScrollBar()->sizeHint().width());
    c->complete(cr); // popup it up!
}

Schließlich wird die Vervollständigung eingeblendet.

Definition der Klasse MainWindow

Die Klasse MainWindow ist eine Unterklasse von QMainWindow und implementiert einen privaten Slot, about(). Diese Klasse verfügt auch über zwei private Funktionen, createMenu() und modelFromFile(), sowie über private Instanzen von QCompleter und TextEdit.

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);

private slots:
    void about();

private:
    void createMenu();
    QAbstractItemModel *modelFromFile(const QString& fileName);

    QCompleter *completer = nullptr;
    TextEdit *completingTextEdit;
};

Implementierung der Klasse MainWindow

Der Konstruktor konstruiert ein MainWindow mit einem Elternteil und initialisiert das completer. Er instanziiert auch ein TextEdit und setzt dessen Komplementär. Ein QStringListModel, das von modelFromFile() stammt, wird verwendet, um das completer zu füllen. Das zentrale Widget von MainWindow wird auf TextEdit und seine Größe auf 500 x 300 gesetzt.

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    createMenu();

    completingTextEdit = new TextEdit;
    completer = new QCompleter(this);
    completer->setModel(modelFromFile(":/resources/wordlist.txt"));
    completer->setModelSorting(QCompleter::CaseInsensitivelySortedModel);
    completer->setCaseSensitivity(Qt::CaseInsensitive);
    completer->setWrapAround(false);
    completingTextEdit->setCompleter(completer);

    setCentralWidget(completingTextEdit);
    resize(500, 300);
    setWindowTitle(tr("Completer"));
}

Die Funktion createMenu() erstellt die erforderlichen QAction Objekte, die für die Menüs "Datei" und "Hilfe" benötigt werden, und ihre triggered()-Signale werden mit den Steckplätzen quit(), about() bzw. aboutQt() verbunden.

void MainWindow::createMenu()
{
    QAction *exitAction = new QAction(tr("Exit"), this);
    QAction *aboutAct = new QAction(tr("About"), this);
    QAction *aboutQtAct = new QAction(tr("About Qt"), this);

    connect(exitAction, &QAction::triggered, qApp, &QApplication::quit);
    connect(aboutAct, &QAction::triggered, this, &MainWindow::about);
    connect(aboutQtAct, &QAction::triggered, qApp, &QApplication::aboutQt);

    QMenu *fileMenu = menuBar()->addMenu(tr("File"));
    fileMenu->addAction(exitAction);

    QMenu *helpMenu = menuBar()->addMenu(tr("About"));
    helpMenu->addAction(aboutAct);
    helpMenu->addAction(aboutQtAct);
}

Die Funktion modelFromFile() nimmt eine fileName entgegen und versucht, den Inhalt dieser Datei in eine QStringListModel zu extrahieren. Wir zeigen die Qt::WaitCursor an, wenn wir die QStringList, words auffüllen, und stellen den Mauszeiger wieder her, wenn wir fertig sind.

QAbstractItemModel *MainWindow::modelFromFile(const QString& fileName)
{
    QFile file(fileName);
    if (!file.open(QFile::ReadOnly))
        return new QStringListModel(completer);

#ifndef QT_NO_CURSOR
    QGuiApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
#endif
    QStringList words;

    while (!file.atEnd()) {
        QByteArray line = file.readLine();
        if (!line.isEmpty())
            words << QString::fromUtf8(line.trimmed());
    }

#ifndef QT_NO_CURSOR
    QGuiApplication::restoreOverrideCursor();
#endif
    return new QStringListModel(words, completer);
}

Die Funktion about() liefert eine kurze Beschreibung des Custom Completer-Beispiels.

void MainWindow::about()
{
    QMessageBox::about(this, tr("About"), tr("This example demonstrates the "
        "different features of the QCompleter class."));
}

main() Funktion

Die Funktion main() instanziiert MainWindow und ruft die Funktion show() auf.

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    MainWindow window;
    window.show();
    return app.exec();
}

Beispielprojekt @ code.qt.io

© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.