Sur cette page

Exemple de module de complétion personnalisé

L'exemple de « Custom Completer » montre comment fournir des fonctionnalités de complétion de chaînes de caractères pour un widget de saisie, en s'appuyant sur les données fournies par un modèle. Le « completer » affiche des suggestions de mots possibles en fonction des trois premiers caractères saisis par l'utilisateur, et le mot choisi par ce dernier est inséré dans l'TextEdit e à l'aide de la méthode QTextCursor.

Éditeur de texte doté d'une fonction d'aide à la saisie

Configuration du fichier de ressources

L'exemple « Custom Completer » nécessite un fichier de ressources, wordlist.txt, contenant une liste de mots destinés à aider l'QCompleter à compléter les mots. Ce fichier contient les éléments suivants :

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

Définition de la classe TextEdit

La classe ` TextEdit ` est une sous-classe de ` QTextEdit ` dotée d’un slot personnalisé ` insertCompletion() ` ; elle réimplémente les fonctions ` keyPressEvent()` et ` focusInEvent()`. ` TextEdit ` contient également une fonction privée ` textUnderCursor() ` et une instance privée de ` 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;
};

Implémentation de la classe TextEdit

Le constructeur de TextEdit crée un objet TextEdit avec un parent et initialise c. Les instructions d’utilisation du compléteur s’affichent sur l’objet TextEdit à l’aide de la fonction setPlainText().

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

De plus, TextEdit inclut également un destructeur par défaut :

TextEdit::~TextEdit()
{
}

La fonction ` setCompleter() ` accepte un ` completer ` et le configure. Nous utilisons ` if (c) ` pour vérifier si ` c ` a été initialisé. S’il l’a été, la fonction ` QObject::disconnect()` est appelée pour déconnecter le signal du slot. Cela permet de s’assurer qu’aucun objet « completer » précédent n’est encore connecté au slot.

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

Nous instancions ensuite c avec completer et le définissons comme widget de TextEdit. Le mode de complétion et la sensibilité à la casse sont également définis, puis nous connectons le signal activated() au slot insertCompletion().

La fonction ` completer() ` est une fonction de récupération qui renvoie ` c`.

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

Le compléteur affiche les options disponibles, en fonction du contenu de wordlist.txt, mais c'est le curseur de texte qui se charge de compléter les caractères manquants, selon le mot choisi par l'utilisateur.

Supposons que l’utilisateur saisisse « ACT » et accepte la suggestion « ACTUAL » proposée par le compléteur. La chaîne « completion » est alors envoyée à insertCompletion() par le signal activated() du compléteur.

La fonction ` insertCompletion() ` est chargée de compléter le mot à l’aide d’un objet ` QTextCursor `, ` tc`. Elle effectue une validation pour s’assurer que le widget du module de complétion est de type ` TextEdit ` avant d’utiliser ` tc ` pour insérer les caractères supplémentaires permettant de compléter le mot.

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

La figure ci-dessous illustre ce processus :

Proposition de complétion « ACTUAL » lorsque l'utilisateur tape « ACT »

completion.length() = 6

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

La différence entre ces deux valeurs est extra, soit 3. Cela signifie que les trois derniers caractères à partir de la droite, « U », « A » et « L », seront insérés par tc.

La fonction ` textUnderCursor() ` utilise ` QTextCursor` ( tc) pour sélectionner le mot situé sous le curseur et le renvoyer.

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

La classe ` TextEdit ` réimplémente la fonction ` focusInEvent()`, qui est un gestionnaire d’événements utilisé pour recevoir les événements de focus clavier pour le widget.

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

La méthode keyPressEvent() est réimplémentée pour ignorer les événements de touche tels que Qt::Key_Enter, Qt::Key_Return, Qt::Key_Escape, Qt::Key_Tab et Qt::Key_Backtab, afin que le module de complétion puisse les gérer.

Si un module de complétion est actif, nous ne pouvons pas traiter le raccourci Ctrl+E.

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);

Nous gérons également d'autres modificateurs et raccourcis auxquels nous ne voulons pas que le système de complétion réponde.

    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!
}

Enfin, nous affichons le module de complétion.

Définition de la classe MainWindow

La classe ` MainWindow ` est une sous-classe de ` QMainWindow ` et implémente un slot privé, ` about()`. Cette classe dispose également de deux fonctions privées, ` createMenu() ` et ` modelFromFile() `, ainsi que d'instances privées de ` QCompleter ` et ` 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;
};

Implémentation de la classe MainWindow

Le constructeur crée un objet ` MainWindow ` avec un parent et initialise l’` completer`. Il instancie également un objet ` TextEdit ` et définit son compléteur. Un objet ` QStringListModel`, obtenu à partir de ` modelFromFile()`, est utilisé pour remplir l’` completer`. Le widget central de l’` MainWindow` est défini sur ` TextEdit ` et sa taille est fixée à 500 x 300.

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"));
}

La fonction createMenu() crée les objets QAction nécessaires aux menus « Fichier » et « Aide », et leurs signaux triggered() sont respectivement connectés aux slots quit(), about() et aboutQt().

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

La fonction ` modelFromFile() ` accepte un ` fileName ` et tente d’extraire le contenu de ce fichier dans un ` QStringListModel`. Nous affichons le ` Qt::WaitCursor ` lorsque nous remplissons les ` QStringList` et ` words`, puis nous rétablissons le curseur de la souris une fois l’opération terminée.

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

La fonction « about() » fournit une brève description de l'exemple « Custom Completer ».

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

main() Fonction

La fonction ` main() ` instancie ` MainWindow ` et appelle la fonction ` show()`.

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

Exemple de projet @ code.qt.io

© 2026 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.