仅做个笔记用,单纯的记录整理出来的一个思路, 没有做任何优化。 写这个控件主要是为后续设置代理或者其他扩展打基础, 不然QT自带的属性控件本来就是可以无限扩展的。
cusomtreeview.h
#ifndef CUSTOMTREE_H
#define CUSTOMTREE_H
#include <qtreeview.h>
#include <qpushbutton.h>
#include <qstandarditemmodel.h>
#include <qvector.h>
#include <qmap.h>
#include <qdebug.h>
#include <qevent.h>
#include <qmutex.h>
#include <qmenu.h>
class CustomCptDirTreeView : public QTreeView {
Q_OBJECT
public:
CustomCptDirTreeView(QWidget* parent = nullptr);
~CustomCptDirTreeView();
// struct
struct TreeViewNodeDataStruct
{
TreeViewNodeDataStruct() {};
TreeViewNodeDataStruct(int parentnodelevel, int parentsernum,int sernum, QString content) :
nParentNodeLevel(parentnodelevel),nParentSerNum(parentsernum), nSerNum(sernum), strContent(content){};
int nParentNodeLevel{ 0 };
int nParentSerNum{ 0 };
int nSerNum{ 0 };
QString strContent{ "" };
};
enum class OperateTreeNodeType
{
DelNode, AddInFrontSameLeDir, AddInRearSameLevDir, AddInRearNextLevDir
};
// func
// 获取第一级节点的数量
int GetFirstLevDirNum();
// 根据m_vecTreeViewNodeData内的数据刷新所有节点
void RefreshUI();
// 插入节点
QString InsertNode(int parentlevel, int parentsernum,int sernum, QString text);
// 删除节点
void DeleteNode(int parentlevel, int parentsernum, int sernum);
// 获取所有节点数据
QVector<CustomCptDirTreeView::TreeViewNodeDataStruct> GetAllNodeData();
// 重设所有节点数据
void ResetAllNodeData(QVector<CustomCptDirTreeView::TreeViewNodeDataStruct>);
// data
QString m_strAutoDirContent{ QStringLiteral("请输入节点名称") };
private slots:
void slot_OpenTreeMenu(const QPoint&);
void slot_DelDir(bool);
void slot_AddInFrontSameLevDir(bool);
void slot_AddInRearSameLevDir(bool);
void slot_AddInRearNextLevDir(bool);
private:
// func
void InitUI();
void InitListener();
void InitData();
// 根据Vector添加节点
void AddNodeByDataVec(int parentnodelevel, QVector<QStandardItem*> parentnodevec,
QVector<TreeViewNodeDataStruct> nodedatavec);
// 操作节点
QString OperateTreeNode(QModelIndex curindex, OperateTreeNodeType type);
// 获取当前节点下的子节点数量
int GetCurLevNodeChildNum(int parentlev);
// 打开右键菜单
void OpenMenu(QStandardItem* item = nullptr);
// 获取所有节点
QVector<QStandardItem*> GetAllNode();
void GetItem(QStandardItem*, QVector<QStandardItem*>&);
// data
QStandardItemModel* m_Model{ nullptr };
QMutex m_TreeViewMutex{};
QVector<CustomCptDirTreeView::TreeViewNodeDataStruct> m_vecTreeViewNodeData{};
};
#endif // CUSTOMTREE_H
customtreeview.cpp
#include "customtree.h"
CustomCptDirTreeView::CustomCptDirTreeView(QWidget* parent) : QTreeView(parent) {
InitUI();
InitListener();
InitData();
}
CustomCptDirTreeView::~CustomCptDirTreeView() {
}
void CustomCptDirTreeView::InitUI()
{
setHeaderHidden(true);
m_Model = new QStandardItemModel(this);
setModel(m_Model);
RefreshUI();
setContextMenuPolicy(Qt::CustomContextMenu);
}
void CustomCptDirTreeView::InitListener()
{
connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(slot_OpenTreeMenu(const QPoint&)));
}
void CustomCptDirTreeView::InitData()
{
InsertNode(0,0,0,"第一章");
InsertNode(0,0,1,"第二章");
InsertNode(0,0,2,"第三章");
}
int CustomCptDirTreeView::GetFirstLevDirNum()
{
int nResult{ 0 };
for (CustomCptDirTreeView::TreeViewNodeDataStruct nodedata : m_vecTreeViewNodeData) {
if (nodedata.nParentNodeLevel == 0) {
nResult++;
}
}
return nResult;
}
void CustomCptDirTreeView::AddNodeByDataVec(int parentnodelevel, QVector<QStandardItem*> parentnodevec,
QVector<TreeViewNodeDataStruct> nodedatavec)
{
if (parentnodevec.isEmpty() || nodedatavec.isEmpty()) {
return;
}
// 提取数据
QVector<CustomCptDirTreeView::TreeViewNodeDataStruct> vecNeedSortData{};
QVector<CustomCptDirTreeView::TreeViewNodeDataStruct> vecLeftData{};
for (auto nodedata : nodedatavec) {
if (nodedata.nParentNodeLevel != parentnodelevel) {
vecLeftData.push_back(nodedata);
continue;
}
vecNeedSortData.push_back(nodedata);
}
// 排序
for (int i = 0; i < vecNeedSortData.count(); i++) {
for (int j = 0; j < vecNeedSortData.count() - i - 1; j++) {
auto firstData = vecNeedSortData[j];
auto secondData = vecNeedSortData[j + 1];
if (firstData.nSerNum > secondData.nSerNum) {
auto tempData = firstData;
vecNeedSortData[j] = vecNeedSortData[j + 1];
vecNeedSortData[j + 1] = tempData;
}
}
}
// 添加节点
QVector<QStandardItem*> vecTempNode{};
for (auto nodedata : vecNeedSortData) {
if (parentnodevec.count() > nodedata.nParentSerNum) {
QStandardItem* pNode = new QStandardItem();
pNode->setText(nodedata.strContent);
parentnodevec[nodedata.nParentSerNum]->appendRow(pNode);
vecTempNode.push_back(pNode);
}
}
AddNodeByDataVec(parentnodelevel + 1, vecTempNode, vecLeftData);
}
void CustomCptDirTreeView::RefreshUI()
{
m_Model->clear();
// 根节点等级为0, 第1级子节点为1, 第二级为2...
int nRootNodeLevel{ 0 };
int nRootNodeSerNum{ 0 };
// 提取数据
QVector<CustomCptDirTreeView::TreeViewNodeDataStruct> vecNeedSortData{};
QVector<CustomCptDirTreeView::TreeViewNodeDataStruct> vecLeftData{};
for (auto nodedata : m_vecTreeViewNodeData) {
if (nodedata.nParentNodeLevel != nRootNodeLevel || nodedata.nParentSerNum != nRootNodeSerNum) {
vecLeftData.push_back(nodedata);
continue;
}
vecNeedSortData.push_back(nodedata);
}
// 排序
for (int i = 0; i < vecNeedSortData.count(); i++) {
for (int j = 0; j < vecNeedSortData.count() - i - 1; j++) {
auto firstData = vecNeedSortData[j];
auto secondData = vecNeedSortData[j + 1];
if (firstData.nSerNum > secondData.nSerNum) {
auto tempData = firstData;
vecNeedSortData[j] = vecNeedSortData[j + 1];
vecNeedSortData[j + 1] = tempData;
}
}
}
// 添加节点
QVector<QStandardItem*> vecTempNode{};
for (auto nodedata : vecNeedSortData) {
QStandardItem* pNode = new QStandardItem();
pNode->setText(nodedata.strContent);
m_Model->appendRow(pNode);
vecTempNode.push_back(pNode);
}
AddNodeByDataVec(nRootNodeLevel + 1, vecTempNode, vecLeftData);
expandAll();
}
QString CustomCptDirTreeView::InsertNode(int parentlevel, int parentsernum,int sernum, QString text)
{
m_TreeViewMutex.lock();
QString strParentNodeText{};
QString strResultText{};
int nCount{0};
for (CustomCptDirTreeView::TreeViewNodeDataStruct& nodedata : m_vecTreeViewNodeData) {
if (nodedata.nParentNodeLevel == parentlevel && nodedata.nSerNum >= sernum) {
nodedata.nSerNum++;
}
if (nodedata.nParentNodeLevel == parentlevel && nodedata.nParentSerNum == parentsernum) {
nCount++;
}
if (nodedata.nParentNodeLevel == parentlevel + 1 && nodedata.nParentSerNum >= sernum) {
nodedata.nParentSerNum++;
}
if(nodedata.nParentNodeLevel == parentlevel - 1 && nodedata.nSerNum == parentsernum){
strParentNodeText = nodedata.strContent;
}
}
if(!strParentNodeText.isEmpty()){
strResultText = QString("%1.%2 %3").arg(strParentNodeText.split(" ").first()).arg(nCount+1).arg(text);
}else{
strResultText = QString("%1 %3").arg(nCount+1).arg(text);
}
m_vecTreeViewNodeData.push_back({ parentlevel,parentsernum,sernum,strResultText });
RefreshUI();
m_TreeViewMutex.unlock();
return strResultText;
}
void CustomCptDirTreeView::DeleteNode(int parentlevel, int parentsernum, int sernum)
{
QVector<CustomCptDirTreeView::TreeViewNodeDataStruct> vecLeftNodeData{};
QVector<CustomCptDirTreeView::TreeViewNodeDataStruct> vecNeedDelChildNodeData{};
for (CustomCptDirTreeView::TreeViewNodeDataStruct nodedata : m_vecTreeViewNodeData) {
if (nodedata.nParentNodeLevel == parentlevel && nodedata.nParentSerNum == parentsernum
&& nodedata.nSerNum == sernum) {
continue;
}
if (nodedata.nParentNodeLevel == (parentlevel + 1) &&
nodedata.nParentSerNum == sernum) {
vecNeedDelChildNodeData.push_back(nodedata);
}
vecLeftNodeData.push_back(nodedata);
}
for (CustomCptDirTreeView::TreeViewNodeDataStruct& leftdata : vecLeftNodeData) {
if (leftdata.nParentNodeLevel == parentlevel && leftdata.nSerNum > sernum) {
leftdata.nSerNum--;
}
if (leftdata.nParentNodeLevel == (parentlevel + 1) && leftdata.nParentSerNum > sernum) {
leftdata.nParentSerNum--;
}
}
m_vecTreeViewNodeData = vecLeftNodeData;
for (auto deldata : vecNeedDelChildNodeData) {
DeleteNode(deldata.nParentNodeLevel, deldata.nParentSerNum, deldata.nSerNum);
}
RefreshUI();
}
QString CustomCptDirTreeView::OperateTreeNode(QModelIndex curindex, OperateTreeNodeType type)
{
QString strResult{ "" };
QString strItemText{ "" };
QStandardItem* pItem = m_Model->itemFromIndex(curindex);
if (!pItem) {
return strResult;
}
int nParentNodeLevel{ 0 };
int nParentNodeSerNum{ 0 };
int nSerNum{ 0 };
strItemText = pItem->text();
for (CustomCptDirTreeView::TreeViewNodeDataStruct nodedata : m_vecTreeViewNodeData) {
if(nodedata.strContent == strItemText){
nParentNodeLevel = nodedata.nParentNodeLevel;
nParentNodeSerNum = nodedata.nParentSerNum;
nSerNum = nodedata.nSerNum;
break;
}
}
switch (type)
{
case CustomCptDirTreeView::OperateTreeNodeType::DelNode:
DeleteNode(nParentNodeLevel, nParentNodeSerNum, nSerNum);
break;
case CustomCptDirTreeView::OperateTreeNodeType::AddInFrontSameLeDir:
strResult = InsertNode(nParentNodeLevel, nParentNodeSerNum,nSerNum, m_strAutoDirContent);
break;
case CustomCptDirTreeView::OperateTreeNodeType::AddInRearSameLevDir:
strResult = InsertNode(nParentNodeLevel, nParentNodeSerNum,
nSerNum + 1, m_strAutoDirContent);
break;
case CustomCptDirTreeView::OperateTreeNodeType::AddInRearNextLevDir:
strResult = InsertNode(nParentNodeLevel + 1,
nSerNum, GetCurLevNodeChildNum(nParentNodeLevel + 1), m_strAutoDirContent);
break;
}
return strResult;
}
void CustomCptDirTreeView::slot_OpenTreeMenu(const QPoint& pos)
{
Q_UNUSED(pos);
OpenMenu();
}
void CustomCptDirTreeView::OpenMenu(QStandardItem* item)
{
QMenu menu{};
menu.setStyleSheet("QMenu{color:black;border:none;background:white;margin:2px;}\
QMenu::item{padding:3px 20px 3px 20px;min-width:188;min-height:28;font:13pt;}\
QMenu::indicator{width:23px;height:23px;}\
QMenu::separator{height:1px;background:#757575;}\
QMenu::item:selected{color:black;border:0px solid #575757;background:#D5D5D5;}");
QModelIndex curIndex = currentIndex();
if (!item) {
item = m_Model->itemFromIndex(curIndex);
}
if (!item) {
return;
}
QString strItemText = item->text();
QModelIndex index = curIndex.sibling(curIndex.row(), 0);
if (index.isValid())
{
menu.addAction(QStringLiteral("删除"), this, SLOT(slot_DelDir(bool)));
menu.addSeparator();
menu.addAction(QStringLiteral("前方新增同级目录"), this, SLOT(slot_AddInFrontSameLevDir(bool)));
menu.addAction(QStringLiteral("后方新增同级目录"), this, SLOT(slot_AddInRearSameLevDir(bool)));
menu.addSeparator();
menu.addAction(QStringLiteral("下级新增子目录"), this, SLOT(slot_AddInRearNextLevDir(bool)));
}
menu.exec(QCursor::pos());
}
void CustomCptDirTreeView::slot_DelDir(bool checked)
{
Q_UNUSED(checked);
OperateTreeNode(currentIndex(), OperateTreeNodeType::DelNode);
}
void CustomCptDirTreeView::slot_AddInFrontSameLevDir(bool checked)
{
Q_UNUSED(checked);
QString strAddedItemText = OperateTreeNode(currentIndex(), OperateTreeNodeType::AddInFrontSameLeDir);
auto itemList = m_Model->findItems(strAddedItemText, Qt::MatchContains | Qt::MatchRecursive);
if (itemList.count() > 0) {
scrollTo(itemList[0]->index());
setCurrentIndex(itemList[0]->index());
}
}
void CustomCptDirTreeView::slot_AddInRearSameLevDir(bool checked)
{
Q_UNUSED(checked);
QString strAddedItemText = OperateTreeNode(currentIndex(), OperateTreeNodeType::AddInRearSameLevDir);
auto itemList = m_Model->findItems(strAddedItemText, Qt::MatchContains | Qt::MatchRecursive);
if (itemList.count() > 0) {
scrollTo(itemList[0]->index());
setCurrentIndex(itemList[0]->index());
}
}
void CustomCptDirTreeView::slot_AddInRearNextLevDir(bool checked)
{
Q_UNUSED(checked);
QString strAddedItemText = OperateTreeNode(currentIndex(), OperateTreeNodeType::AddInRearNextLevDir);
auto itemList = m_Model->findItems(strAddedItemText, Qt::MatchContains | Qt::MatchRecursive);
if (itemList.count() > 0) {
scrollTo(itemList[0]->index());
setCurrentIndex(itemList[0]->index());
}
}
int CustomCptDirTreeView::GetCurLevNodeChildNum(int parentlev)
{
int nResult{ 0 };
for (CustomCptDirTreeView::TreeViewNodeDataStruct nodedata : m_vecTreeViewNodeData) {
if (nodedata.nParentNodeLevel == parentlev) {
nResult++;
}
}
return nResult;
}
QVector<CustomCptDirTreeView::TreeViewNodeDataStruct> CustomCptDirTreeView::GetAllNodeData()
{
return m_vecTreeViewNodeData;
}
void CustomCptDirTreeView::ResetAllNodeData(QVector<CustomCptDirTreeView::TreeViewNodeDataStruct> vec)
{
m_vecTreeViewNodeData = vec;
RefreshUI();
}
QVector<QStandardItem*> CustomCptDirTreeView::GetAllNode()
{
QVector<QStandardItem*> vecTempNode{};
for (int i = 0; i < m_Model->rowCount(); i++)
{
QStandardItem* item = m_Model->item(i);
vecTempNode.push_back(item);
GetItem(item, vecTempNode);
}
return vecTempNode;
}
void CustomCptDirTreeView::GetItem(QStandardItem* item, QVector<QStandardItem*>& vec)
{
Q_ASSERT(item);
if (item->hasChildren())
{
for (int i = 0; i < item->rowCount(); i++)
{
QStandardItem* childitem = item->child(i);
vec.push_back(childitem);
GetItem(childitem, vec);
}
}
}