Completer-Beispiel

Das Completer-Beispiel zeigt, wie man die Vervollständigung von Zeichenketten für ein Eingabewidget auf der Grundlage der von einem Modell bereitgestellten Daten ermöglicht.

Dieses Beispiel verwendet ein benutzerdefiniertes Elementmodell, FileSystemModel, und ein QCompleter Objekt. QCompleter ist eine Klasse, die Vervollständigungen basierend auf einem Elementmodell bereitstellt. Die Art des Modells, der Vervollständigungsmodus und die Groß- und Kleinschreibung können über Kombinationsfelder ausgewählt werden.

Die Ressourcendatei

Für das Completer-Beispiel wird eine Ressourcendatei benötigt, um die countries.txt und words.txt zu speichern. Die Ressourcendatei enthält den folgenden Code:

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

FileSystemModel Klassendefinition

Die Klasse FileSystemModel ist eine Unterklasse von QFileSystemModel, die ein Datenmodell für das lokale Dateisystem bereitstellt.

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

Diese Klasse hat nur einen Konstruktor und eine Funktion data(), da sie nur erstellt wurde, um data() in die Lage zu versetzen, den gesamten Dateipfad für die Anzeigerolle zurückzugeben, im Gegensatz zu QFileSystemModel's data() Funktion, die nur den Ordner und nicht die Laufwerksbezeichnung zurückgibt. Dies wird in der Implementierung von FileSystemModel näher erläutert.

Implementierung der FileSystemModel-Klasse

Der Konstruktor für die Klasse FileSystemModel wird verwendet, um parent an QFileSystemModel zu übergeben.

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

Wie bereits erwähnt, wird die Funktion data() reimplementiert, damit sie den gesamten Dateipfad für die Anzeigerolle zurückgibt. Mit QFileSystemModel sehen Sie zum Beispiel "Programme" in der Ansicht. Mit FileSystemModel wird jedoch "C:\Programmdateien" angezeigt.

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

Die Qt::EditRole, die QCompleter für die Suche nach Übereinstimmungen verwendet, bleibt unverändert.

Definition der Klasse MainWindow

Die Klasse MainWindow ist eine Unterklasse von QMainWindow und implementiert fünf private Slots - about(), changeCase(), changeMode(), changeModel(), und 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);

Innerhalb der Klasse MainWindow haben wir zwei private Funktionen: createMenu() und modelFromFile(). Wir deklarieren auch die benötigten privaten Widgets - drei QComboBox Objekte, ein QCheckBox, ein QCompleter, ein QLabel und ein 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;
};

Implementierung der MainWindow-Klasse

Der Konstruktor von MainWindow konstruiert ein MainWindow mit einem übergeordneten Widget und initialisiert die privaten Mitglieder. Anschließend wird die Funktion createMenu() aufgerufen.

Wir richten drei QComboBox Objekte ein, modelComb, modeCombo und caseCombo. Standardmäßig ist modelCombo auf QFileSystemModel, modeCombo auf "Filtered Popup" und caseCombo auf "Case Insensitive" eingestellt.

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

Die maxVisibleSpinBox wird erstellt und bestimmt die Anzahl der sichtbaren Elemente im Completter

Anschließend wird die wrapCheckBox erstellt. Diese checkBox bestimmt, ob die Eigenschaft setWrapAround() von completer aktiviert oder deaktiviert ist.

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

Wir instanziieren contentsLabel und setzen seine Größenpolitik auf fixed. Die Signale der Kombinationsfelder activated() werden dann mit ihren jeweiligen Steckplätzen verbunden.

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

Die lineEdit wird eingerichtet und dann ordnen wir alle Widgets mit einer QGridLayout an. Die changeModel() Funktion wird aufgerufen, um die completer zu initialisieren.

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

Die Funktion createMenu() wird verwendet, um die QAction Objekte zu instanziieren, die benötigt werden, um die fileMenu und helpMenu zu füllen. Die triggered()-Signale der Aktionen sind mit ihren jeweiligen Slots 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 die fileName einer Datei entgegen und verarbeitet sie abhängig von ihrem Inhalt.

Zunächst wird die file validiert, um sicherzustellen, dass sie im Modus QFile::ReadOnly geöffnet werden kann. Ist dies nicht der Fall, gibt die Funktion ein leeres QStringListModel zurück.

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

Der Mauszeiger wird dann mit Qt::WaitCursor überschrieben, bevor wir ein QStringList Objekt, words, mit dem Inhalt von file füllen. Danach stellen wir den Mauszeiger wieder her.

#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

Wie bereits erwähnt, enthält die Ressourcendatei zwei Dateien - countries.txt und words.txt. Wenn file words.txt lautet, geben wir QStringListModel mit words als QStringList und completer als übergeordnetes Element zurück.

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

Wenn file countries.txt gelesen wird, benötigen wir QStandardItemModel mit words.count() Zeilen, 2 Spalten und completer als übergeordnetes Element.

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

Eine Standardzeile in countries.txt ist:

Norwegen NEIN

Um das Objekt QStandardItemModel, m, zu füllen, müssen wir also den Ländernamen und sein Symbol aufteilen. Sobald dies geschehen ist, geben wir m zurück.

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

Mit der Funktion changeMode() wird der Modus von completer in Abhängigkeit vom Wert von index eingestellt.

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

Die Funktion changeModel() ändert das verwendete Elementmodell auf der Grundlage des vom Benutzer ausgewählten Modells.

Eine switch -Anweisung wird verwendet, um das Elementmodell basierend auf dem Index von modelCombo zu ändern. Wenn case gleich 0 ist, wird ein unsortiertes QFileSystemModel verwendet, das uns einen Dateipfad ohne die Laufwerksbezeichnung liefert.

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;

Beachten Sie, dass wir das Modell mit completer als übergeordnetes Modell erstellen, da dies uns erlaubt, das Modell durch ein neues Modell zu ersetzen. Die completer sorgt dafür, dass das alte Modell gelöscht wird, sobald ihm ein neues Modell zugewiesen wird.

Wenn case gleich 1 ist, verwenden wir DirModel, das wir zuvor definiert haben, was zu vollständigen Pfaden für die Dateien führt.

    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;

Wenn case gleich 2 ist, versuchen wir, die Namen der Länder zu vervollständigen. Dazu ist ein Objekt QTreeView erforderlich, treeView. Die Ländernamen werden aus countries.txt extrahiert und das Popup, das zur Anzeige der Vervollständigungen verwendet wird, auf treeView gesetzt.

    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;

Der folgende Screenshot zeigt den Completer mit dem Länderlistenmodell.

Wenn case gleich 3 ist, versuchen wir, Wörter zu vervollständigen. Dies geschieht mit Hilfe eines QStringListModel, das Daten aus words.txt enthält. Das Modell ist nach case insensitively sortiert.

Der unten stehende Screenshot zeigt den Completer mit dem Wortlistenmodell.

Sobald der Modelltyp ausgewählt ist, rufen wir die Funktionen changeMode() und changeCase() auf und setzen die Option wrap entsprechend. Das Signal clicked() von wrapCheckBox ist mit dem Steckplatz setWrapAround() von completer verbunden.

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

Die Funktion changeMaxVisible() aktualisiert die maximale Anzahl der sichtbaren Elemente im Completer.

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

Die Funktion about() liefert eine kurze Beschreibung des 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 QApplication und 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.