【QT】自定义可无限增自由删的树形控件

仅做个笔记用,单纯的记录整理出来的一个思路, 没有做任何优化。 写这个控件主要是为后续设置代理或者其他扩展打基础, 不然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);
        }
    }
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值