En esta página

Ejemplo de completador personalizado

El ejemplo de completador personalizado muestra cómo proporcionar funciones de completado de cadenas para un widget de entrada basado en datos proporcionados por un modelo. El completador muestra sugerencias de posibles palabras basadas en los tres primeros caracteres introducidos por el usuario y la palabra elegida por el usuario se inserta en TextEdit utilizando QTextCursor.

Editor de texto con función de autocompletar

Configuración del archivo de recursos

El ejemplo del completador personalizado requiere un archivo de recursos, wordlist.txt, que contiene una lista de palabras para ayudar a QCompleter a completar palabras. Este archivo contiene lo siguiente:

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

Definición de la clase TextEdit

La clase TextEdit es una subclase de QTextEdit con una ranura personalizada insertCompletion() y reimplementa las funciones keyPressEvent() y focusInEvent(). TextEdit también contiene una función privada textUnderCursor() y una instancia privada 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;
};

Implementación de la clase TextEdit

El constructor para TextEdit construye un TextEdit con un padre e inicializa c. Las instrucciones para utilizar el completador se muestran en el objeto TextEdit, utilizando la función 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));
}

Además, TextEdit también incluye un destructor por defecto:

TextEdit::~TextEdit()
{
}

La función setCompleter() acepta un completer y lo inicializa. Utilizamos if (c) para comprobar si c ha sido inicializado. Si se ha inicializado, se invoca la función QObject::disconnect() para desconectar la señal de la ranura. Esto es para asegurar que ningún objeto completador anterior está todavía conectado a la ranura.

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

A continuación, instanciamos c con completer y lo establecemos como widget de TextEdit. También se establecen el modo de completado y la sensibilidad a mayúsculas y minúsculas y, a continuación, conectamos la señal activated() a la ranura insertCompletion().

La función completer() es una función getter que devuelve c.

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

El completador muestra las opciones disponibles, basándose en el contenido de wordlist.txt, pero el cursor de texto se encarga de rellenar los caracteres que faltan, según la palabra elegida por el usuario.

Supongamos que el usuario introduce "ACT" y acepta la sugerencia del completador de "ACTUAL". La cadena completion se envía entonces a insertCompletion() mediante la señal activated() del completador.

La función insertCompletion() se encarga de completar la palabra utilizando un objeto QTextCursor, tc. Valida para asegurarse de que el widget del completador es TextEdit antes de utilizar tc para insertar los caracteres extra para completar la palabra.

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 figura siguiente ilustra este proceso:

Cadena de finalización sugerida "ACTUAL" cuando el usuario escribe "ACT".

completion.length() = 6

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

La diferencia entre estos dos valores es extra, que es 3. Esto significa que los tres últimos caracteres de la derecha, "U", "A" y "L", serán insertados por tc.

La función textUnderCursor() utiliza una clase QTextCursor, tc, para seleccionar una palabra bajo el cursor y devolverla.

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

La clase TextEdit reimplementa la función focusInEvent(), que es un controlador de eventos utilizado para recibir eventos de enfoque del teclado para el widget.

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

La función keyPressEvent() se reimplementa para ignorar eventos de teclado como Qt::Key_Enter, Qt::Key_Return, Qt::Key_Escape, Qt::Key_Tab, y Qt::Key_Backtab para que el completador pueda manejarlos.

Si hay un completador activo, no podemos procesar el atajo de teclado, 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);

También tratamos otros modificadores y atajos para los que no queremos que el completador responda.

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

Finalmente, desplegamos el completador.

Definición de la clase MainWindow

La clase MainWindow es una subclase de QMainWindow e implementa una ranura privada, about(). Esta clase también tiene dos funciones privadas, createMenu() y modelFromFile() así como instancias privadas de QCompleter y 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;
};

Implementación de la clase MainWindow

El constructor construye un MainWindow con un padre e inicializa el completer. También instancia un TextEdit y establece su completador. Un QStringListModel, obtenido de modelFromFile(), se utiliza para rellenar el completer. El widget central de MainWindow se establece en TextEdit y su tamaño se fija en 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 función createMenu() crea los objetos QAction necesarios para los menús "Archivo" y "Ayuda" y sus señales triggered() se conectan a las ranuras quit(), about() y aboutQt() respectivamente.

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 función modelFromFile() acepta un fileName e intenta extraer el contenido de este archivo en un QStringListModel. Mostramos el Qt::WaitCursor cuando estamos rellenando los QStringList, words, y restauramos el cursor del ratón cuando hemos terminado.

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 función about() proporciona una breve descripción sobre el ejemplo de completador personalizado.

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

main() Función

La función main() instancia MainWindow e invoca la función show().

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

Proyecto de ejemplo @ 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.