Qt中自定义只读TreeView的Model

通过学习Qt自带的例子和帮助文档,简化了其中的一个例子,来学习如何自定义TreeView的Model。
以下是自定义一个只读的TreeView的Model,实际中可以直接复制过来使用,避免重复造轮子。
官方的文档一直都是很好的学习资料,尤其是官方的例子和help文档。本文参考了官方的Simple Tree Model Example例子。
在这里插入图片描述
这是简单的树状结构,可以看出行的结构,但是缺乏列的信息。
在这里插入图片描述
在这里插入图片描述
这样我们就清楚treeitem中的成员变量:QList m_itemData;中存储的链表的情况了。

treeitem.h文件:

#ifndef TREEITEM_H
#define TREEITEM_H

#include <QList>
#include <QVariant>

class TreeItem
{
public:
    explicit TreeItem(const QList<QVariant> &data, TreeItem *parentItem = nullptr);
    ~TreeItem();

    void appendChild(TreeItem *child);

    TreeItem *child(int row);
    int childCount() const;
    int columnCount() const;
    QVariant data(int column) const;
    int row() const;
    TreeItem *parentItem();

private:
    QList<TreeItem*> m_childItems;
    QList<QVariant> m_itemData;
    TreeItem *m_parentItem;
};

#endif // TREEITEM_H

treeitem.cpp文件:

#include <QStringList>
#include "treeitem.h"

TreeItem::TreeItem(const QList<QVariant> &data, TreeItem *parent)
{
    m_parentItem = parent;
    m_itemData = data;
}

TreeItem::~TreeItem()
{
    qDeleteAll(m_childItems);
}

void TreeItem::appendChild(TreeItem *item)
{
    m_childItems.append(item);
}

TreeItem *TreeItem::child(int row)
{
    return m_childItems.value(row);
}

int TreeItem::childCount() const
{
    return m_childItems.count();
}

int TreeItem::columnCount() const
{
    return m_itemData.count();
}

QVariant TreeItem::data(int column) const
{
    return m_itemData.value(column);
}

TreeItem *TreeItem::parentItem()
{
    return m_parentItem;
}

int TreeItem::row() const
{
    if (m_parentItem)
        return m_parentItem->m_childItems.indexOf(const_cast<TreeItem*>(this));

    return 0;
}

treemodel.h文件:

#ifndef TREEMODEL_H
#define TREEMODEL_H

#include <QAbstractItemModel>
#include <QModelIndex>
#include <QVariant>

class TreeItem;

class TreeModel : public QAbstractItemModel
{
    Q_OBJECT

public:
    explicit TreeModel(QObject *parent = nullptr);
    ~TreeModel() override;

    QVariant data(const QModelIndex &index, int role) const override;
    Qt::ItemFlags flags(const QModelIndex &index) const override;
    QVariant headerData(int section, Qt::Orientation orientation,
                        int role = Qt::DisplayRole) const override;
    QModelIndex index(int row, int column,
                      const QModelIndex &parent = QModelIndex()) const override;
    QModelIndex parent(const QModelIndex &index) const override;
    int rowCount(const QModelIndex &parent = QModelIndex()) const override;
    int columnCount(const QModelIndex &parent = QModelIndex()) const override;

private:

    TreeItem *rootItem;
};
#endif // TREEMODEL_H

treemodel.cpp文件:

#include "treeitem.h"
#include "treemodel.h"

TreeModel::TreeModel(QObject *parent)
    : QAbstractItemModel(parent)
{
    //以下代码都是为了装填数据用,实际中需要另外写接口加载数据
    QList<QVariant> rootData;
    rootData << "Title" << "summary";
    rootItem = new TreeItem(rootData);

    QList<QVariant> gettingStartedData;
    gettingStartedData << "Getting Started" << "How to familiarize yourself with Qt Designer";
    TreeItem * gettingStartedItem = new TreeItem(gettingStartedData, rootItem);
    rootItem->appendChild(gettingStartedItem);

    QList<QVariant> launchingDesignerData;
    launchingDesignerData << "Launching Designer" << "Running the Qt Designer application";
    TreeItem * launchingDesignerItem = new TreeItem(launchingDesignerData, gettingStartedItem);
    gettingStartedItem->appendChild(launchingDesignerItem);

    QList<QVariant> userInterfaceData;
    userInterfaceData << "The User Interface" << "How to interact with Qt Designer";
    TreeItem * userInterfaceItem = new TreeItem(userInterfaceData, gettingStartedItem);
    gettingStartedItem->appendChild(userInterfaceItem);

    QList<QVariant> designingComponentData;
    designingComponentData << "Designing a Component" << "Creating a GUI for your application";
    TreeItem * designingComponentItem = new TreeItem(designingComponentData, rootItem);
    rootItem->appendChild(designingComponentItem);

    QList<QVariant> creatingDialogData;
    creatingDialogData << "Creating a Dialog" << "How to create a dialog";
    TreeItem * creatingDialogItem = new TreeItem(creatingDialogData, designingComponentItem);
    designingComponentItem->appendChild(creatingDialogItem);

    QList<QVariant> composingDialogData;
    composingDialogData << "Composing the Dialog" << "Putting widgets into the dialog example";
    TreeItem * composingDialogItem = new TreeItem(composingDialogData, designingComponentItem);
    designingComponentItem->appendChild(composingDialogItem);
}

TreeModel::~TreeModel()
{
    delete rootItem;
}

int TreeModel::columnCount(const QModelIndex &parent) const
{
    if (parent.isValid())
        return static_cast<TreeItem*>(parent.internalPointer())->columnCount();
    else
        return rootItem->columnCount();
}

QVariant TreeModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();

    if (role != Qt::DisplayRole)
        return QVariant();

    TreeItem *item = static_cast<TreeItem*>(index.internalPointer());

    return item->data(index.column());
}

Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const
{
    if (!index.isValid())
        return 0;

    return QAbstractItemModel::flags(index);
}

QVariant TreeModel::headerData(int section, Qt::Orientation orientation,
                               int role) const
{
    if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
        return rootItem->data(section);

    return QVariant();
}

QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent)
            const
{
    if (!hasIndex(row, column, parent))
        return QModelIndex();

    TreeItem *parentItem;

    if (!parent.isValid())
        parentItem = rootItem;
    else
        parentItem = static_cast<TreeItem*>(parent.internalPointer());

    TreeItem *childItem = parentItem->child(row);
    if (childItem)
        return createIndex(row, column, childItem);
    else
        return QModelIndex();
}

QModelIndex TreeModel::parent(const QModelIndex &index) const
{
    if (!index.isValid())
        return QModelIndex();

    TreeItem *childItem = static_cast<TreeItem*>(index.internalPointer());
    TreeItem *parentItem = childItem->parentItem();

    if (parentItem == rootItem)
        return QModelIndex();

    return createIndex(parentItem->row(), 0, parentItem);
}

int TreeModel::rowCount(const QModelIndex &parent) const
{
    TreeItem *parentItem;
    if (parent.column() > 0)
        return 0;

    if (!parent.isValid())
        parentItem = rootItem;
    else
        parentItem = static_cast<TreeItem*>(parent.internalPointer());

    return parentItem->childCount();
}

main.cpp

#include <QApplication>
#include <QFile>
#include <QTreeView>

#include "treemodel.h"

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    TreeModel model;
    QTreeView view;
    view.setModel(&model);
    view.setWindowTitle(QObject::tr("Simple Tree Model"));
    view.show();
    return app.exec();
}

运行效果:
在这里插入图片描述
接下来,我们对treemodel.cpp文件的构造函数进行改造
如果去掉第二列的数据:
在这里插入图片描述
去掉方框的代码,就能看到:
在这里插入图片描述
发现不显示第二列的数据,就是一般见到的树形控件了。
对treemodel.cpp中的构造函数进行改造

TreeModel::TreeModel(QObject *parent)
    : QAbstractItemModel(parent)
{
    //以下代码都是为了装填数据用,实际中需要另外写接口加载数据
    QList<QVariant> rootData;
    rootData << "Title";
    rootItem = new TreeItem(rootData);

    QList<QVariant> gettingStartedData;
    gettingStartedData << "Getting Started";
    TreeItem * gettingStartedItem = new TreeItem(gettingStartedData, rootItem);
    rootItem->appendChild(gettingStartedItem);

    QList<QVariant> launchingDesignerData;
    launchingDesignerData << "Launching Designer";
    TreeItem * launchingDesignerItem = new TreeItem(launchingDesignerData, gettingStartedItem);
    gettingStartedItem->appendChild(launchingDesignerItem);

    QList<QVariant> userInterfaceData;
    userInterfaceData << "The User Interface";
    TreeItem * userInterfaceItem = new TreeItem(userInterfaceData, gettingStartedItem);
    gettingStartedItem->appendChild(userInterfaceItem);

    QList<QVariant> designingComponentData;
    designingComponentData << "Designing a Component";
    TreeItem * designingComponentItem = new TreeItem(designingComponentData, rootItem);
    rootItem->appendChild(designingComponentItem);

    QList<QVariant> creatingDialogData;
    creatingDialogData << "Creating a Dialog";
    TreeItem * creatingDialogItem = new TreeItem(creatingDialogData, designingComponentItem);
    designingComponentItem->appendChild(creatingDialogItem);

    QList<QVariant> composingDialogData;
    composingDialogData << "Composing the Dialog";
    TreeItem * composingDialogItem = new TreeItem(composingDialogData, designingComponentItem);
    designingComponentItem->appendChild(composingDialogItem);
}

这样就只有一列的数据。
继续修改构造函数:


TreeModel::TreeModel(QObject *parent)
    : QAbstractItemModel(parent)
{
    //以下代码都是为了装填数据用,实际中需要另外写接口加载数据
    QList<QVariant> rootData;
    rootData << "Title";
    rootItem = new TreeItem(rootData);

    QList<QVariant> AData;
    AData << "A";
    TreeItem * AItem = new TreeItem(AData, rootItem);
    rootItem->appendChild(AItem);

    QList<QVariant> a0Data;
    a0Data << "a0";
    TreeItem * a0Item = new TreeItem(a0Data, AItem);
    AItem->appendChild(a0Item);

    QList<QVariant> a1Data;
    a1Data << "a1";
    TreeItem * a1Item = new TreeItem(a1Data, AItem);
    AItem->appendChild(a1Item);

    QList<QVariant> a2Data;
    a2Data << "a2";
    TreeItem * a2Item = new TreeItem(a2Data, AItem);
    AItem->appendChild(a2Item);

    QList<QVariant> BData;
    BData << "B";
    TreeItem * BItem = new TreeItem(BData, rootItem);
    rootItem->appendChild(BItem);

    QList<QVariant> b0Data;
    b0Data << "b0";
    TreeItem * b0Item = new TreeItem(b0Data, BItem);
    BItem->appendChild(b0Item);

    QList<QVariant> b1Data;
    b1Data << "b1";
    TreeItem * b1Item = new TreeItem(b1Data, BItem);
    BItem->appendChild(b1Item);

    QList<QVariant> b2Data;
    b2Data << "b2";
    TreeItem * b2Item = new TreeItem(b2Data, BItem);
    BItem->appendChild(b2Item);
}

在这里插入图片描述
这样我们就掌握了只读TreeView的Model的开发。

  • 2
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值