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, FileSystemModel
, 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:
<Code snippet "/tmp/snapshot-qt5full-6.2/qt5/qtbase/tools/completer/completer.qrc" not found>
FileSystemModel Class Definition¶
The FileSystemModel
class is a subclass of QFileSystemModel
, which provides a data model for the local filesystem.
class FileSystemModel(QFileSystemModel): # public FileSystemModel(QObject parent = None) data = QVariant(QModelIndex index, int role = Qt.DisplayRole)
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 QFileSystemModel
‘s data()
function that only returns the folder and not the drive label. This is further explained in FileSystemModel
's implementation.
FileSystemModel Class Implementation¶
The constructor for the FileSystemModel
class is used to pass parent
to QFileSystemModel
.
def __init__(self, parent): QFileSystemModel.__init__(self, 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 QFileSystemModel
, you will see “Program Files” in the view. However, with FileSystemModel
, you will see “C:\Program Files”.
def data(self, QModelIndex index, int role): if (role == Qt.DisplayRole and index.column() == 0) { path = QDir.toNativeSeparators(filePath(index)) if (path.endsWith(QDir.separator())) path.chop(1) return path return QFileSystemModel.data(index, role)
The EditRole
, which QCompleter
uses to look for matches, is left unchanged.
MainWindow Class Definition¶
The MainWindow
class is a subclass of QMainWindow
and implements five private slots - about()
, changeCase()
, changeMode()
, changeModel()
, and changeMaxVisible()
.
class MainWindow(QMainWindow): Q_OBJECT # public MainWindow(QWidget parent = None) slots: = private() def about(): def changeCase(int): def changeMode(int): def changeModel(): def changeMaxVisible(int):
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 def createMenu(): modelFromFile = QAbstractItemModel(QString fileName) caseCombo = None() modeCombo = None() modelCombo = None() maxVisibleSpinBox = None() wrapCheckBox = None() completer = None() contentsLabel = None() lineEdit = None()
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 QFileSystemModel
, the modeCombo
is set to “Filtered Popup” and the caseCombo
is set to “Case Insensitive”.
def __init__(self, parent): QMainWindow.__init__(self, parent) createMenu() centralWidget = QWidget() modelLabel = QLabel() modelLabel.setText(tr("Model")) modelCombo = 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) modeLabel = QLabel() modeLabel.setText(tr("Completion Mode")) modeCombo = QComboBox modeCombo.addItem(tr("Inline")) modeCombo.addItem(tr("Filtered Popup")) modeCombo.addItem(tr("Unfiltered Popup")) modeCombo.setCurrentIndex(1) caseLabel = QLabel() caseLabel.setText(tr("Case Sensitivity")) caseCombo = QComboBox caseCombo.addItem(tr("Case Insensitive")) caseCombo.addItem(tr("Case Sensitive")) caseCombo.setCurrentIndex(0)
The maxVisibleSpinBox
is created and determines the number of visible item in the completer
The wrapCheckBox
is then set up. This checkBox
determines if the completer
's setWrapAround()
property is enabled or disabled.
maxVisibleLabel = QLabel() maxVisibleLabel.setText(tr("Max Visible Items")) maxVisibleSpinBox = QSpinBox maxVisibleSpinBox.setRange(3,25) maxVisibleSpinBox.setValue(10) wrapCheckBox = 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.
contentsLabel = QLabel contentsLabel.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) connect(modelCombo, QComboBox.activated, self, MainWindow::changeModel) connect(modeCombo, QComboBox.activated, self, MainWindow::changeMode) connect(caseCombo, QComboBox.activated, self, MainWindow::changeCase) connect(maxVisibleSpinBox, QSpinBox.valueChanged, self, MainWindow::changeMaxVisible)
The lineEdit
is set up and then we arrange all the widgets using a QGridLayout
. The changeModel()
function is called, to initialize the completer
.
lineEdit = QLineEdit layout = 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()
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.
def createMenu(self): exitAction = QAction(tr("Exit"), self) aboutAct = QAction(tr("About"), self) aboutQtAct = QAction(tr("About Qt"), self) connect(exitAction, QAction.triggered, qApp, QApplication.quit) connect(aboutAct, QAction.triggered, self, MainWindow.about) connect(aboutQtAct, QAction.triggered, qApp, QApplication.aboutQt) fileMenu = menuBar().addMenu(tr("File")) fileMenu.addAction(exitAction) 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
.
MainWindow::modelFromFile = QAbstractItemModel(QString fileName) file = QFile(fileName) if (not file.open(QFile.ReadOnly)) def QStringListModel(completer):
The mouse cursor is then overridden with WaitCursor
before we fill a QStringList
object, words
, with the contents of file
. Once this is done, we restore the mouse cursor.
#ifndef QT_NO_CURSOR QGuiApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) #endif words = QStringList() while (not file.atEnd()) { line = file.readLine() if (not line.isEmpty()) words << QString.fromUtf8(line.trimmed()) #ifndef QT_NO_CURSOR QGuiApplication.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.
if (not fileName.contains("countries.txt")) def 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.
m = 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
.
for i in range(0, words.count()): countryIdx = m.index(i, 0) symbolIdx = m.index(i, 1) country = words.at(i).mid(0, words[i].length() - 2).trimmed() symbol = words.at(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
.
def changeMode(self, index): QCompleter.CompletionMode mode if (index == 0) mode = QCompleter.InlineCompletion elif 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 QFileSystemModel
, providing us with a file path excluding the drive label.
def changeModel(self): del completer completer = QCompleter(self) completer.setMaxVisibleItems(maxVisibleSpinBox.value()) switch (modelCombo.currentIndex()) { default: case 0: { // Unsorted QFileSystemModel fsModel = QFileSystemModel(completer) fsModel.setRootPath(QString()) completer.setModel(fsModel) 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.
case 1: { // FileSystemModel that shows full paths fsModel = FileSystemModel(completer) completer.setModel(fsModel) fsModel.setRootPath(QString()) 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
.
case 2: { // Country List completer.setModel(modelFromFile(":/resources/countries.txt")) treeView = 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
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.
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)
The changeMaxVisible()
update the maximum number of visible items in the completer.
def changeMaxVisible(self, max): completer.setMaxVisibleItems(max)
The about()
function provides a brief description about the example.
def about(self): QMessageBox.about(self, tr("About"), tr("This example demonstrates the " "different features of the QCompleter class."))
`` main()``
Function¶
The main()
function instantiates QApplication
and MainWindow
and invokes the show()
function.
if __name__ == "__main__": Q_INIT_RESOURCE(completer) app = QApplication([]) window = MainWindow() window.show() sys.exit(app.exec())
© 2022 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.