Qt重写QTreeWidget实现拖拽

介绍

此文章记录QTreeWidget的重写进度,暂时停滞使用,重写了QTreeWidget的拖拽功能,和绘制功能,自定义了数据结构,增加复制,粘贴,删除,准备实现动态刷新数据支持千万数据动态刷新,重写了部分代码,重写了滑块拖拽但是有bug。

效果展示

在这里插入图片描述
在这里插入图片描述

实现功能

  1. 实现了自定义节点类来存储数据。
  2. item采用Label来实现富文本显示(数据量大不建议使用)。
  3. 重写了QTreeWidget拖拽,滚动,绘制。
  4. 拖拽实现了节点与节点之间的互相拖拽,仅移动。
  5. 实现了节点的递归遍历,查找,插入,删除。
  6. 实现了滚动刷新数据,大量数据也不卡,但是滚动条刷新数据稍微复杂未完成,且有bug。

代码

#ifndef MYTREEWIDGET_H
#define MYTREEWIDGET_H

#include <QWidget>
#include <QtWidgets>
#include <QDebug>

class MyTreeWidgetItem;
class TreeNode;
class MyLabel;

// 自定义节点类
class TreeNode {
public:
    QString nodeText;
    QString nodeItemTest;

    QList<TreeNode*> children;
    TreeNode* parent = nullptr;
    MyTreeWidgetItem* item;

    // 重载==运算符以判断nodeText是否相等
    bool operator==(const TreeNode& other) const {
        return nodeText == other.nodeText;
    }
};

class MyTreeWidgetItem :public QTreeWidgetItem
{
public:
    MyTreeWidgetItem();

    void setNodeText(QString str);
    QString getNodeText();

    QString m_Text;
    MyLabel* m_Label;

    int indexOfChild(QTreeWidgetItem *child)
    {
        int childCount = this->childCount();
        for (int i = 0; i < childCount; ++i) {
            if (this->child(i) == child) {
                return i;
            }
        }
        return -1;
    }
};

class MyLabel : public QLabel
{
    Q_OBJECT
public:
    MyLabel(QString text, QWidget *parent = nullptr);

    bool m_IsSelect = false;
    bool m_IsLeave = true;

    QString enterStyle;
    QString leaveStyle;
    QString selectStyle;
protected:
    bool eventFilter(QObject *watched, QEvent *event);
};


class MyTreeWidget : public QTreeWidget
{
    Q_OBJECT
public:
    MyTreeWidget(QWidget *parent = nullptr);

    QList<TreeNode*> m_ListData;
  void wait(int ms); //等待
    // 删除具有特定文本的节点及其子节点
    bool deleteNodeByText(const QString& targetText) {
        for (int i = 0; i < m_ListData.size(); ++i) {
            TreeNode* node = m_ListData.at(i);
            if (node->nodeText == targetText) {
                // 找到目标节点,删除其子节点
                qDeleteAll(node->children);
                delete node;
                m_ListData.removeAt(i);
                return true; // 成功删除节点
            }
        }
        return false; // 未找到匹配的节点
    }


    //删除指定父节点下的子节点
    bool deleteChildNode(TreeNode* node, const QString& nodeText) {
        for (int i = 0; i < node->children.size(); ++i) {
            if (node->children.at(i)->nodeText == nodeText) {
                node->children.removeAt(i);
                return true;
            }
        }
        return false;
    }

    //根据nodeText递归查找该对象
    TreeNode* recursiveFindNodeByText(const QString& targetText, TreeNode* currentNode) {
        if (currentNode->nodeText == targetText) {
            return currentNode;
        }

        for (TreeNode* child : currentNode->children) {
            TreeNode* result = recursiveFindNodeByText(targetText, child);
            if (result) {
                return result;
            }
        }

        return nullptr; // 未找到匹配的节点
    }



    //根据nodeText递归查找该对象
    TreeNode* findNodeByText(const QString& targetText, const QList<TreeNode*>& nodeList) {
        for (TreeNode* node : nodeList) {
            TreeNode* result = recursiveFindNodeByText(targetText, node);
            if (result) {
                return result;
            }
        }

        return nullptr; // 未找到匹配的节点
    }

    //插入字节点 包括数据更新
    TreeNode* insertChileItem(TreeNode* parentNode, int row, QString itemText);
    //追加子节点
    TreeNode* appendChileItem(TreeNode* parentNode, QString itemText);
    //插入顶部节点
    TreeNode* insertTopItem(int row, QString itemText, bool isInster = true);
    //
    void updateItem(int row, TreeNode* node);
    //追加顶部节点
    TreeNode* appendTopItem(QString itemText);
    //删除顶部的item 以及数据列表
    void removeTopItem(TreeNode* node);
    //一个更新nodetext的变量保证每次更新节点都会变化
    int number = 0;
    double m_NodeSize = 0;
    double m_ScrollBarValue = 0;

    bool m_BarValueChangedTemp = false;
    bool m_WheelEventTemp = false;

    //递归插入
    void RecursionInsert(TreeNode* ParentNode, TreeNode* childNode);
    //展开所有子节点
    void expandAllItems(QTreeWidget* treeWidget, QTreeWidgetItem* item);

    void onScrollBarValueChanged(int value);
protected:
    void mouseMoveEvent(QMouseEvent *event);
    void mousePressEvent(QMouseEvent *event);
    void mouseReleaseEvent(QMouseEvent *event);
    void startDrag(Qt::DropActions supportedActions);
    void dragEnterEvent(QDragEnterEvent *event);
    void dragMoveEvent(QDragMoveEvent *event);
    void dropEvent(QDropEvent *event);
    void paintEvent(QPaintEvent* event);
    void wheelEvent(QWheelEvent *event);
private:
    TreeNode* m_CopyNode = nullptr; //当前拷贝的节点
    QPoint m_DragMovePos; //拖拽移动坐标
    QPoint m_CurPos; //点击保存坐标
    int m_LastScrollBarValue = 0;
    QMenu* m;

    MyTreeWidgetItem* m_CurrDragItem = nullptr; //保存当前正在拖拽的对象

    QScrollBar *m_VerticalScrollBar;
};


#endif // MYTREEWIDGET_H

/*******************************************************************************
 * Copyright [2023] <键鼠自动化>
 * All rights reserved.
 *
 * version:     1.0
 * author:      lx
 * brief:       自定义item,重写绘制和拖拽,以及建立数据结构
*******************************************************************************/
#include "mytreewidget.h"


MyTreeWidget::MyTreeWidget(QWidget *parent): QTreeWidget(parent)
{
    setDragEnabled(true);
    setAcceptDrops(true);
    setDragDropMode(DragDropMode::InternalMove);
    setHeaderHidden(true);

    m_VerticalScrollBar = this->verticalScrollBar();
    connect(m_VerticalScrollBar, &QScrollBar::valueChanged, this, &MyTreeWidget::onScrollBarValueChanged);

    m = new QMenu(this);
    QAction* action1 = new QAction("删除");
    QAction* action2 = new QAction("复制");
    QAction* action3 = new QAction("粘贴");

    connect(action1, &QAction::triggered, [=]() {
        MyTreeWidgetItem* targetItem = dynamic_cast<MyTreeWidgetItem*>(itemAt(m_CurPos));
        if (targetItem) {

            TreeNode* targetNode = findNodeByText(targetItem->getNodeText(), m_ListData);

            if (targetNode->parent == nullptr) {
                this->removeTopItem(targetNode);
            }
            else {
                targetNode->parent->item->removeChild(targetNode->item);
                deleteChildNode(targetNode->parent, targetNode->nodeText);
            }
        }
        qDebug() << "1231231";
    });


    connect(action2, &QAction::triggered, [=](){
        MyTreeWidgetItem* targetItem = dynamic_cast<MyTreeWidgetItem*>(itemAt(m_CurPos));
        if (targetItem) {
            m_CopyNode = findNodeByText(targetItem->getNodeText(), m_ListData);
        }
    });


    connect(action3, &QAction::triggered, [=]() {
        MyTreeWidgetItem* targetItem = dynamic_cast<MyTreeWidgetItem*>(itemAt(m_CurPos));
        if (targetItem && m_CopyNode != nullptr) {

            TreeNode* targetNode = findNodeByText(targetItem->getNodeText(), m_ListData);
            if (targetNode->parent == nullptr) {
                int targetRow = this->indexOfTopLevelItem(targetNode->item);

                if (m_CopyNode->children.isEmpty()) {
                    this->insertTopItem(targetRow + 1, m_CopyNode->nodeItemTest);
                }
                else {
                    TreeNode* newParentNode = this->insertTopItem(targetRow + 1, m_CopyNode->nodeItemTest);
                    for (int i = 0 ; i < m_CopyNode->children.size(); ++i) {
                        this->RecursionInsert(newParentNode, m_CopyNode->children.at(i));
                    }
                }
            }
            else {
                int targetRow = targetNode->parent->item->indexOfChild(targetNode->item);

                if (m_CopyNode->children.isEmpty()) {
                    this->insertChileItem(targetNode->parent, targetRow + 1, m_CopyNode->nodeItemTest);
                }
                else {
                    TreeNode* newParentNode = this->insertChileItem(targetNode->parent, targetRow + 1, m_CopyNode->nodeItemTest);
                    for (int i = 0 ; i < m_CopyNode->children.size(); ++i) {
                        this->RecursionInsert(newParentNode, m_CopyNode->children.at(i));
                    }
                }
            }

            m_CopyNode = nullptr;
        }
    });

    m->addAction(action1);
    m->addAction(action2);
    m->addAction(action3);
}

void MyTreeWidget::wait(int ms)
{
    QElapsedTimer t;
    t.start();
    while (t.elapsed() < ms)
        QCoreApplication::processEvents();
}

TreeNode *MyTreeWidget::insertChileItem(TreeNode *parentNode, int row, QString itemText)
{
    number++;
    QTreeWidgetItem* parentItem = parentNode->item; //获取父节点

    MyTreeWidgetItem* childItem = new MyTreeWidgetItem(); //创建子节点
    childItem->setNodeText(QString("child%1").arg(number));

    MyLabel* label = new MyLabel(itemText); //创建label
    childItem->m_Label = label;

    TreeNode* childData = new TreeNode; //创建子节点对象数据
    childData->parent = parentNode;
    childData->item = childItem;
    childData->nodeText = QString("child%1").arg(number);
    childData->nodeItemTest = itemText;

    parentNode->children.insert(row, childData); //父节点对象数据插入子节点

    parentItem->insertChild(row, childItem); //父节点item插入子节点item
    setItemWidget(childItem, 0, label); //item转换lable

    return childData;
}

TreeNode *MyTreeWidget::appendChileItem(TreeNode *parentNode, QString itemText)
{
    number++;
    QTreeWidgetItem* parentItem = parentNode->item;

    MyTreeWidgetItem* childItem = new MyTreeWidgetItem();
    childItem->setNodeText(QString("child%1").arg(number));

    MyLabel* label = new MyLabel(itemText);
    childItem->m_Label = label;

    TreeNode* childData = new TreeNode;
    childData->parent = parentNode;
    childData->item = childItem;
    childData->nodeText = QString("child%1").arg(number);
    childData->nodeItemTest = itemText;

    parentNode->children.append(childData);

    parentItem->addChild(childItem);
    setItemWidget(childItem, 0, label);

    return childData;
}

TreeNode* MyTreeWidget::insertTopItem(int row, QString itemText, bool isInster)
{
    number++;

    MyTreeWidgetItem* parentItem = new MyTreeWidgetItem();
    parentItem->setNodeText(QString("parent%1").arg(number));

    TreeNode* parentData = new TreeNode;
    parentData->item = parentItem;
    parentData->nodeText = QString("parent%1").arg(number);
    parentData->nodeItemTest = itemText;

    if (isInster) {

        MyLabel* label = new MyLabel(itemText);
        parentItem->m_Label = label;

        insertTopLevelItem(row, parentItem);
        setItemWidget(parentItem, 0, label);
    }


    m_ListData.insert(row, parentData);

    return parentData;
}

void MyTreeWidget::updateItem(int row, TreeNode *node)
{
    qDebug() << row << node->nodeItemTest << "aaaa";
    MyLabel* label = new MyLabel(node->nodeItemTest);
    node->item->m_Label = label;

    insertTopLevelItem(row, node->item);
//    setItemWidget(node->item, 0, label);
}

TreeNode *MyTreeWidget::appendTopItem(QString itemText)
{
    number++;

    MyTreeWidgetItem* parentItem = new MyTreeWidgetItem();
    parentItem->setNodeText(QString("parent%1").arg(number));

    MyLabel* label = new MyLabel(itemText);
    parentItem->m_Label = label;

    TreeNode* parentData = new TreeNode;
    parentData->item = parentItem;
    parentData->nodeText = QString("parent%1").arg(number);
    parentData->nodeItemTest = itemText;

    addTopLevelItem(parentItem);
    setItemWidget(parentItem, 0, label);

    m_ListData.append(parentData);

    return parentData;
}

void MyTreeWidget::removeTopItem(TreeNode *node)
{
    int index = indexOfTopLevelItem(node->item);
    takeTopLevelItem(index);
    deleteNodeByText(node->nodeText);
}

void MyTreeWidget::RecursionInsert(TreeNode *ParentNode, TreeNode *childNode)
{
    TreeNode* node = this->appendChileItem(ParentNode, childNode->nodeItemTest);
    for (int i = 0 ; i < childNode->children.size(); ++i) {
        this->RecursionInsert(node, childNode->children.at(i));
    }
}

void MyTreeWidget::expandAllItems(QTreeWidget *treeWidget, QTreeWidgetItem *item)
{
    if (item) {
        treeWidget->expandItem(item); // 展开当前项
        // 递归展开子项
        for (int i = 0; i < item->childCount(); ++i) {
            expandAllItems(treeWidget, item->child(i));
        }
    }
}

void MyTreeWidget::onScrollBarValueChanged(int value)
{
    //m_BarValueChangedTemp用于该函数在没有结束的时候又进来了如果不调用takeTopLevelItem就没事,如果没有m_BarValueChangedTemp在该函数未结束还会进入导致崩溃



    int step = 3; //滚动一次刷新三条数据

    if (value >= m_VerticalScrollBar->maximum()) {
        for (int index = 0; index < 1; ++index) {
            int itemCount = this->topLevelItemCount();

            MyTreeWidgetItem* bottomItem = dynamic_cast<MyTreeWidgetItem*>(this->topLevelItem(itemCount - 1));
            TreeNode* bottomNode = findNodeByText(bottomItem->getNodeText(), m_ListData);
            int bottomIndex = m_ListData.indexOf(bottomNode);

            if (bottomIndex + 100 < m_ListData.size()) {
                step = 1000;
            }
            else {
                step = (m_ListData.size() - 1) - bottomIndex;
            }
            for (int i = 0 ; i < step; ++i) {
                //尾部追加显示
                //获取当前底部的数据
                bottomIndex++;
                updateItem(itemCount, m_ListData.at(bottomIndex));
                itemCount++;
            }
            for (int i = 0 ; i < step; ++i) {
                takeTopLevelItem(0);
            }
        }
        qDebug() << QDateTime::currentDateTime() << "222222222222222222";
   m_VerticalScrollBar->setValue(value-5);
    }








    /*  if (m_BarValueChangedTemp) {
        qDebug() << "onScrollBarValueChanged未结束就进入";
        return;
    }
    if (m_WheelEventTemp) {
        qDebug() << "wheelEvent正在滚动中";
        return;
    }
    m_BarValueChangedTemp = true;

    qDebug() << value << m_LastScrollBarValue<<  this->topLevelItemCount();

    int step = 3; //滚动一次刷新三条数据

    if (m_LastScrollBarValue > value) {
        // 上拉
        int number = m_LastScrollBarValue - value;
        for (int index = 0; index < number; ++index) {
            MyTreeWidgetItem* topItem = dynamic_cast<MyTreeWidgetItem*>(this->topLevelItem(0));
            TreeNode* topNode = findNodeByText(topItem->getNodeText(), m_ListData);
            int topIndex = m_ListData.indexOf(topNode);

            if (topIndex - 3 > 0) {
                step = 3;
            }
            else {
                step = topIndex;
            }

            for (int i = step; i > 0; --i) {
                //尾部追加显示
                //获取当前底部的数据
                topIndex--;
              updateItem(0, m_ListData.at(topIndex));
            }

            for (int i = 0 ; i < step; ++i) {
                takeTopLevelItem(this->topLevelItemCount() - 1);
            }
        }
    }
    else {
        int number = value - m_LastScrollBarValue;
        qDebug() << QDateTime::currentDateTime() << "111111111111111";
        for (int index = 0; index < number; ++index) {
            int itemCount = this->topLevelItemCount();

            MyTreeWidgetItem* bottomItem = dynamic_cast<MyTreeWidgetItem*>(this->topLevelItem(itemCount - 1));
            TreeNode* bottomNode = findNodeByText(bottomItem->getNodeText(), m_ListData);
            int bottomIndex = m_ListData.indexOf(bottomNode);

            if (bottomIndex + 3 < m_ListData.size()) {
                step = 3;
            }
            else {
                step = (m_ListData.size() - 1) - bottomIndex;
            }
            for (int i = 0 ; i < step; ++i) {
                //尾部追加显示
                //获取当前底部的数据
                bottomIndex++;
                updateItem(itemCount, m_ListData.at(bottomIndex));
                itemCount++;
            }
                        for (int i = 0 ; i < step; ++i) {
                            takeTopLevelItem(0);
                        }
        }
        qDebug() << QDateTime::currentDateTime() << "222222222222222222";
    }

    m_ScrollBarValue = value;
    m_LastScrollBarValue = value;
    m_VerticalScrollBar->setValue(value);
    m_BarValueChangedTemp = false;*/
}

void MyTreeWidget::mouseMoveEvent(QMouseEvent *event)
{
    QTreeWidget::mouseMoveEvent(event);
}

void MyTreeWidget::mousePressEvent(QMouseEvent *event)
{
    QTreeWidget::mousePressEvent(event);
    m_CurPos = event->pos();

    if (event->buttons() & Qt::RightButton) {

        m->move((QCursor::pos()));
        m->show();
    }

}

void MyTreeWidget::mouseReleaseEvent(QMouseEvent *event)
{
    QTreeWidget::mouseReleaseEvent(event);
}

void MyTreeWidget::startDrag(Qt::DropActions supportedActions)
{
    //拖拽重写此函数下发当前拖拽对象的nodetext
    Q_UNUSED(supportedActions);

    MyTreeWidgetItem* currItem = dynamic_cast<MyTreeWidgetItem*>(this->currentItem());

    if (currItem) {
        QString text = currItem->getNodeText();

        QMimeData* mimeData = new QMimeData;
        mimeData->setData("application/labelData", text.toUtf8());

        QDrag* drag = new QDrag(this);
        drag->setMimeData(mimeData);

        m_CurrDragItem = currItem;
        drag->start(Qt::MoveAction);
    }
}

void MyTreeWidget::dragEnterEvent(QDragEnterEvent *event)
{
    QTreeWidget::dragEnterEvent(event);
    // 检查拖拽数据格式
    if (event->mimeData()->hasFormat("application/labelData")) {
        event->acceptProposedAction();
    }
}

void MyTreeWidget::dragMoveEvent(QDragMoveEvent *event)
{
    QTreeWidget::dragMoveEvent(event);

    // 检查拖拽数据格式
    if (event->mimeData()->hasFormat("application/labelData")) {
        event->acceptProposedAction();
        m_DragMovePos = event->pos();
        viewport()->update();
    }



}

void debugTreeData(TreeNode* node, int depth = 0) {
    QString kongge;
    for (int i = 0; i < depth; ++i) {
        kongge.append("----");
    }
    qDebug() << kongge << node->nodeItemTest << node->nodeText;
    for (int i = 0; i < node->children.size(); ++i) {
        debugTreeData(node->children.at(i), depth + 1); // 递归时深度加1
    }
}




void MyTreeWidget::dropEvent(QDropEvent *event)
{
    // 检查拖拽数据格式
    if (event->mimeData()->hasFormat("application/labelData")) {
        QString itemData = event->mimeData()->data("application/labelData");

        MyTreeWidgetItem* targetItem = dynamic_cast<MyTreeWidgetItem*>(itemAt(event->pos()));
        m_CurrDragItem = nullptr;
        if (!targetItem) {
            return;
        }

        qDebug() << itemData << "-------------------" << targetItem->getNodeText();
        TreeNode* targetNode = findNodeByText(targetItem->getNodeText(), m_ListData);
        TreeNode* sourceNode = findNodeByText(itemData, m_ListData);

        if (sourceNode && targetNode) {
            // 找到了匹配的节点,可以对其进行操作
            qDebug() << "找到了匹配的节点,可以对其进行操作" << targetNode->nodeText << sourceNode->nodeText;
            if (sourceNode->parent == nullptr && targetNode->parent == nullptr) { //父与父
                //当前是父节点与父节点直接拖拽
                int targetRow = this->indexOfTopLevelItem(targetNode->item);
                int sourceRow = this->indexOfTopLevelItem(sourceNode->item);
                if (targetRow != sourceRow) {
                    qDebug() << "1111111111111111" << targetRow << sourceRow;
                    //在目标源下插入一行

                    if (sourceNode->children.isEmpty()) {
                        this->insertTopItem(targetRow + 1, sourceNode->nodeItemTest);
                        //删除来源item
                        this->removeTopItem(sourceNode);
                    }
                    else if (!sourceNode->children.isEmpty()) {
                        //如果来源里面有子节点就需要递归插入,先查入头节点
                        TreeNode* newParentNode = this->insertTopItem(targetRow + 1, sourceNode->nodeItemTest);
                        for (int i = 0 ; i < sourceNode->children.size(); ++i) {
                            this->RecursionInsert(newParentNode, sourceNode->children.at(i));
                        }
                        //删除来源item
                        this->removeTopItem(sourceNode);
                        deleteNodeByText(sourceNode->nodeText);
                    }
                    else {
                        qDebug() << "未知动作!!!!!!!!!!!!!!!!!!!!!!!!";
                    }
                }
            }
            else if (sourceNode->parent != nullptr && targetNode->parent != nullptr) { //子与子
                //双方都是子节点
                int targetRow = targetNode->parent->item->indexOfChild(targetNode->item);
                int sourceRow = sourceNode->parent->item->indexOfChild(sourceNode->item);
                qDebug() << "44444444444444" << targetRow << sourceRow << sourceNode->nodeItemTest << targetNode->nodeItemTest;

                if (recursiveFindNodeByText(targetNode->nodeText, sourceNode) != nullptr) {
                    //来源节点中不能有目标节点
                    return;
                }
                if (sourceNode->children.isEmpty()) {
                    this->insertChileItem(targetNode->parent, targetRow + 1, sourceNode->nodeItemTest);

                    sourceNode->parent->item->removeChild(sourceNode->item);
                    deleteChildNode(sourceNode->parent, sourceNode->nodeText);
                }
                else if (!sourceNode->children.isEmpty()) {
                    TreeNode* newParentNode = this->insertChileItem(targetNode->parent, targetRow + 1, sourceNode->nodeItemTest);
                    for (int i = 0 ; i < sourceNode->children.size(); ++i) {
                        this->RecursionInsert(newParentNode, sourceNode->children.at(i));
                    }

                    //删除来源item
                    sourceNode->parent->item->removeChild(sourceNode->item);
                    deleteChildNode(sourceNode->parent, sourceNode->nodeText);
                }
            }
            else if (sourceNode->parent != nullptr && targetNode->parent == nullptr) { //子与父
                //来源是子节点,指向端是父节点
                int targetRow = this->indexOfTopLevelItem(targetNode->item);
                int sourceRow = sourceNode->parent->item->indexOfChild(sourceNode->item);
                qDebug() << "222222222222222222" << targetRow << sourceRow << sourceNode->parent->nodeItemTest << sourceNode->nodeItemTest;

                if (sourceNode->children.isEmpty()) {
                    this->insertTopItem(targetRow + 1, sourceNode->nodeItemTest);
                    sourceNode->parent->item->removeChild(sourceNode->item);

                    deleteChildNode(sourceNode->parent, sourceNode->nodeText);
                }
                else if (!sourceNode->children.isEmpty()) {
                    TreeNode* newParentNode = this->insertTopItem(targetRow + 1, sourceNode->nodeItemTest);
                    for (int i = 0 ; i < sourceNode->children.size(); ++i) {
                        this->RecursionInsert(newParentNode, sourceNode->children.at(i));
                    }
                    //删除来源item
                    sourceNode->parent->item->removeChild(sourceNode->item);

                    deleteChildNode(sourceNode->parent, sourceNode->nodeText);
                }
            }
            else if (sourceNode->parent == nullptr && sourceNode->children.isEmpty() && targetNode->parent != nullptr) { //空父 与子
                //来源父节点 且没有子节点 且指向端是子节点
                int targetRow = targetNode->parent->item->indexOfChild(targetNode->item);
                int sourceRow = this->indexOfTopLevelItem(sourceNode->item);
                qDebug() << "333333333333" << targetRow << sourceRow << sourceNode->nodeItemTest << targetNode->nodeItemTest;

                this->insertChileItem(targetNode->parent, targetRow + 1, sourceNode->nodeItemTest);

                this->removeTopItem(sourceNode);
            }
            else if (sourceNode->parent == nullptr && !sourceNode->children.isEmpty() && targetNode->parent != nullptr) { //父多子 与子
                //来源父节点 有子节点 且指向端是子节点

                if (recursiveFindNodeByText(targetNode->nodeText, sourceNode) != nullptr) {
                    //来源节点中不能有目标节点
                    return;
                }
                int targetRow = targetNode->parent->item->indexOfChild(targetNode->item);
                int sourceRow = this->indexOfTopLevelItem(sourceNode->item);
                qDebug() << "5555555555555" << targetRow << sourceRow << sourceNode->nodeItemTest << targetNode->nodeItemTest;

                TreeNode* newParentNode = this->insertChileItem(targetNode->parent, targetRow + 1, sourceNode->nodeItemTest);
                for (int i = 0 ; i < sourceNode->children.size(); ++i) {
                    this->RecursionInsert(newParentNode, sourceNode->children.at(i));
                }

                //删除来源item
                this->removeTopItem(sourceNode);
                deleteNodeByText(sourceNode->nodeText);
            }

        } else {
            // 未找到匹配的节点
            qDebug() << "未找到匹配的节点2";
        }
    }

    //    for (int i = 0; i < m_ListData.size(); ++i) {
    //        debugTreeData(m_ListData.at(i));
    //    }
}

void MyTreeWidget::paintEvent(QPaintEvent *event)
{
    // qDebug() << "123123123123";
    QTreeWidget::paintEvent(event);

    QPainter painter(viewport());

    if (m_CurrDragItem != nullptr) {
        QModelIndex hoveredIndex = indexAt(m_DragMovePos);

        MyTreeWidgetItem* mcurrMoveItem = dynamic_cast<MyTreeWidgetItem*>(itemFromIndex(hoveredIndex));
        if (mcurrMoveItem) {

            QRect rect = visualRect(hoveredIndex); // 获取悬停项的矩形区域
            QString labelText = mcurrMoveItem->m_Label->text();
            qDebug()<< "---------------" << labelText << rect << "-----------------";

            painter.setPen(QPen(QColor(34, 142, 243), 2)); // 设置线条颜色和粗细

            // 绘制线条
            painter.drawLine(rect.x() + 6, rect.y() + rect.height(), rect.x() + rect.width() - 6, rect.y() + rect.height()); // 绘制底部线条

            // 绘制头部小圆圈
            painter.setBrush(QBrush(QColor(34, 142, 243))); // 设置圆圈颜色
            painter.drawEllipse(QPoint(rect.x() + 2, rect.y() + rect.height()), 2, 2); // 绘制小圆圈

            // 绘制尾部小圆圈
            painter.drawEllipse(QPoint(rect.x() + rect.width()-2, rect.y() + rect.height()), 2, 2); // 绘制小圆圈
        }
    }
}

void MyTreeWidget::wheelEvent(QWheelEvent *event)
{
    if (m_WheelEventTemp) {
        qDebug() << "wheelEvent未结束就进入";
        return;
    }
    if (m_VerticalScrollBar->isSliderDown()) {
        qDebug() << "正在拖拽滚动条";
        return;
    }
    m_WheelEventTemp = true;

    qDebug() << "1111111111111111111" << event->angleDelta().y() <<this->topLevelItemCount() << m_VerticalScrollBar->isSliderDown();

    int number = qAbs(event->angleDelta().y()) / 120;
    int step = 3; //滚动一次刷新三条数据

    for (int i = 0; i < number; ++i) {
        if (event->angleDelta().y() > 0) {
            MyTreeWidgetItem* topItem = dynamic_cast<MyTreeWidgetItem*>(this->topLevelItem(0));
            TreeNode* topNode = findNodeByText(topItem->getNodeText(), m_ListData);
            int topIndex = m_ListData.indexOf(topNode);

            if (topIndex - 3 > 0) {
                step = 3;
            }
            else {
                step = topIndex;
            }

            for (int i = step; i > 0; --i) {
                //尾部追加显示
                //获取当前底部的数据
                topIndex--;
                updateItem(0, m_ListData.at(topIndex));
            }

            for (int i = 0 ; i < step; ++i) {
                takeTopLevelItem(this->topLevelItemCount() - 1);
            }

            if (m_ScrollBarValue > 0) {
                m_ScrollBarValue -= double(m_VerticalScrollBar->maximum()) / double(m_ListData.size() / (step == 0 ? 1 : step));
                qDebug() << "向上滚动" << topIndex <<m_ScrollBarValue;
                m_VerticalScrollBar->setValue(m_ScrollBarValue);
            }
        }
        else {
            int itemCount = this->topLevelItemCount();

            MyTreeWidgetItem* bottomItem = dynamic_cast<MyTreeWidgetItem*>(this->topLevelItem(itemCount - 1));
            TreeNode* bottomNode = findNodeByText(bottomItem->getNodeText(), m_ListData);
            int bottomIndex = m_ListData.indexOf(bottomNode);

            if (bottomIndex+3 < m_ListData.size()) {
                step = 3;
            }
            else {
                step = (m_ListData.size() - 1) - bottomIndex;
            }


            for (int i = 0 ; i < step; ++i) {
                //尾部追加显示
                //获取当前底部的数据
                bottomIndex++;
                updateItem(itemCount, m_ListData.at(bottomIndex));
                itemCount++;
            }
            for (int i = 0 ; i < step; ++i) {
                takeTopLevelItem(0);
            }

            if (m_VerticalScrollBar->value() < m_VerticalScrollBar->maximum()) {
                m_ScrollBarValue += double(m_VerticalScrollBar->maximum()) / double(m_ListData.size() / (step == 0 ? 1 : step));
                qDebug() << "向下滚动" << bottomIndex <<m_ScrollBarValue;
                m_VerticalScrollBar->setValue(m_ScrollBarValue);
            }
        }
    }
    m_WheelEventTemp = false;
}


MyTreeWidgetItem::MyTreeWidgetItem()
{

}

void MyTreeWidgetItem::setNodeText(QString str)
{
    m_Text = str;
}

QString MyTreeWidgetItem::getNodeText()
{
    return m_Text;
}


MyLabel::MyLabel(QString text, QWidget *parent)
    : QLabel(parent)
{
    setText(text);
    installEventFilter(this);

    enterStyle = "background-color: rgb(225, 243, 255);";
    leaveStyle = "background-color: white;";
    selectStyle = "background-color: rgb(204, 232, 255);";
}

bool MyLabel::eventFilter(QObject *watched, QEvent *event)
{
    return QWidget::eventFilter(watched, event);
}

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    m_MyTreeWidget = new MyTreeWidget();

    //    if (1) {

    qDebug() << QDateTime::currentDateTime() << "111111111111111";
    for (int i = 0; i < 100000; ++i) {
        if (i >= 100) {
            m_MyTreeWidget->insertTopItem(i, QString("父节点%1").arg(i+1), false);
        }
        else {
            m_MyTreeWidget->insertTopItem(i, QString("父节点%1").arg(i+1));
        }
    }
    qDebug() << QDateTime::currentDateTime() << "2222222222222";

    //    for (int i = 0; i < 500; ++i) {
    //        m_MyTreeWidget->insertChileItem(m_MyTreeWidget->m_ListData.at(0), m_MyTreeWidget->m_ListData.at(0)->item->childCount(),QString("子节点%1").arg(i+1));
    //    }

    //    for (int i = 0; i < 500; ++i) {
    //        m_MyTreeWidget->insertChileItem(m_MyTreeWidget->m_ListData.at(1), m_MyTreeWidget->m_ListData.at(1)->item->childCount(),QString("子节点%1").arg(i+1));
    //    }
    //    for (int i = 0; i < 500; ++i) {
    //        m_MyTreeWidget->insertChileItem(m_MyTreeWidget->m_ListData.at(2), m_MyTreeWidget->m_ListData.at(2)->item->childCount(),QString("子节点%1").arg(i+1));
    //    }
    //    for (int i = 0; i < 500; ++i) {
    //        m_MyTreeWidget->insertChileItem(m_MyTreeWidget->m_ListData.at(3), m_MyTreeWidget->m_ListData.at(3)->item->childCount(),QString("子节点%1").arg(i+1));
    //    }


    ui->gridLayout->addWidget(m_MyTreeWidget);



    // 在您的代码中调用此函数来展开所有项
    //m_MyTreeWidget->expandAllItems(m_MyTreeWidget, m_MyTreeWidget->invisibleRootItem());
}

Widget::~Widget()
{
    delete ui;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现QTreeWidget之间的拖拽功能,需要进行以下步骤: 1.在源QTreeWidget中启用拖拽功能:调用setDragEnabled(True)方法来启用拖拽功能。 2.实现startDrag方法:当用户开始拖拽一个项目时,调用startDrag方法。该方法创建QDrag对象并将要拖动的数据存储在其中。 3.在目标QTreeWidget中启用放置功能:调用setAcceptDrops(True)方法来启用放置功能。 4.实现dragEnterEvent方法:当用户将拖动的项目拖到目标QTreeWidget上时,调用dragEnterEvent方法。该方法检查拖动的数据是否与目标兼容,并在兼容的情况下接受拖放操作。 5.实现dropEvent方法:当用户在目标QTreeWidget上放置拖动的项目时,调用dropEvent方法。该方法将项目添加到目标QTreeWidget中。 下面是一个示例代码: ```python class TreeWidget(QTreeWidget): def __init__(self, parent=None): super(TreeWidget, self).__init__(parent) self.setAcceptDrops(True) self.setDragEnabled(True) def startDrag(self, event): item = self.currentItem() if item: data = QByteArray() stream = QDataStream(data, QIODevice.WriteOnly) stream.writeQString(item.text(0)) mimeData = QMimeData() mimeData.setData('application/x-qabstractitemmodeldatalist', data) drag = QDrag(self) drag.setMimeData(mimeData) drag.exec_() def dragEnterEvent(self, event): if event.mimeData().hasFormat('application/x-qabstractitemmodeldatalist'): event.accept() else: event.ignore() def dropEvent(self, event): if event.mimeData().hasFormat('application/x-qabstractitemmodeldatalist'): data = event.mimeData().data('application/x-qabstractitemmodeldatalist') stream = QDataStream(data, QIODevice.ReadOnly) text = '' stream >> text item = QTreeWidgetItem(self) item.setText(0, text) event.accept() else: event.ignore() ``` 在上面的代码中,我们继承了QTreeWidget类,并重写了startDrag,dragEnterEvent和dropEvent方法。startDrag方法创建了一个QMimeData对象并存储要拖动的数据。dragEnterEvent方法检查拖动的数据是否与目标兼容,并在兼容的情况下接受拖放操作。dropEvent方法将项目添加到目标QTreeWidget中。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值