En esta página

Ejemplo de completador

El ejemplo de completador muestra cómo proporcionar funciones de completado de cadenas para un widget de entrada basado en datos proporcionados por un modelo.

Aplicación que muestra varias opciones y ajustes

Este ejemplo utiliza un modelo de elemento personalizado, FileSystemModel, y un objeto QCompleter. QCompleter es una clase que proporciona completados basados en un modelo de elemento. El tipo de modelo, el modo de cumplimentación y la sensibilidad a mayúsculas y minúsculas pueden seleccionarse mediante cuadros combinados.

El archivo de recursos

El ejemplo Completer requiere un archivo de recursos para almacenar los datos countries.txt y words.txt. El archivo de recursos contiene el siguiente código:

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

Definición de la clase FileSystemModel

La clase FileSystemModel es una subclase de QFileSystemModel, que proporciona un modelo de datos para el sistema de archivos local.

class FileSystemModel : public QFileSystemModel
{
public:
    FileSystemModel(QObject *parent = nullptr);
    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
};

Esta clase sólo tiene un constructor y una función data() ya que sólo se crea para permitir que data() devuelva la ruta de archivo completa para el rol de visualización, a diferencia de la función data() de QFileSystemModel que sólo devuelve la carpeta y no la etiqueta de la unidad. Esto se explica con más detalle en la implementación de FileSystemModel.

Implementación de la clase FileSystemModel

El constructor de la clase FileSystemModel se utiliza para pasar parent a QFileSystemModel.

FileSystemModel::FileSystemModel(QObject *parent)
    : QFileSystemModel(parent)
{
}

Como se mencionó anteriormente, la función data() se reimplementa para conseguir que devuelva la ruta de archivo completa para la función de visualización. Por ejemplo, con QFileSystemModel, verá "Archivos de programa" en la vista. Sin embargo, con FileSystemModel, verá "C:\Program Files".

QVariant FileSystemModel::data(const QModelIndex &index, int role) const
{
    if (role == Qt::DisplayRole && index.column() == 0) {
        QString path  = QDir::toNativeSeparators(filePath(index));
        if (path.endsWith(QDir::separator()))
            path.chop(1);
        return path;
    }

    return QFileSystemModel::data(index, role);
}

El Qt::EditRole, que QCompleter utiliza para buscar coincidencias, no se modifica.

Definición de la clase MainWindow

La clase MainWindow es una subclase de QMainWindow e implementa cinco ranuras privadas - about(), changeCase(), changeMode(), changeModel(), y changeMaxVisible().

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);

private slots:
    void about();
    void changeCase(int);
    void changeMode(int);
    void changeModel();
    void changeMaxVisible(int);

Dentro de la clase MainWindow, tenemos dos funciones privadas: createMenu() y modelFromFile(). También declaramos los widgets privados necesarios - tres objetos QComboBox, un QCheckBox, un QCompleter, un QLabel, y un QLineEdit.

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

    QComboBox *caseCombo = nullptr;
    QComboBox *modeCombo = nullptr;
    QComboBox *modelCombo = nullptr;
    QSpinBox *maxVisibleSpinBox = nullptr;
    QCheckBox *wrapCheckBox = nullptr;
    QCompleter *completer = nullptr;
    QLabel *contentsLabel = nullptr;
    QLineEdit *lineEdit = nullptr;
};

Implementación de la clase MainWindow

El constructor de MainWindow construye un MainWindow con un widget padre e inicializa los miembros privados. A continuación se invoca la función createMenu().

Configuramos tres objetos QComboBox, modelComb, modeCombo y caseCombo. Por defecto, el modelCombo está configurado como QFileSystemModel, el modeCombo está configurado como "Filtered Popup" y el caseCombo está configurado como "Case Insensitive".

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

    QWidget *centralWidget = new QWidget;

    QLabel *modelLabel = new QLabel;
    modelLabel->setText(tr("Model"));

    modelCombo = new QComboBox;
    modelCombo->addItem(tr("QFileSystemModel"));
    modelCombo->addItem(tr("QFileSystemModel that shows full path"));
    modelCombo->addItem(tr("Country list"));
    modelCombo->addItem(tr("Word list"));
    modelCombo->setCurrentIndex(0);

    QLabel *modeLabel = new QLabel;
    modeLabel->setText(tr("Completion Mode"));
    modeCombo = new QComboBox;
    modeCombo->addItem(tr("Inline"));
    modeCombo->addItem(tr("Filtered Popup"));
    modeCombo->addItem(tr("Unfiltered Popup"));
    modeCombo->setCurrentIndex(1);

    QLabel *caseLabel = new QLabel;
    caseLabel->setText(tr("Case Sensitivity"));
    caseCombo = new QComboBox;
    caseCombo->addItem(tr("Case Insensitive"));
    caseCombo->addItem(tr("Case Sensitive"));
    caseCombo->setCurrentIndex(0);

El maxVisibleSpinBox se crea y determina el número de elementos visibles en el completador.

A continuación se crea el wrapCheckBox. Este checkBox determina si la propiedad setWrapAround() de completer está activada o desactivada.

    QLabel *maxVisibleLabel = new QLabel;
    maxVisibleLabel->setText(tr("Max Visible Items"));
    maxVisibleSpinBox = new QSpinBox;
    maxVisibleSpinBox->setRange(3,25);
    maxVisibleSpinBox->setValue(10);

    wrapCheckBox = new QCheckBox;
    wrapCheckBox->setText(tr("Wrap around completions"));
    wrapCheckBox->setChecked(true);

Instanciamos contentsLabel y establecemos su política de tamaño en fixed. Las señales activated() de los cuadros combinados se conectan a sus respectivas ranuras.

    contentsLabel = new QLabel;
    contentsLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);

    connect(modelCombo, &QComboBox::activated,
            this, &MainWindow::changeModel);
    connect(modeCombo, &QComboBox::activated,
            this, &MainWindow::changeMode);
    connect(caseCombo, &QComboBox::activated,
            this, &MainWindow::changeCase);
    connect(maxVisibleSpinBox, &QSpinBox::valueChanged,
            this, &MainWindow::changeMaxVisible);

El lineEdit se instala y luego organizamos todos los widgets usando un QGridLayout. Se llama a la función changeModel(), para inicializar el completer.

    lineEdit = new QLineEdit;

    QGridLayout *layout = new QGridLayout;
    layout->addWidget(modelLabel, 0, 0); layout->addWidget(modelCombo, 0, 1);
    layout->addWidget(modeLabel, 1, 0);  layout->addWidget(modeCombo, 1, 1);
    layout->addWidget(caseLabel, 2, 0);  layout->addWidget(caseCombo, 2, 1);
    layout->addWidget(maxVisibleLabel, 3, 0); layout->addWidget(maxVisibleSpinBox, 3, 1);
    layout->addWidget(wrapCheckBox, 4, 0);
    layout->addWidget(contentsLabel, 5, 0, 1, 2);
    layout->addWidget(lineEdit, 6, 0, 1, 2);
    centralWidget->setLayout(layout);
    setCentralWidget(centralWidget);

    changeModel();

    setWindowTitle(tr("Completer"));
    lineEdit->setFocus();
}

La función createMenu() se utiliza para instanciar los objetos QAction necesarios para rellenar fileMenu y helpMenu. Las señales triggered() de las acciones se conectan a sus respectivas ranuras.

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 el fileName de un archivo y lo procesa en función de su contenido.

Primero validamos el file para asegurarnos de que se puede abrir en modo QFile::ReadOnly. Si esto no se consigue, la función devuelve un QStringListModel vacío.

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

A continuación, el cursor del ratón se sobrescribe con Qt::WaitCursor antes de que rellenemos un objeto QStringList, words, con el contenido de file. Una vez hecho esto, restauramos el cursor del ratón.

#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

Como ya se ha mencionado, el archivo de recursos contiene dos archivos: countries.txt y words.txt. Si el file leído es words.txt, devolvemos un QStringListModel con words como su QStringList y completer como su padre.

    if (!fileName.contains(QLatin1String("countries.txt")))
        return new QStringListModel(words, completer);

Si el file leído es countries.txt, entonces requerimos un QStandardItemModel con words.count() filas, 2 columnas, y completer como su padre.

    QStandardItemModel *m = new QStandardItemModel(words.count(), 2, completer);

Una línea estándar en countries.t xt es:

Noruega NO

Por lo tanto, para rellenar el objeto QStandardItemModel, m, tenemos que dividir el nombre del país y su símbolo. Una vez hecho esto, devolvemos m.

    for (int i = 0; i < words.count(); ++i) {
        QModelIndex countryIdx = m->index(i, 0);
        QModelIndex symbolIdx = m->index(i, 1);
        QString country = words.at(i).mid(0, words[i].length() - 2).trimmed();
        QString symbol = words.at(i).right(2);
        m->setData(countryIdx, country);
        m->setData(symbolIdx, symbol);
    }

    return m;
}

La función changeMode() establece el modo de completer en función del valor de index.

void MainWindow::changeMode(int index)
{
    QCompleter::CompletionMode mode;
    if (index == 0)
        mode = QCompleter::InlineCompletion;
    else if (index == 1)
        mode = QCompleter::PopupCompletion;
    else
        mode = QCompleter::UnfilteredPopupCompletion;

    completer->setCompletionMode(mode);
}

La función changeModel() cambia el modelo de elemento utilizado en función del modelo seleccionado por el usuario.

Se utiliza una sentencia switch para cambiar el modelo de elemento en función del índice de modelCombo. Si case es 0, utilizamos un QFileSystemModel sin ordenar, lo que nos proporciona una ruta de archivo que excluye la etiqueta de la unidad.

void MainWindow::changeModel()
{
    delete completer;
    completer = new QCompleter(this);
    completer->setMaxVisibleItems(maxVisibleSpinBox->value());

    switch (modelCombo->currentIndex()) {
    default:
    case 0:
        { // Unsorted QFileSystemModel
            QFileSystemModel *fsModel = new QFileSystemModel(completer);
            fsModel->setRootPath(QString());
            completer->setModel(fsModel);
            contentsLabel->setText(tr("Enter file path"));
        }
        break;

Nótese que creamos el modelo con completer como padre ya que esto nos permite reemplazar el modelo por uno nuevo. completer se asegurará de que el antiguo se borre en el momento en que se le asigne un nuevo modelo.

Si case es 1, utilizamos el DirModel que definimos anteriormente, lo que resulta en rutas completas para los archivos.

    case 1:
        {   // FileSystemModel that shows full paths
            FileSystemModel *fsModel = new FileSystemModel(completer);
            completer->setModel(fsModel);
            fsModel->setRootPath(QString());
            contentsLabel->setText(tr("Enter file path"));
        }
        break;

Cuando case es 2, intentamos completar los nombres de los países. Esto requiere un objeto QTreeView, treeView. Los nombres de los países se extraen de countries.txt y se establece la ventana emergente utilizada para mostrar las finalizaciones en treeView.

    case 2:
        { // Country List
            completer->setModel(modelFromFile(":/resources/countries.txt"));
            QTreeView *treeView = new QTreeView;
            completer->setPopup(treeView);
            treeView->setRootIsDecorated(false);
            treeView->header()->hide();
            treeView->header()->setStretchLastSection(false);
            treeView->header()->setSectionResizeMode(0, QHeaderView::Stretch);
            treeView->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
            contentsLabel->setText(tr("Enter name of your country"));
        }
        break;

La siguiente captura de pantalla muestra el Completador con el modelo de lista de países.

Aplicación que muestra el contenido del modelo de lista de países

Si case es 3, intentamos completar las palabras. Para ello se utiliza un QStringListModel que contiene datos extraídos de words.txt. El modelo se ordena case insensitively.

La siguiente captura de pantalla muestra el Completador con el modelo de lista de palabras.

Aplicación que muestra el contenido del modelo de lista de palabras

Una vez seleccionado el tipo de modelo, llamamos a la función changeMode() y a la función changeCase() y configuramos la opción de ajuste en consecuencia. La señal clicked() de wrapCheckBox se conecta a la ranura setWrapAround() de completer.

    case 3:
        { // Word list
            completer->setModel(modelFromFile(":/resources/wordlist.txt"));
            completer->setModelSorting(QCompleter::CaseInsensitivelySortedModel);
            contentsLabel->setText(tr("Enter a word"));
        }
        break;
    }

    changeMode(modeCombo->currentIndex());
    changeCase(caseCombo->currentIndex());
    completer->setWrapAround(wrapCheckBox->isChecked());
    lineEdit->setCompleter(completer);
    connect(wrapCheckBox, &QAbstractButton::clicked, completer, &QCompleter::setWrapAround);
}

La función changeMaxVisible() actualiza el número máximo de elementos visibles en el completador.

void MainWindow::changeMaxVisible(int max)
{
    completer->setMaxVisibleItems(max);
}

La función about() proporciona una breve descripción sobre el ejemplo.

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() instantiza QApplication y 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.