|  | Home · Overviews · Examples

# Completer Example

The Completer example shows how to provide string-completion facilities for an input widget based on data provided by a model.

This example uses a custom item model, `DirModel`, and a QCompleter object. QCompleter is a class that provides completions based on an item model. The type of model, the completion mode, and the case sensitivity can be selected using combo boxes.

## The Resource File

The Completer example requires a resource file in order to store the countries.txt and words.txt. The resource file contains the following code:

## DirModel Class Definition

The `DirModel` class is a subclass of QDirModel, which provides a data model for the local filesystem.

    class DirModel : public QDirModel
    {
    public:
        DirModel(QObject *parent = 0);
        QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
    };

This class only has a constructor and a `data()` function as it is only created to enable `data()` to return the entire file path for the display role, unlike QDirModel's `data()` function that only returns the folder and not the drive label. This is further explained in `DirModel`'s implementation.

## DirModel Class Implementation

The constructor for the `DirModel` class is used to pass `parent` to QDirModel.
The following code example is written in c++.

    DirModel::DirModel(QObject *parent)
        : QDirModel(parent)
    {
    }

As mentioned earlier, the `data()` function is reimplemented in order to get it to return the entire file parth for the display role. For example, with a QDirModel, you will see "Program Files" in the view. However, with `DirModel`, you will see "C:\Program Files".
The following code example is written in c++.

    QVariant DirModel::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 QDirModel::data(index, role);
    }

The screenshots below illustrate this difference:

The Qt::EditRole, which QCompleter uses to look for matches, is left unchanged.

## MainWindow Class Definition

The `MainWindow` class is a subclass of QMainWindow and implements four private slots - `about()`, `changeCase()`, `changeMode()`, and `changeModel()`.

    class MainWindow : public QMainWindow
    {
        Q_OBJECT

    public:
        MainWindow(QWidget *parent = 0);

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

Within the `MainWindow` class, we have two private functions: `createMenu()` and `modelFromFile()`. We also declare the private widgets needed - three QComboBox objects, a QCheckBox, a QCompleter, a QLabel, and a QLineEdit.

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

        QComboBox *caseCombo;
        QComboBox *modeCombo;
        QComboBox *modelCombo;
        QCheckBox *wrapCheckBox;
        QCompleter *completer;
        QLabel *contentsLabel;
        QLineEdit *lineEdit;
    };

## MainWindow Class Implementation

The constructor of `MainWindow` constructs a `MainWindow` with a parent widget and initializes the private members. The `createMenu()` function is then invoked.

We set up three QComboBox objects, `modelComb`, `modeCombo` and `caseCombo`. By default, the `modelCombo` is set to QDirModel, the `modeCombo` is set to "Filtered Popup" and the `caseCombo` is set to "Case Insensitive".
The following code example is written in c++.

    MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent), completer(0), lineEdit(0)
    {
        createMenu();

        QWidget *centralWidget = new QWidget;

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

        modelCombo = new QComboBox;
        modelCombo->addItem(tr("QDirModel"));
        modelCombo->addItem(tr("QDirModel 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);

The `wrapCheckBox` is then set up. This `checkBox` determines if the `completer`'s setWrapAround() property is enabled or disabled.
The following code example is written in c++.

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

We instantiate `contentsLabel` and set its size policy to fixed. The combo boxes' activated() signals are then connected to their respective slots.
The following code example is written in c++.

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

        connect(modelCombo, SIGNAL(activated(int)), this, SLOT(changeModel()));
        connect(modeCombo, SIGNAL(activated(int)), this, SLOT(changeMode(int)));
        connect(caseCombo, SIGNAL(activated(int)), this, SLOT(changeCase(int)));

The `lineEdit` is set up and then we arrange all the widgets using a QGridLayout. The `changeModel()` function is called, to initialize the `completer`.
The following code example is written in c++.

        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(wrapCheckBox, 3, 0);
        layout->addWidget(contentsLabel, 4, 0, 1, 2);
        layout->addWidget(lineEdit, 5, 0, 1, 2);
        centralWidget->setLayout(layout);
        setCentralWidget(centralWidget);

        changeModel();

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

The `createMenu()` function is used to instantiate the QAction objects needed to fill the `fileMenu` and `helpMenu`. The actions' triggered() signals are connected to their respective slots.
The following code example is written in c++.

    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, SIGNAL(triggered()), qApp, SLOT(quit()));
        connect(aboutAct, SIGNAL(triggered()), this, SLOT(about()));
        connect(aboutQtAct, SIGNAL(triggered()), qApp, SLOT(aboutQt()));

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

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

The `modelFromFile()` function accepts the `fileName` of a file and processes it depending on its contents.

We first validate the `file` to ensure that it can be opened in QFile::ReadOnly mode. If this is unsuccessful, the function returns an empty QStringListModel.
The following code example is written in c++.

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

The mouse cursor is then overriden with Qt::WaitCursor before we fill a QStringList object, `words`, with the contents of `file`. Once this is done, we restore the mouse cursor.
The following code example is written in c++.

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

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

    #ifndef QT_NO_CURSOR
        QApplication::restoreOverrideCursor();
    #endif

As mentioned earlier, the resources file contains two files - countries.txt and words.txt. If the `file` read is words.txt, we return a QStringListModel with `words` as its QStringList and `completer` as its parent.
The following code example is written in c++.

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

If the `file` read is countries.txt, then we require a QStandardItemModel with `words.count()` rows, 2 columns, and `completer` as its parent.
The following code example is written in c++.

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

A standard line in countries.txt is:Norway NO Hence, to populate the QStandardItemModel object, `m`, we have to split the country name and its symbol. Once this is done, we return `m`.
The following code example is written in c++.

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

        return m;
    }

The `changeMode()` function sets the `completer`'s mode, depending on the value of `index`.
The following code example is written in c++.

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

The `changeModel()` function changes the item model used based on the model selected by the user.

A `switch` statement is used to change the item model based on the index of `modelCombo`. If `case` is 0, we use an unsorted QDirModel, providing us with a file path excluding the drive label.
The following code example is written in c++.

    void MainWindow::changeModel()
    {
        delete completer;
        completer = new QCompleter(this);

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

Note that we create the model with `completer` as the parent as this allows us to replace the model with a new model. The `completer` will ensure that the old one is deleted the moment a new model is assigned to it.

If `case` is 1, we use the `DirModel` we defined earlier, resulting in full paths for the files.
The following code example is written in c++.

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

When `case` is 2, we attempt to complete names of countries. This requires a QTreeView object, `treeView`. The country names are extracted from countries.txt and set the popup used to display completions to `treeView`.
The following code example is written in c++.

        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()->setResizeMode(0, QHeaderView::Stretch);
                treeView->header()->setResizeMode(1, QHeaderView::ResizeToContents);
                contentsLabel->setText(tr("Enter name of your country"));
            }
            break;

The screenshot below shows the Completer with the country list model.
If `case` is 3, we attempt to complete words. This is done using a QStringListModel that contains data extracted from words.txt. The model is sorted case insensitively.

The screenshot below shows the Completer with the word list model.

Once the model type is selected, we call the `changeMode()` function and the `changeCase()` function and set the wrap option accordingly. The `wrapCheckBox`'s clicked() signal is connected to the `completer`'s setWrapAround() slot.
The following code example is written in c++.

        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, SIGNAL(clicked(bool)), completer, SLOT(setWrapAround(bool)));
    }

The `about()` function provides a brief description about the example.
The following code example is written in c++.

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

## main() Function

`main()` The `main()` function instantiates QApplication and `MainWindow` and invokes the show() function.
The following code example is written in c++.

    int main(int argc, char *argv[])
    {
        Q_INIT_RESOURCE(completer);

        QApplication app(argc, argv);
        MainWindow window;
        window.show();
        return app.exec();
    }