目录
- 前言
- 效果
- TreeItem:树的数据结构
- CModel:继承QAbstractItemModel
- CTreeView:继承QTreeView
- 记录一些点:
前言
一直以来都知道view/model,平时工作也在用,但很多点都很模糊,所以从头写一遍。
效果
TreeItem:树的数据结构
数据结构类比较重要的是信息(属性),和对信息的获取、变更的方法。根据自己的需要去定义数据结构即可。
这里需要实现树结构,所有每个节点的信息有:父节点、子节点列表、信息。
TreeItem.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35#pragma once #include <QString> #include <QList> class TreeItem { public: TreeItem(TreeItem *parent = nullptr,QString caption = QString(),QString description = QString()); ~TreeItem(); TreeItem *parent() { return m_parent; } void setParent(TreeItem* parent) { this->m_parent = parent; } TreeItem *child(int index) { return m_childrens.at(index); } int childCount() { return m_childrens.size(); } void setCaption(QString caption) { m_caption = caption; } QString getCaption() { return m_caption; } void setDescription(QString description) { m_description = description; } QString getDescription() { return m_description; } int indexOf(TreeItem *item) { return m_childrens.indexOf(item); } void addItem(TreeItem *item); private: TreeItem* m_parent; QString m_caption; QString m_description; QList<TreeItem *> m_childrens; };
TreeItem.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27#include "TreeItem.h" TreeItem::TreeItem(TreeItem * parent, QString caption, QString description ) :m_parent(parent),m_caption(caption),m_description(description) { if(parent) parent->addItem(this); m_childrens.clear(); } TreeItem::~TreeItem() { } void TreeItem::addItem(TreeItem * item) { if (item) { m_childrens.append(item); item->setParent(this); } else Q_ASSERT(0); }
CModel:继承QAbstractItemModel
必须要重写的五个虚函数:index、parent、rowCount、columnCount、data。针对不同的数据类型,也有不同的实现,这里是针对树结构的实现。
flag是为了实现可编辑才重写的。非必须。
setData是为了编辑之后把数据保存到数据结构才重写的。非必须。
CModel.h:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28#pragma once #include <qabstractitemmodel.h> class TreeItem; class CModel : public QAbstractItemModel { public: CModel(QObject *parent = nullptr); ~CModel(); TreeItem* getTopNode() { return m_topTreeItem; } // 通过 QAbstractItemModel 继承 virtual Q_INVOKABLE QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const override; virtual Q_INVOKABLE QModelIndex parent(const QModelIndex & child) const override; virtual Q_INVOKABLE int rowCount(const QModelIndex & parent = QModelIndex()) const override; virtual Q_INVOKABLE int columnCount(const QModelIndex & parent = QModelIndex()) const override; virtual Q_INVOKABLE QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override; Qt::ItemFlags flags(const QModelIndex &index) const; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); private: TreeItem *m_topTreeItem; };
CModel.cpp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123#include "CModel.h" #include "TreeItem.h" CModel::CModel(QObject * parent) :QAbstractItemModel(parent) { m_topTreeItem = new TreeItem(); } CModel::~CModel() { } Q_INVOKABLE QModelIndex CModel::index(int row, int column, const QModelIndex & parent) const { if (!hasIndex(row, column, parent)) return Q_INVOKABLE QModelIndex(); TreeItem *parentItem; if (parent.isValid()) parentItem = (TreeItem *)parent.internalPointer(); else parentItem = m_topTreeItem; TreeItem *item = parentItem->child(row); return createIndex(row, column, item); } Q_INVOKABLE QModelIndex CModel::parent(const QModelIndex & child) const { if (!child.isValid()) return QModelIndex(); TreeItem *item = (TreeItem *)child.internalPointer(); TreeItem *parentItem = item->parent(); if (parentItem == m_topTreeItem) return QModelIndex(); int row = 0; if (parentItem->parent()) row = parentItem->parent()->indexOf(parentItem); return createIndex(row,0,parentItem); } Q_INVOKABLE int CModel::rowCount(const QModelIndex & parent) const { TreeItem *parentItem; if (parent.column() > 0) return 0; if (parent.isValid()) parentItem = (TreeItem *)parent.internalPointer(); else parentItem = m_topTreeItem; return parentItem->childCount(); } Q_INVOKABLE int CModel::columnCount(const QModelIndex & parent) const { return 2; } Q_INVOKABLE QVariant CModel::data(const QModelIndex & index, int role) const { if (!index.isValid()) return QVariant(); TreeItem *item = (TreeItem *)index.internalPointer(); int colum = index.column(); switch (role) { case Qt::EditRole: case Qt::DisplayRole: if (index.column() == 0) { return item->getCaption(); } else if (index.column() == 1) { return item->getDescription(); } break; default: break; } return QVariant(); } Qt::ItemFlags CModel::flags(const QModelIndex & index) const { return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable; } bool CModel::setData(const QModelIndex & index, const QVariant & value, int role) { if (!index.isValid()) return false; TreeItem *item = (TreeItem *)index.internalPointer(); int column = index.column(); if (column == 0) { item->setCaption(value.toString()); return true; } else if (column == 1) { item->setDescription(value.toString()); return true; } else { Q_ASSERT(0); } return false; }
CTreeView:继承QTreeView
在CTreeView中实现一些具体的操作:添加一级节点、添加子节点。(没写删除节点)
如果要处理每项之前的图标那块,就重写drawBranches。
如果要处理每行的显示,(没有代理的情况下),重写drawRow。
CTreeView.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38#pragma once #include <qtreeview.h> class CModel; class CTreeView : public QTreeView { Q_OBJECT public: CTreeView(QWidget *parent = nullptr); virtual ~CTreeView(); //设置model void setTreeModel(QAbstractItemModel *model); //设置CModel CModel *getCModel() { return m_model; } public slots: //slot:添加一级节点 void slotAddOneLevelItem(); //slot:添加子节点 void slotAddSubItem(); protected: void mousePressEvent(QMouseEvent *event); private: //获得选中行的首列QModelIndex QModelIndexList getSelectedIndexList(); //获得选中的行数 int32_t getSelectedRows(); void addFolder(QModelIndex index = QModelIndex(), QString caption = QStringLiteral("newFolder"), QString des = QStringLiteral("newDes")); private: CModel *m_model; };
CTreeView.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107#include "CTreeView.h" #include "CModel.h" #include "TreeItem.h" #include <QMouseEvent> CTreeView::CTreeView(QWidget * parent):QTreeView(parent), m_model(nullptr) { //支持多选 this->setSelectionMode(QAbstractItemView::ExtendedSelection); } CTreeView::~CTreeView() { } void CTreeView::setTreeModel(QAbstractItemModel * model) { m_model = dynamic_cast<CModel*>(model); if (!m_model) Q_ASSERT(0); this->setModel(m_model); } //slot:添加一级文件夹 void CTreeView::slotAddOneLevelItem() { //判断选中行数 int32_t selectedRows = this->getSelectedRows(); if (selectedRows < 0) { Q_ASSERT(0); return; } //获得根节点 TreeItem* topNode = getCModel()->getTopNode(); if (!topNode) Q_ASSERT(0); //在根节点添加一级节点 TreeItem* newItem = new TreeItem(topNode, QStringLiteral("新建文件夹"), QStringLiteral("...")); //刷新界面 this->doItemsLayout(); } //slot:添加子节点 void CTreeView::slotAddSubItem() { //判断选中行数 int32_t selectedRows = this->getSelectedRows(); if (selectedRows != 1) { return; } QModelIndexList selectedList = this->getSelectedIndexList(); TreeItem* selectedItem = (TreeItem*)selectedList.at(0).internalPointer(); if (!selectedItem) { Q_ASSERT(0); return; } TreeItem* newItem = new TreeItem(selectedItem, QStringLiteral("新建文件夹"), QStringLiteral("...")); //刷新界面 this->doItemsLayout(); } void CTreeView::mousePressEvent(QMouseEvent * event) { QTreeView::mousePressEvent(event); } //获得选中行的首列QModelIndex QModelIndexList CTreeView::getSelectedIndexList() { //获得所有选中的QModelIndex QModelIndexList selectedList = this->selectionModel()->selectedIndexes(); //只留下每行column为0的QModelIndex for (int i = 0; i < selectedList.size(); ++i) { if (selectedList.at(i).column() != 0) selectedList.removeOne(selectedList.at(i)); } return selectedList; } //获得选中的行数 int32_t CTreeView::getSelectedRows() { QModelIndexList selectedList = this->getSelectedIndexList(); return selectedList.size(); } void CTreeView::addFolder(QModelIndex index, QString caption, QString des) { }
记录一些点:
- QTreeView支持shift多选,ctrl点选:
1
2m_treeView->setSelectionMode(QAbstractItemView::ExtendedSelection);
-
更新了数据结构,调用QTreeView的doItemsLayout()刷新界面。
-
QTreeView的selectedIndexs(),获得是QModelIndexList,其中包含了选中行的所有QModelIndex。比如:选中三行,但是每行有两列,那么List中就有6个QModelIndex。
最后
以上就是花痴胡萝卜最近收集整理的关于Qt 自定义数据结构,重写QTreeView和QAbstractItemModel。前言效果TreeItem:树的数据结构CModel:继承QAbstractItemModelCTreeView:继承QTreeView记录一些点:的全部内容,更多相关Qt内容请搜索靠谱客的其他文章。
发表评论 取消回复