树模型补全示例
树模型补全示例展示了如何为层次模型提供补全功能,使用句点作为分隔符以访问子、孙子及曾孙级别的对象。
类似于补全示例,我们提供了QComboBox对象以启用完成模式的选项以及大小写敏感,同时还有一个QCheckBox用于换行补全。
资源文件
TreeModelCompleter的内容从treemodel.txt读取。该文件嵌入在treemodelcompleter.qrc资源文件中,包含以下内容
<!DOCTYPE RCC><RCC version="1.0"> <qresource prefix="/"> <file>resources/treemodel.txt</file> </qresource> </RCC>
TreeModelCompleter类定义
TreeModelCompleter
是QCompleter的子类,有两个构造函数 - 一个接受parent作为参数,另一个接受parent和model作为参数。
class TreeModelCompleter : public QCompleter { Q_OBJECT Q_PROPERTY(QString separator READ separator WRITE setSeparator) public: explicit TreeModelCompleter(QObject *parent = nullptr); explicit TreeModelCompleter(QAbstractItemModel *model, QObject *parent = nullptr); QString separator() const; public slots: void setSeparator(const QString &separator); protected: QStringList splitPath(const QString &path) const override; QString pathFromIndex(const QModelIndex &index) const override; private: QString sep; };
该类重新实现了受保护的函数splitPath() 和 pathFromIndex() 以适应树模型。有关如何自定义QCompleter以适应树模型的信息,请参阅处理树模型。
TreeModelCompleter
还有一个分隔符属性,使用Q_PROPERTY() 宏声明。分隔符具有READ和WRITE属性,对应的功能为separator()
和setSeparator()
。有关Q_PROPERTY()的更多信息,请参阅Qt的属性系统。
TreeModelCompleter类实现
第一个构造函数构建了一个具有父级的TreeModelCompleter
对象,而第二个构造函数构建了一个具有父级和一个QAbstractItemModel,model的对象。
TreeModelCompleter::TreeModelCompleter(QObject *parent) : QCompleter(parent) { } TreeModelCompleter::TreeModelCompleter(QAbstractItemModel *model, QObject *parent) : QCompleter(model, parent) { }
separator()
函数是一个获取函数,返回分隔符字符串。
QString TreeModelCompleter::separator() const { return sep; }
如前所述,由于默认实现更适合QFileSystemModel或列表模型,我们重新实现了splitPath()
函数。为了使QCompleter能够将路径分割成在每一级都匹配的字符串列表,我们使用具有sep
作为分隔符的QString::split()进行分割。
QStringList TreeModelCompleter::splitPath(const QString &path) const { return (sep.isNull() ? QCompleter::splitPath(path) : path.split(sep)); }
函数 pathFromIndex()
返回树模型的 completionRole() 数据。此函数被重新实现,因为其默认实现更适合列表模型。如果没有分隔符,我们使用 QCompleter 的默认实现;否则,我们使用 prepend() 函数向上导航并累积数据。然后该函数返回一个使用分隔符连接不同级别的对象的 QStringList,dataList
。
QString TreeModelCompleter::pathFromIndex(const QModelIndex &index) const { if (sep.isNull()) return QCompleter::pathFromIndex(index); // navigate up and accumulate data QStringList dataList; for (QModelIndex i = index; i.isValid(); i = i.parent()) dataList.prepend(model()->data(i, completionRole()).toString()); return dataList.join(sep); }
MainWindow 类定义
MainWindow
类是 QMainWindow 的子类,并实现了五个自定义槽:about()
、changeCase()
、changeMode()
、highlight()
以及 updateContentsLabel()
。
class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); private slots: void about(); void changeCase(int); void changeMode(int); void highlight(const QModelIndex &index); void updateContentsLabel(const QString &sep);
此外,该类还有两个私有函数 createMenu()
和 modelFromFile()
,以及私有实例 QTreeView、QComboBox、QLabel、TreeModelCompleter
和 QLineEdit。
private: void createMenu(); QAbstractItemModel *modelFromFile(const QString &fileName); QTreeView *treeView = nullptr; QComboBox *caseCombo = nullptr; QComboBox *modeCombo = nullptr; QLabel *contentsLabel = nullptr; TreeModelCompleter *completer = nullptr; QLineEdit *lineEdit = nullptr; };
MainWindow 类实现
MainWindow
的构造函数创建一个具有父对象的 MainWindow
对象,并初始化 completer
和 lineEdit
。调用 createMenu()
函数来设置 "文件" 菜单和 "帮助" 菜单。将 completer
的模型设置为从 modelFromFile()
获得的 QAbstractItemModel,并将 highlighted() 信号连接到 MainWindow
的 highlight()
槽。
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { createMenu(); completer = new TreeModelCompleter(this); completer->setModel(modelFromFile(":/resources/treemodel.txt")); completer->setSeparator(QLatin1String(".")); QObject::connect(completer, QOverload<const QModelIndex &>::of(&TreeModelCompleter::highlighted), this, &MainWindow::highlight); QWidget *centralWidget = new QWidget; QLabel *modelLabel = new QLabel; modelLabel->setText(tr("Tree Model<br>(Double click items to edit)")); 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);
实例化了 QLabel 对象 modelLabel
、modeLabel
和 caseLabel
。还实例化了并填充了 QComboBox 对象 modeCombo
和 caseCombo
。默认情况下,completer
的模式是 "过滤弹出",而大小写不敏感。
QLabel *separatorLabel = new QLabel; separatorLabel->setText(tr("Tree Separator")); QLineEdit *separatorLineEdit = new QLineEdit; separatorLineEdit->setText(completer->separator()); connect(separatorLineEdit, &QLineEdit::textChanged, completer, &TreeModelCompleter::setSeparator); QCheckBox *wrapCheckBox = new QCheckBox; wrapCheckBox->setText(tr("Wrap around completions")); wrapCheckBox->setChecked(completer->wrapAround()); connect(wrapCheckBox, &QAbstractButton::clicked, completer, &QCompleter::setWrapAround); contentsLabel = new QLabel; contentsLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); connect(separatorLineEdit, &QLineEdit::textChanged, this, &MainWindow::updateContentsLabel); treeView = new QTreeView; treeView->setModel(completer->model()); treeView->header()->hide(); treeView->expandAll(); connect(modeCombo, &QComboBox::activated, this, &MainWindow::changeMode); connect(caseCombo, &QComboBox::activated, this, &MainWindow::changeMode); lineEdit = new QLineEdit; lineEdit->setCompleter(completer);
我们使用一个 QGridLayout 将所有对象放置在 MainWindow
中。
QGridLayout *layout = new QGridLayout; layout->addWidget(modelLabel, 0, 0); layout->addWidget(treeView, 0, 1); layout->addWidget(modeLabel, 1, 0); layout->addWidget(modeCombo, 1, 1); layout->addWidget(caseLabel, 2, 0); layout->addWidget(caseCombo, 2, 1); layout->addWidget(separatorLabel, 3, 0); layout->addWidget(separatorLineEdit, 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); changeCase(caseCombo->currentIndex()); changeMode(modeCombo->currentIndex()); setWindowTitle(tr("Tree Model Completer")); lineEdit->setFocus(); }
createMenu()
函数设置了必要的 QAction 对象,并将它们添加到 "文件" 菜单和 "帮助" 菜单中。这些操作的动作 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); }
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); }
about()
函数提供了 Tree Model Completer 示例的简要描述。
void MainWindow::about() { QMessageBox::about(this, tr("About"), tr("This example demonstrates how " "to use a QCompleter with a custom tree model.")); }
changeCase()
函数在 大小写敏感 和 大小写不敏感 模式之间切换,取决于 cs 的值。
void MainWindow::changeCase(int cs) { completer->setCaseSensitivity(cs ? Qt::CaseSensitive : Qt::CaseInsensitive); }
main()
函数
main()
函数实例化 MainWindow
并调用 show() 函数来显示它。
int main(int argc, char *argv[]) { QApplication app(argc, argv); MainWindow window; window.show(); return app.exec(); }
© 2024 Qt 公司 Ltd。此处包含的文档贡献是各自所有者的版权。此处提供的文档根据自由软件基金会的 GNU 自由文档许可证版本 1.3 的条款提供。Qt 和相应的标志是芬兰及其在全世界各地的 The Qt Company Ltd. 的 商标。所有其他商标均为各自所有者的财产。