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); //创建labelchildItem->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插入子节点itemsetItemWidget(childItem, 0, label); //item转换lablereturn 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)
{//拖拽重写此函数下发当前拖拽对象的nodetextQ_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);//删除来源itemthis->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));}//删除来源itemthis->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));}//删除来源itemsourceNode->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));}//删除来源itemsourceNode->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));}//删除来源itemthis->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.xdnf.cn/news/138963.html

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

相关文章

JAVA面经整理(2)

一)解决哈希冲突的方法有哪些&#xff1f; 哈希冲突指的是在哈希表中&#xff0c;不同的键值映射到了相同的哈希桶&#xff0c;也就是数组索引&#xff0c;导致键值对的冲突 1)设立合适的哈希函数:通过哈希函数计算出来的地址要均匀的分布在整个空间中 2)负载因子调节: 2.1)开放…

【vue】vue+easyPlayer 实现宫格布局及视频播放

由于业务需要&#xff0c;ant-design-vue框架集成easyPlayer.js作为视频播放器。EasyPlayer.js H5播放器&#xff0c;是一款能够同时支持HTTP、HTTP-FLV、HLS&#xff08;m3u8&#xff09;、WS视频直播与视频点播等多种协议&#xff0c;支持H.264、H.265、AAC等多种音视频编码格…

王江涛十天搞定考研词汇

学习目标&#xff1a; 考研词汇 学习内容&#xff1a; 2023-9-17 第一天考研词汇 学习时间&#xff1a; 开始:2023-9-17 结束:进行中 学习产出&#xff1a; 2023-9-17intellect智力&#xff1b;知识分子intellectual智力的&#xff1b;聪明的intellectualize使...理智化&a…

堆的实现(C版)

普通的二叉树是不适合用数组来存储的&#xff0c;因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结构存储。现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储&#xff0c;需要注意的是这里的堆和操作系统虚拟进程地址空间中的堆是两回事&#xff0c;一个是…

软件设计模式系列之十二——外观模式

在软件设计中&#xff0c;经常会遇到需要与复杂子系统进行交互的情况。为了简化客户端与子系统之间的交互&#xff0c;提高系统的可维护性和可用性&#xff0c;外观模式应运而生。外观模式&#xff08;Facade Pattern&#xff09;是一种结构型设计模式&#xff0c;它提供一个统…

激活函数总结(四十一):激活函数补充(ShiLU、ReLUN)

激活函数总结&#xff08;四十一&#xff09;&#xff1a;激活函数补充 1 引言2 激活函数2.1 ShiLU激活函数2.2 ReLUN激活函数 3. 总结 1 引言 在前面的文章中已经介绍了介绍了一系列激活函数 (Sigmoid、Tanh、ReLU、Leaky ReLU、PReLU、Swish、ELU、SELU、GELU、Softmax、Sof…

腾讯云16核服务器性能测评_轻量和CVM配置大全

腾讯云16核服务器配置大全&#xff0c;CVM云服务器可选择标准型S6、标准型SA3、计算型C6或标准型S5等&#xff0c;目前标准型S5云服务器有优惠活动&#xff0c;性价比高&#xff0c;计算型C6云服务器16核性能更高&#xff0c;轻量16核32G28M带宽优惠价3468元15个月&#xff0c;…

简单的手机电脑无线传输方案@固定android生成ftp的IP地址(android@windows)

文章目录 abstractwindows浏览android文件环境准备客户端软件无线网络链接步骤其他方法 手机浏览电脑文件公网局域网everythingpython http.server 高级:固定android设备IP准备检查模块是否生效 windows 访问ftp服务器快捷方式命令行方式双击启动方式普通快捷方式映射新的网络位…

第六次面试、第一次复试

第六面&#xff1a; hr迟到&#xff0c;说是搞错了以为线下&#xff0c;我打电话过去才开始&#xff0c;问我想电话面还是视频&#xff0c;果断电话面 自我介绍 介绍了一下公司的工作 ................. 项目拷打&#xff1a; grpc数据如何传输的如何调用两个接口如何获取…

Android 12 源码分析 —— 应用层 六(StatusBar的UI创建和初始化)

Android 12 源码分析 —— 应用层 六&#xff08;StatusBar的UI创建和初始化) 在前面的文章中,我们分别介绍了Layout整体布局,以及StatusBar类的初始化.前者介绍了整体上面的布局,后者介绍了三大窗口的创建的入口处,以及需要做的准备工作.现在我们分别来细化三大窗口的UI创建和…

利用大模型知识图谱技术,告别繁重文案,实现非结构化数据高效管理

我&#xff0c;作为一名产品经理&#xff0c;对文案工作可以说是又爱又恨&#xff0c;爱的是文档作为嘴替&#xff0c;可以事事展开揉碎讲清道明&#xff1b;恨的是只有一个脑子一双手&#xff0c;想一边澄清需求一边推广宣传一边发布版本一边申报认证实在是分身乏术&#xff0…

模块化软件架构:使用单体、微服务和模块化单体的优缺点

【squids.cn】 全网zui低价RDS&#xff0c;免费的迁移工具DBMotion、数据库备份工具DBTwin、SQL开发工具等 近年来&#xff0c;微服务架构在大多数软件解决方案中已经处于领先地位&#xff0c;而且在很多情况下&#xff0c;它经常被选择为我们开始开发的架构。然而&#xff0c…

解密list的底层奥秘

&#x1f388;个人主页:&#x1f388; :✨✨✨初阶牛✨✨✨ &#x1f43b;强烈推荐优质专栏: &#x1f354;&#x1f35f;&#x1f32f;C的世界(持续更新中) &#x1f43b;推荐专栏1: &#x1f354;&#x1f35f;&#x1f32f;C语言初阶 &#x1f43b;推荐专栏2: &#x1f354;…

软件定制APP开发步骤分析|小程序

软件定制APP开发步骤分析|小程序 软件定制开发步骤&#xff1a; 1.需求分析&#xff1a; 这是软件定制开发的第一步&#xff0c;也是最关键的一步。在这个阶段&#xff0c;软件开发团队需要与客户进行沟通&#xff0c;了解客户的具体需求和期望。通过讨论和交流&#xff0c;确…

前后端分离管理系统day01---Springboot+MybatisPlus

目录 目录 软件 基础知识 一创建后端项目 注意&#xff1a; 删除多余项 创建测试类 二 加入mybatis-plus依赖支持 1.加入依赖码 2.创建数据库实例/创建用户表/插入默认数据 创建数据库实例 创建表 插入数据 3.配置yml文件 注意&#xff1a;wms01必须是数据库的名字&…

[maven] scopes 管理 profile 测试覆盖率

[maven] scopes & 管理 & profile & 测试覆盖率 这里将一些其他的特性和测试覆盖率&#xff08;主要是 jacoco&#xff09; scopes maven 的 scope 主要就是用来限制和管理依赖的传递性&#xff0c;简单的说就是&#xff0c;每一个 scope 都有其对应的特性&…

WebGL 初始化着色器

目录 前言 初始化着色器的7个步骤 创建着色器对象&#xff08;gl.createShader&#xff08;&#xff09;&#xff09; gl.createShader&#xff08;&#xff09;规范 gl.deleteShader&#xff08;&#xff09;规范 指定着色器对象的代码&#xff08;gl.shaderSource&…

Linux高并发服务器开发第四章:Linux网络编程

1. 网络结构模式 C/S结构 简介 服务器 - 客户机&#xff0c;即 Client - Server&#xff08;C/S&#xff09;结构。C/S 结构通常采取两层结构。服务器负责数据的管理&#xff0c;客户机负责完成与用户的交互任务。客户机是因特网上访问别人信息的机器&#xff0c;服务器则是…

【结构型】代理模式(Proxy)

目录 代理模式(Proxy)适用场景代理模式实例代码&#xff08;Java&#xff09; 代理模式(Proxy) 为其他对象提供一种代理以控制对这个对象的访问。Proxy 模式适用于在需要比较通用和复杂的对象指针代替简单的指针的时候。 适用场景 远程代理 (Remote Proxy) 为一个对象在不同…

10分钟设置免费海外远程桌面

前言 本教程将向您介绍如何使用 Amazon Lightsail 服务的免费套餐轻松搭建属于您的远程桌面。依托于 Amazon 全球可用区&#xff0c;您可以在世界各地搭建符合您配置需求的远程桌面。 本教程需要先拥有亚马逊云科技海外账户。现在注册亚马逊云科技账户可以享受12个月免费套餐…