树形模型补全示例
“树模型补全”示例演示了如何为分层模型提供补全功能,其中使用句点作为分隔符来访问子、孙和曾孙级别的对象。

与“补全示例”类似,我们提供了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 该类还具有一个 separator 属性,该属性使用Q_PROPERTY() 宏进行声明。separator 具有 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;
}如前所述,splitPath() 函数被重新实现,因为其默认实现更适合QFileSystemModel 或列表模型。为了让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 的模式为“Filtered Popup”,且不区分大小写。
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() 函数会根据cs 的值,在Case Sensitive 和Case Insensitive 模式之间切换。
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();
}© 2026 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.