Qt重写QTreeWidget实现拖拽

news2024/11/27 1:45:16

介绍

此文章记录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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1028990.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Postman应用——Pre-request Script和Test Script脚本介绍

文章目录 Pre-request Script所在位置CollectionFolderRequest Test Script所在位置CollectionFolderRequest Pre-request Script&#xff08;前置脚本&#xff09;&#xff1a;可以使用在Collection、Folder和Request中&#xff0c;并在Request请求之前执行&#xff0c;可用于…

整站抓取的神器

整站抓取的神器 TeleportUltraWebZipMihov Picture DownloaderWinHTTrack HTTrackMaxprogWebDumper 五款整站抓取的工具 TeleportUltra Teleport Ultra所能做的&#xff0c;不仅仅是离线浏览某个网页(让你离线快速浏览某个网页的内容当然是它的一项重要功能)&#xff0c;它可…

链表oj题2(Leetcode)(牛客)——合并两个有序链表;判断回文链表;链表分割

链表oj题2&#xff08;Leetcode&#xff09;&#xff08;牛客&#xff09; 一&#xff0c;合并两个有序链表1.1分析2.2代码 二&#xff0c;链表的回文结构2.1分析2.2代码 三&#xff0c;链表分割3.1分析3.2代码 四&#xff0c;小结 一&#xff0c;合并两个有序链表 合并两个有…

vue的工程化开发全流程,以及开发中的细节语法和用法

vue的工程化开发全流程 文章目录 vue的工程化开发全流程1、工程化开发&脚手架Vue CLI1.1、前言1.2、脚手架Vue CLI1.3、脚手架目录文件介绍&项目运行流程1.4、组件化开发&根组件1.5、普通组件的注册使用 2、工程化开发细则2.1、组件的三大组成部分2.2、组件的样式冲…

yolov8在设置amp=False 之后map 训练依旧为0 解决办法

可能原因 是cuda 版本导致的半精度浮点数计算出现nan的bug 解决办法 设置ampFalse 就是不使用混合精度训练。或者直接改用低版本的cuda和pytorch。cuda11.6 以下 直接有效也有可能是学习率过高 降低学习率 设置ampFalse之后还是存在问题 是因为yolov8库的问题 按以下修改 找到…

RKDevTool打包成update.img

(1) 准备好RKDevTool_Release和rockdev目录相关的文件工具 (2) 在rockdev建立image目录 (3) 往image填入和package-file有关的img文件 (4) 运行需要的xxx_mkupdate文件,直到生成想要的update.img (5) 导入烧录工具,文件大,需要等待一段时间,进入MASKROM模式,点击烧录upd…

我只是个小市民——经受不住宏大叙事

我只是个小市民 ——经受不住宏大叙事 作日看到一个曾经的朋友在朋友圈晒出了在某地旅游的照片&#xff0c;照片清新动人&#xff0c;狠狠地打入了我的内心&#xff0c;我知道光靠手机是拍不出来这样唯美的画面的&#xff0c;于是我问她是如何弄出这么好看的照片&#xff0c;…

淘宝商品sku数据接口监控

淘宝商品sku数据接口监控是指通过API接口对淘宝店铺的商品库存、价格、销售量等数据进行实时监控&#xff0c;以便商家能够及时调整销售策略、优化库存、了解竞争对手的动态等。 监控的具体功能包括&#xff1a; 实时监控商品库存和价格变化&#xff0c;及时调整自己的销售策…

数据结构前瞻

集合框架 JAVA的集合框架是定义在java.util包下的一组接口和实现类&#xff0c;用于将多个元素置于一个单元中&#xff0c;对这些元素进行快速&#xff0c;便捷的存储&#xff0c;减速和管理&#xff0c;即增删查改 下面的格子&#xff0c;黄色代表接口&#xff0c;蓝色代表抽…

监控办公室电脑用什么软件?

监控办公室员工电脑的工作情况是一项非常重要的管理任务&#xff0c;它可以帮助企业管理者及时发现员工的问题、提高工作效率和保障企业安全。以下是一些具体的方法和步骤&#xff0c;供您参考&#xff1a; 1、安装监控软件 在监控员工电脑之前&#xff0c;您需要先安装一款专…

Redis核心数据结构实战与高性能解析

目录 一、安装Redis 二、Redis线程与高性能 2.1 Redis是单线程么&#xff1f; 2.2 Redis读写是单线程为何这么快&#xff1f; 2.3 Redis如何处理并发操作命令&#xff1f; 三、核心数据结构实战 3.1 字符串常用操作实战 SET 存入键值对 SETNX SETEX MSET 批量存入键…

vue3中两个el-select下拉框选项相互影响

vue3中两个el-select下拉框选项相互影响 1、开发需求2、代码2.1 定义hooks文件2.2 在组件中使用 1、开发需求 如图所示&#xff0c;在项目开发过程中&#xff0c;遇到这样一个需求&#xff0c;常规时段中选中的月份在高峰时段中是禁止选择的状态&#xff0c;反之亦然。 2、代…

《Cesium 进阶知识点》- 关闭天空盒,自定义背景图

效果 关键代码 1.代码第 4 行&#xff0c;初始化时配置 webgl.alpha 为 true&#xff1b; 2.代码第 8 行&#xff0c;不显示天空盒&#xff1b; 3.代码第 9 行&#xff0c;背景色透明&#xff1b; const viewer new Cesium.Viewer(cesiumContainer, {contextOptions: {…

室内导航制作:从背景到实施效益的全面解析

室内导航市场需求的增长主要源于两方面&#xff1a;一是人们对便捷生活的追求&#xff0c;二是物联网、大数据、人工智能等技术的发展。在日常生活中&#xff0c;我们经常需要在复杂的环境中进行定位和导航&#xff0c;比如大型购物商场、机场、车站等。传统的室外导航方式无法…

雷龙CS SD NAND(贴片式TF卡)性能体验及应用

前段时间有幸得到了雷龙出品的贴片式的TF卡的芯片及转接板&#xff0c;从而对其产品进行了相应的了解和测评。 从获得的相关资料看&#xff0c;雷龙出品的贴片式芯片分为两类&#xff0c;即BOW型和AOW型&#xff0c;其中BOW型为第一代产品&#xff0c;属商业级&#xff1b;AOW…

评价实施范围

声明 本文是学习GB-T 42874-2023 城市公共设施服务 城市家具 系统建设实施评价规范. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本文件确立了城市家具系统建设实施的评价原则、评价流程&#xff0c;给出了评价指标&#xff0c;描述了 方…

Zabbix“专家坐诊”第204期问答汇总

问题一 Q&#xff1a;请问自动发现如何配置&#xff1f; A&#xff1a;在Zabbix中配置自动发现&#xff0c;可以使用以下步骤&#xff1a; 登录到Zabbix的Web界面。 确保您具有管理员或具有适当权限的用户角色。 导航到“配置”菜单&#xff0c;然后选择“自动发现”。 点击…

虹科案例 | LIN/CAN总线汽车零部件测试方案

文章来源&#xff1a;虹科汽车电子 点此阅读原文 虹科的LIN/CAN总线汽车零部件测试方案是一款优秀的集成套装&#xff0c;基于Baby-LIN系列产品&#xff0c;帮助客户高效完成在测试、生产阶段车辆零部件质量、功能、控制等方面的检测工作。 1、汽车零部件测试的重要性&#xf…

基于javaweb的顶岗实习管理系统(jsp+servlet)

系统简介 本项目采用eclipse工具开发&#xff0c;jspservletjquery技术编写&#xff0c;数据库采用的是mysql&#xff0c;navicat开发工具。 三个角色&#xff1a;管理员&#xff0c;教师&#xff0c;学生 模块简介 管理员&#xff1a; 1、登录 2、学生管理 3、公告管理 …

软考之软件设计师考试总结(内附资料)

今年5月27日参加的软考&#xff0c;虽然研究生专业已经和计算机无缘了&#xff0c;但是只要想学&#xff0c;就没有什么能够阻挡。 参加软考的初衷只是因为&#xff0c;&#xff0c;&#xff0c;辽宁省软考它不要钱&#xff0c;不要钱的证书咱不白嫖一个说不过去&#xff0c;先…