树模型补全示例

树模型补全示例展示了如何为层次模型提供补全功能,使用句点作为分隔符以访问子、孙子及曾孙级别的对象。

类似于补全示例,我们提供了QComboBox对象以启用完成模式的选项以及大小写敏感,同时还有一个QCheckBox用于换行补全。

资源文件

TreeModelCompleter的内容从treemodel.txt读取。该文件嵌入在treemodelcompleter.qrc资源文件中,包含以下内容

<!DOCTYPE RCC><RCC version="1.0">
<qresource prefix="/">
   <file>resources/treemodel.txt</file>
</qresource>
</RCC>

TreeModelCompleter类定义

TreeModelCompleterQCompleter的子类,有两个构造函数 - 一个接受parent作为参数,另一个接受parentmodel作为参数。

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对象,而第二个构造函数构建了一个具有父级和一个QAbstractItemModelmodel的对象。

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() 函数向上导航并累积数据。然后该函数返回一个使用分隔符连接不同级别的对象的 QStringListdataList

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(),以及私有实例 QTreeViewQComboBoxQLabelTreeModelCompleterQLineEdit

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 对象,并初始化 completerlineEdit。调用 createMenu() 函数来设置 "文件" 菜单和 "帮助" 菜单。将 completer 的模型设置为从 modelFromFile() 获得的 QAbstractItemModel,并将 highlighted() 信号连接到 MainWindowhighlight() 槽。

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 对象 modelLabelmodeLabelcaseLabel。还实例化了并填充了 QComboBox 对象 modeCombocaseCombo。默认情况下,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();
}

示例项目 @ code.qt.io

© 2024 Qt 公司 Ltd。此处包含的文档贡献是各自所有者的版权。此处提供的文档根据自由软件基金会的 GNU 自由文档许可证版本 1.3 的条款提供。Qt 和相应的标志是芬兰及其在全世界各地的 The Qt Company Ltd. 的 商标。所有其他商标均为各自所有者的财产。