Completer 示例
Completer 示例展示了如何在模型提供的数据基础上,为输入小部件提供字符串自动完成功能。
本示例使用自定义项模型 FileSystemModel
和一个 QCompleter 对象。 QCompleter 是一个提供基于项模型自动完成的类。可以使用下拉菜单选择模型类型、完成模式和大小写敏感度。
资源文件
为了存储 countries.txt 和 words.txt,Completer 示例需要一个资源文件。资源文件包含以下代码
<!DOCTYPE RCC><RCC version="1.0"> <qresource prefix="/"> <file>resources/countries.txt</file> <file>resources/wordlist.txt</file> </qresource> </RCC>
FileSystemModel 类定义
FileSystemModel
类是 QFileSystemModel 的子类,为本地文件系统提供数据模型。
class FileSystemModel : public QFileSystemModel { public: FileSystemModel(QObject *parent = nullptr); QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; };
此类仅有一个构造函数和一个 data()
函数,因为它只创造用于使 data()
返回整个文件路径以供显示角色使用,这与仅返回文件夹而不返回驱动器标签的 QFileSystemModel 的 data()
函数不同。这在 FileSystemModel
的实现中进一步解释。
FileSystemModel 类实现
FileSystemModel
类的构造函数用于将 parent 传递给 QFileSystemModel。
FileSystemModel::FileSystemModel(QObject *parent) : QFileSystemModel(parent) { }
如前所述,重新实现了 data()
函数,以便使其为显示角色返回整个文件路径。例如,使用 QFileSystemModel,你将在视图中看到 "Program Files"。然而,使用 FileSystemModel
,你将看到 "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); }
使用来寻找匹配项的 Qt::EditRole,未做更改。
MainWindow 类定义
MainWindow
类是 QMainWindow 的子类,并实现了五个私有槽 - about()
、changeCase()
、changeMode()
、changeModel()
和 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);
在 MainWindow
类中,我们有两个私有函数:createMenu()
和 modelFromFile()
。我们还声明了所需的私有小部件 - 三个 QComboBox 对象、一个 QCheckBox、一个 QCompleter、一个 QLabel 和一个 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; };
MainWindow 类实现
MainWindow
的构造函数创建一个具有父窗口小部件的 MainWindow
并初始化私有成员。然后调用 createMenu()
函数。
我们设置了三个 QComboBox 对象:modelComb
,modeCombo
和 caseCombo
。默认情况下,modelComb
被设置为 QFileSystemModel,modeCombo
被设置为 "Filtered Popup",caseCombo
被设置为 "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);
创建了 maxVisibleSpinBox
并确定补全器中可见项的数量。
然后设置了 wrapCheckBox
。此 checkBox
决定 completer
的 setWrapAround() 属性是启用还是禁用。
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);
实例化了 contentsLabel
并将其大小策略设置为 fixed。然后连接组合框的 activated() 信号到相应的槽。
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);
设置了 lineEdit
并使用一个 QGridLayout 来安排所有小部件。然后调用 changeModel()
函数以初始化 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(); }
使用 createMenu()
函数实例化 QAction 对象,以填充 fileMenu
和 helpMenu
。将动作的 triggered() 信号连接到相应的槽。
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); }
modelFromFile()
函数接受一个文件的 fileName 并根据其内容进行处理。
我们首先验证 file
以确保它可以以 QFile::ReadOnly 模式打开。如果这不成功,函数返回一个空的 QStringListModel。
QAbstractItemModel *MainWindow::modelFromFile(const QString &fileName) { QFile file(fileName); if (!file.open(QFile::ReadOnly)) return new QStringListModel(completer);
在我们填充一个 QStringList 对象 words
,其中包含 file
的内容之前,使用 Qt::WaitCursor 覆盖鼠标光标。完成后,我们恢复鼠标光标。
#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
如前所述,资源文件包含两个文件 - countries.txt 和 words.txt。如果读取的是 words.txt 文件,我们返回一个具有 words
作为它的 QStringList 和 completer
作为父级的 QStringListModel。
if (!fileName.contains(QLatin1String("countries.txt"))) return new QStringListModel(words, completer);
如果读取的是 countries.txt 文件,我们则需要一个具有 words.count()
行,2 列,并将 completer
作为其父级的 QStandardItemModel。
QStandardItemModel *m = new QStandardItemModel(words.count(), 2, completer);
countries.txt 中的一个标准行是:
挪威 NO
因此,要填充 QStandardItemModel 对象 m
,我们必须分割国家名称和其符号。这样,我们返回 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; }
changeMode()
函数根据 index
的值设置 completer
的模式。
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); }
changeModel()
函数根据用户选择的模型更改用于的项目模型。
使用 switch
语句根据 modelCombo
的索引更改项目模型。如果 case
为 0,我们使用一个未排序的 QFileSystemModel,提供我们一个不含驱动器标签的文件路径。
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;
请注意,我们使用completer
作为父节点创建模型,因为这允许我们用新模型替换旧模型。当将新的模型分配给completer
时,它会确保删除旧模型。
如果case
为1,我们使用之前定义的DirModel
,生成文件的完整路径。
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;
当case
为2时,我们尝试完成国家名称。这需要一个treeView
。国家名称从countries.txt中提取,并将用于显示补全的弹出窗口设置为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;
下方的截图显示了具有国家列表模型的Completer。
如果case
为3,我们尝试完成单词。这使用包含从words.txt中提取的数据的QStringListModel完成,模型按不区分大小写地排序。
下方的截图显示了具有单词列表模型的Completer。
一旦选择模型类型,我们调用changeMode()
函数和changeCase()
函数,并相应地设置包裹选项。将wrapCheckBox
的clicked()信号连接到completer
的setWrapAround()槽。
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); }
changeMaxVisible()
函数更新Completer中可见的最大项目数量。
void MainWindow::changeMaxVisible(int max) { completer->setMaxVisibleItems(max); }
about()
函数提供了关于该示例的简要描述。
void MainWindow::about() { QMessageBox::about(this, tr("About"), tr("This example demonstrates the " "different features of the QCompleter class.")); }
main()
函数
main()
函数实例化QApplication和MainWindow
,并调用show()函数。
int main(int argc, char *argv[]) { QApplication app(argc, argv); MainWindow window; window.show(); return app.exec(); }
© 2024 Qt公司有限公司。文档贡献的版权属于各自的拥有者。提供的文档受GNU自由文档许可版本1.3的条款约束,由自由软件基金会发布。Qt和相应的标志是芬兰和/或其他国家的Qt公司之商标。所有其他商标均为其各自所有者的财产。