警告
本节包含从 C++ 自动翻译到 Python 的代码片段,可能包含错误。
完成示例#
完成示例展示了如何根据模型提供的数据为输入小部件提供字符串补全功能。
本示例使用自定义项模型 FileSystemModel
和 QCompleter
对象。 QCompleter
是一个基于项模型提供补全功能的类。可以使用组合框选择模型类型、补全模式和大小写敏感度。
资源文件#
完成示例需要资源文件来存储 countries.txt 和 words.txt。资源文件包含以下代码
<Code snippet "/data/qt5-full-670/6.7.0/Src/qtbase/tools/completer/completer.qrc" not found>
FileSystemModel 类定义#
FileSystemModel 类是 QFileSystemModel 的子类,提供本地文件系统的数据模型。
class FileSystemModel(QFileSystemModel): # public FileSystemModel(QObject parent = None) QVariant data(QModelIndex index, int role = Qt.DisplayRole) override
此类仅有一个构造函数和一个 data()
函数,因为它只被创建来使 data()
能够返回显示角色的整个文件路径,而 QFileSystemModel 的 data()
函数仅返回文件夹,而不包括驱动器标签。这在与 FileSystemModel 的实现部分中进一步解释。
FileSystemModel 类实现#
FileSystemModel 类的构造函数用于将 parent
传递给 QFileSystemModel。
def __init__(self, parent): super().__init__(parent)
如前所述,重新实现了 data()
函数,使其返回显示角色的完整文件路径。例如,使用 QFileSystemModel,您将在视图中看到“Program Files”。然而,在 FileSystemModel 中,您将看到“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)
Qt::EditRole,它是 QCompleter 用于查找匹配项的role,保持不变。
MainWindow 类定义#
MainWindow
类是 QMainWindow
的子类,并实现了五个私有槽 —— about()
、changeCase()
、changeMode()
、changeModel()
和 changeMaxVisible()
。
class MainWindow(QMainWindow): Q_OBJECT # public MainWindow(QWidget parent = None) # private slots def about(): def changeCase(int): def changeMode(int): def changeModel(): def changeMaxVisible(int):
在 MainWindow
类中,我们有两个私有函数: createMenu()
和 modelFromFile()
。我们同时还声明了所需的私有小部件 - 三个 QComboBox
对象,一个 QCheckBox
,一个 QCompleter
,一个 QLabel
,和一个 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 类实现#
MainWindow
的构造函数创建一个带有父小部件的 MainWindow
并初始化私有成员。然后调用 createMenu()
函数。
我们设置了三个 QComboBox
对象,modelComb
、modeCombo
和 caseCombo
。默认情况下,modelComb
设置为 QFileSystemModel,modeCombo
设置为“过滤弹出”,caseCombo
设置为“忽略大小写”。
def __init__(self, parent): super().__init__(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)
创建了 maxVisibleSpinBox
并确定了补全器中可见项的数量。
然后设置了 wrapCheckBox
。这个复选框决定是否启用或禁用 completer
的 setWrapAround()
属性。
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)
实例化了 contentsLabel
并将其大小策略设置为 fixed。然后将选项框的 activated()
信号连接到对应的槽。
contentsLabel = QLabel() contentsLabel.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) modelCombo.activated.connect( self.changeModel) modeCombo.activated.connect( self.changeMode) caseCombo.activated.connect( self.changeCase) maxVisibleSpinBox.valueChanged.connect( self.changeMaxVisible)
设置 lineEdit
,然后使用一个 QGridLayout
布局来排列所有的控件。调用 changeModel()
函数以初始化 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()
使用 createMenu()
函数来创建填充 fileMenu
和 helpMenu
所需的 QAction 对象。将这些动作的 triggered() 信号连接到各自的槽。
def createMenu(self): exitAction = QAction(tr("Exit"), self) aboutAct = QAction(tr("About"), self) aboutQtAct = QAction(tr("About Qt"), self) exitAction.triggered.connect(qApp.quit) aboutAct.triggered.connect(self.about) aboutQtAct.triggered.connect(qApp.aboutQt) fileMenu = menuBar().addMenu(tr("File")) fileMenu.addAction(exitAction) helpMenu = menuBar().addMenu(tr("About")) helpMenu.addAction(aboutAct) helpMenu.addAction(aboutQtAct)
modelFromFile()
函数接受一个文件的 fileName
,并根据其内容进行处理。
首先验证 file
是否可以以 QFile::ReadOnly 模式打开,如果失败,则函数返回一个空的 QStringListModel。
QAbstractItemModel MainWindow.modelFromFile(QString fileName) file = QFile(fileName) if not file.open(QFile.ReadOnly): return QStringListModel(completer)
在用文件内容填充 QStringList 对象 words
之前,我们将鼠标光标覆盖为 Qt::WaitCursor。完成此操作后,我们恢复鼠标光标。
#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
如前所述,资源文件包含两个文件 - countries.txt 和 words.txt。如果读取的文件是 words.txt,则返回一个以 words
作为它的 QStringList,并且 completer
作为其父代的 QStringListModel。
if not fileName.contains("countries.txt"): return QStringListModel(words, completer)
如果读取的文件是 countries.txt,那么我们需要一个具有 words.count()
行,2 列,并且以 completer
作为其父代的 QStandardItemModel。
m = QStandardItemModel(words.count(), 2, completer)
countries.txt 的标准行是
挪威 NO
因此,为了填充 QStandardItemModel 对象 m
,我们必须分割国家和它的符号。完成此操作后,返回 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
changeMode()
函数根据 index
的值设置 completer
的模式。
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)
changeModel()
函数根据用户选择的模型来更改所用的项模型。
使用 switch
语句根据 modelCombo
的索引来更改项模型。如果为 case
0,我们使用未排序的 QFileSystemModel,提供不带驱动器标签的文件路径。
def changeModel(self): del completer completer = QCompleter(self) completer.setMaxVisibleItems(maxVisibleSpinBox.value()) switch (modelCombo.currentIndex()) { else: elif role == 0: { // Unsorted QFileSystemModel fsModel = QFileSystemModel(completer) fsModel.setRootPath(QString()) completer.setModel(fsModel) contentsLabel.setText(tr("Enter file path")) break
请注意,我们以 completer
作为父代创建模型,因为这允许我们用新模型替换旧模型。当将新模型分配给它时,completer
将确保删除旧模型。
如果 case
是 1,我们使用我们之前定义的 DirModel
,结果生成文件的完整路径。
elif role == 1: { // FileSystemModel that shows full paths fsModel = FileSystemModel(completer) completer.setModel(fsModel) fsModel.setRootPath(QString()) contentsLabel.setText(tr("Enter file path")) break
当 case
为 2 时,我们尝试完成国家名称。这需要一个 QTreeView
对象, treeView
。国家名称从 countries.txt 中提取,并将用于显示补全内容的弹出窗口设置为 treeView
。
elif role == 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
下面的截图显示了具有国家列表模型的补全器。
如果 case
为 3,我们尝试完成单词。这是通过使用包含从 words.txt 提取的数据的 QStringListModel 来完成的。该模型不区分大小写进行排序 case insensitively
。
下面的截图显示了具有单词列表模型的补全器。
一旦选择了模型类型,我们就调用 changeMode()
函数和 changeCase()
函数,并相应地设置 wrap 选项。将 wrapCheckBox
的 clicked()
信号连接到 completer
的 setWrapAround()
穿槽。
elif role == 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) wrapCheckBox.clicked.connect(completer.setWrapAround)
changeMaxVisible()
更新补全器中可见项的最大数量。
def changeMaxVisible(self, max): completer.setMaxVisibleItems(max)
about()
函数提供了关于该示例的简要描述。
def about(self): QMessageBox.about(self, tr("About"), tr("This example demonstrates the " "different features of the QCompleter class."))
`` main()
``
函数#
main()
函数实例化了 QApplication
和 MainWindow
,并调用 show()
函数。
if __name__ == "__main__": app = QApplication([]) window = MainWindow() window.show() sys.exit(app.exec())