树形模型补全器示例
树形模型补全器示例展示了如何为分层模型提供补全功能,使用句号作为分隔符来访问子代、孙代和外孙代级别的对象。
与完形填空示例类似,我们提供了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 以适应树模型的更多信息,请参阅Handling Tree Models 。
TreeModelCompleter
该类还有一个分隔符属性,使用 () 宏声明。分隔符具有 READ 和 WRITE 属性以及相应的函数 和 。有关 () 的更多信息,请参阅Q_PROPERTY separator()
setSeparator()
Q_PROPERTYQt XML 的属性系统。
TreeModelCompleter 类的实现
第一个构造函数构造一个带有父对象的TreeModelCompleter
对象,第二个构造函数构造一个带有父对象和QAbstractItemModel,model 的对象。
TreeModelCompleter::TreeModelCompleter(QObject *parent) : QCompleter(parent) { } TreeModelCompleter::TreeModelCompleter(QAbstractItemModel *model, QObject *parent) : QCompleter(model, parent) { }
separator()
函数是一个 getter 函数,用于返回分隔符字符串。
QString TreeModelCompleter::separator() const { return sep; }
如前所述,由于默认实现更适合QFileSystemModel 或列表模型,因此重新实现了splitPath()
函数。为了让QCompleter 将路径拆分成每一级都匹配的字符串列表,我们使用QString::split() 将其拆分,并将sep
作为分隔符。
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()
函数简要介绍了树模型完成器示例。
void MainWindow::about() { QMessageBox::about(this, tr("About"), tr("This example demonstrates how " "to use a QCompleter with a custom tree model.")); }
changeCase()
函数在Case Sensitive 和Case Insensitive 模式之间交替运行,具体取决于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(); }
© 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.