在 Qt 中使用 TreeView
目录
提示:本文中的 Demo 已 push 到 github,可忽略本文直接到 我的github 中查看代码。
Qt 提供了 QuickControl TreeView
。但是比较奇葩的是该控件不能直接使用,而需要用户自己扩展实现。
官方给出了一个示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
TreeView { TableViewColumn { title: "Name" role: "fileName" width: 300 } TableViewColumn { title: "Permissions" role: "filePermissions" width: 100 } model: fileSystemModel } |
它声明了一个 TreeView控件,该控件有 2 列,分别为 Name 和 Permissions,还有一个名为 fileSystemModel 的 model
。对用户来说,这里的 model 是一个关键性的对象,它需要用户使用 C++ 实现 ,并注册到 qml 中供 TreeView 使用。按官方的说法, model 是一个 为 tree view 提供数据的属性,它包含了 tree view 将要展示的数据。
用户的 model 必须继承于 QAbstractItemModel
类。
该类是一个抽象类,在运行中,treeview 从该类中获取用户数据,再在UI上展示。该类有如下纯虚函数,必须在子类中实现:
1 2 3 4 5 |
virtual int columnCount(const QModelIndex &parent = QModelIndex()) const = 0; virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const = 0; virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const = 0; virtual QModelIndex parent(const QModelIndex &index) const = 0; virtual int rowCount(const QModelIndex &parent = QModelIndex()) const = 0; |
columnCount(const QModelIndex &parent = QModelIndex())
返回 parent 的列数。这里的参数 parent
给出默认值,因为一般来说,一个 treeview 中的列数都是固定的。当然 ,如果 parent is valid, 需要返回 0.
rowCount(const QModelIndex &parent = QModelIndex())
返回 parent 下数据的行数。
parent(const QModelIndex &index)
返回给定节点的 index 的父节点的 index .如果其没有父节点,需要返回 QModelIndex()
默认值,否则需要调用 createIndex(int row, int column, quintptr id)
构建父节点的 index并返回。一般来说,只有第 1 列才会有子节点,所以 column
直接给 0
就行了
index(int row, int column, const QModelIndex &parent = QModelIndex())
给定节点的 行、列以及父节点 index,返回该节点的 index
data(const QModelIndex &index, int role = Qt::DisplayRole)
给定节点 index, 返回该节点存储的 role 相关的数据。这里出现了一个参数 role
, 它和官方给出的 qml 例子中的 role 是一个东西,都是用来给数据分类的标识。这需要另一个虚函数的配合: QHash<int, QByteArray> QAbstractItemModel::roleNames() const
QAbstractItemModel::roleNames()
它返回角色的 数值 到 字符串 的映射,以将 TableViewColumn
中的 role 与 C++ 中的属性连接起来。
按以上,给出我们实现 QAbstractItemModel
类的定义:
treeview_item.h
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
class TreeViewModel : public QAbstractItemModel { Q_OBJECT public: enum ItemRoles{ MROLE_Name=Qt::UserRole + 1, MROLE_CreateDate }; Q_ENUM(ItemRoles) public: TreeViewModel(QObject *parent=nullptr); virtual int columnCount(const QModelIndex &parent) const override; virtual QModelIndex index(int row, int column, const QModelIndex &parent) const override; virtual QVariant data(const QModelIndex &index, int role) const override; virtual QModelIndex parent(const QModelIndex &child) const override; virtual int rowCount(const QModelIndex &parent) const override; virtual QHash<int,QByteArray> roleNames() const override; private: TreeViewItem *m_rootItem; }; |
该类还定义了两个新的角色,这些角色将在后面的例子中用到。另外该类还有一个成员变量 TreeViewItem *m_rootItem
,它是用户自定义的数据。做为一个树型结构,用户数据的抽象应该有树型数据结构的常用操作。我们将 TreeViewItem
数据结构抽象如下:
treeview_item.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
class TreeViewItem { public: TreeViewItem(); TreeViewItem(const QList<QVariant>& data, TreeViewItem* parent); ~TreeViewItem(); void appendChild(TreeViewItem* item); void clearAllChild(); TreeViewItem* child(int row); int childCount() const; int columnCount() const; QVariant data(int column) const; TreeViewItem* parent() const; int row() const; private: TreeViewItem* m_parentItem; QList<TreeViewItem*> m_childItems; QList<QVariant> m_data; }; |
具体的代码可以到 我的 github 中 checkout。Demo 中遍历了输出目录的文件,并添加到TreeView 中。Deme 的效果如下图: