QTreeWidget实现节点拖拽

前言

刚进公司没多久,老大就让做一个实现拖放的小插件,要求可拖拽,可缩放。从网上查了很多资料,也看了别人写的demo,总算摸索出一些门道,界面布局有些丑,希望大家不要介意,下面分享给大家,如有疑问,可评论,大家一起学习!

效果

在这里插入图片描述

核心代码

QCTreeWidget.cpp

// 左侧项目树
QCTreeWidget::QCTreeWidget(const QString& text, QWidget *parent)
: QTreeWidget(parent)
{
    this->setSelectionMode(QAbstractItemView::ExtendedSelection);
    this->setDragEnabled(true);
    this->setAcceptDrops(true);
    this->setDefaultDropAction(Qt::MoveAction);
    //this->setDragDropMode(QAbstractItemView::DragDrop);
    this->setDragDropMode(QAbstractItemView::InternalMove);
}

void QCTreeWidget::mousePressEvent(QMouseEvent *e)
{
    QTreeWidgetItem *item = currentItem();
    if (item == NULL)
        return;

    QString pValue = QString::number(int((void*)item));
    QByteArray itemData;
    itemData = QVariant(pValue).toByteArray();

    QMimeData *mimeData = new QMimeData;
    mimeData->setData("application/x-qabstractitemmodeldatalist", itemData);

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

    hide();

    Qt::DropAction dropAction = drag->exec(Qt::CopyAction | Qt::MoveAction);

    if (dropAction == Qt::MoveAction)
        close();
    else
        show();
}

void QCTreeWidget::dragEnterEvent(QDragEnterEvent *event)
{
    QWidget *source =  qobject_cast<QCTreeWidget *>(event->source());
    if (source /*&& source != this*/)
    {
        event->setDropAction(Qt::MoveAction);
        /*event->setDropAction(Qt::MoveAction);  */
        event->accept();
    }
}

void QCTreeWidget::dragMoveEvent(QDragMoveEvent *event)
{
    QWidget *source =  qobject_cast<QCTreeWidget *>(event->source());
    if (source /*&& source != this*/)
    {
        event->setDropAction(Qt::MoveAction);
        event->accept();
    }
}

void QCTreeWidget::dropEvent(QDropEvent *event)
{
    QCTreeWidget *source =  qobject_cast<QCTreeWidget *>(event->source());
    if (source /*&& source != this*/)
    {
        QCTreeWidget *source = qobject_cast<QCTreeWidget *>(event->source());
        if(source)
        {
            QTreeWidgetItem *item = this->itemAt(event->pos()); //当前位置的item
            if( item == nullptr)   //如果放下的位置没有item,则退出,没有这句话死机!
                return;
			//如果“放下位置的item是顶层item,且原来的是顶层”或者“放下的不是顶层,且原来也不是顶层”
            if( -1 == this->indexOfTopLevelItem(item) && (-1 == this->indexOfTopLevelItem(currentItem())) ||
                    -1 != this->indexOfTopLevelItem(item) && (-1 != this->indexOfTopLevelItem(currentItem())))
 
            {
                qDebug()<< QStringLiteral("放下的文本是: ")<<event->mimeData()->text();
                item->setText(currentColumn(),event->mimeData()->text());
                event->setDropAction(Qt::MoveAction);
                event->accept();
            }
        }
    }
}

void QCTreeWidget::startDrag(Qt::DropActions /*supportedActions*/)
{
    QMimeData *mimeData = new QMimeData;
    mimeData->setText("fasfas");
    QDrag *drag = new QDrag((QWidget*)(this));
    drag->setMimeData(mimeData);
    drag->exec(Qt::MoveAction);//注意这里一定要是MoveAction
}

void QCTreeWidget::mouseMoveEvent(QMouseEvent *e)
{
    if(e->buttons() & Qt::LeftButton)
    {
        //int distance = (e->pos() - _startPos).manhattanLength();
        int distance = e->pos().manhattanLength();
        if(distance >= QApplication::startDragDistance())   //当拖动距离大于一个推荐抖动距离时,表示同意已经拖动操作了
            performDrag();
    }
    QTreeWidget::mouseMoveEvent(e);
}

void QCTreeWidget::performDrag()
{
    QTreeWidgetItem *item = currentItem();
    int column = currentColumn();
    if(item)  //必须是非顶层item才可以移动数据
// if(item)
    {
        QMimeData *mineData = new QMimeData;
        if(column != 4)   //只有第四列才可以移动数据
            return;
        mineData->setText(item->text(column));
        qDebug()<<item->text(column);

        QDrag *drag = new QDrag(this);
        drag->setMimeData(mineData);
        drag->exec(Qt::MoveAction);
    }
}

DragTreeWidget.cpp

DragTreeWidget::DragTreeWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::DragTreeWidget)
{
    ui->setupUi(this);

    // 开启鼠标跟踪
    setMouseTracking(true);

    ui->treeWidget->setColumnCount(1); //设置列数
    ui->treeWidget->setColumnWidth(0, 200); //设置列宽
    ui->treeWidget->setHeaderHidden(true); //隐藏标题栏

    //启用拖放
    ui->treeWidget->setDragEnabled(true);
    //设置拖放
    ui->treeWidget->setAcceptDrops(true);

    //设置显示将要被放置的位置
    //ui->treeWidget->setDropIndicatorShown(true);

    //设置拖放模式为移动项目,否则为复制项目
    ui->treeWidget->setDragDropMode(QAbstractItemView::InternalMove);

    //设置item进入编辑的方式
    ui->treeWidget->setEditTriggers(QAbstractItemView::SelectedClicked);
    init();// 初始化项目树
    //createUI();// 初始化文本文字
    QString str;
    m_pCustomDragLabel = new CustomDragLabel(str, this);

    //connect(m_pCustomDragLabel, SIGNAL(wantToGetPoint()), this, SLOT(receiverSIGNAL()));
    //connect(this, SIGNAL(sendPoint(QPoint)), m_pCustomDragLabel, SLOT(receiverData(QPoint)));


}

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

void DragTreeWidget::init()
{
    QTreeWidgetItem *imageItem1 = new QTreeWidgetItem(ui->treeWidget,QStringList(QString("图像1")));
    imageItem1->setIcon(0,QIcon("xxx.png"));
    QTreeWidgetItem *imageItem1_1 = new QTreeWidgetItem(imageItem1,QStringList(QString("Band1"))); //子节点1
    imageItem1->addChild(imageItem1_1); //添加子节点

    QTreeWidgetItem *imageItem2 = new QTreeWidgetItem(ui->treeWidget,QStringList(QString("图像2")));
    QTreeWidgetItem *imageItem2_1 = new QTreeWidgetItem(imageItem2,QStringList(QString("Band1"))); //子节点1
    QTreeWidgetItem *imageItem2_2 = new QTreeWidgetItem(imageItem2,QStringList(QString("Band2"))); //子节点2
    imageItem2->addChild(imageItem2_1);  //添加子节点
    imageItem2->addChild(imageItem2_2);

    ui->treeWidget->expandAll(); //结点全部展开
}

void DragTreeWidget::createUI()
{
    QTextEdit *edit = new QTextEdit;
    edit->setText(QObject::tr("中国欢迎您!\n 地球欢迎您!"));
    ui->verticalLayout->addWidget(edit);
    //ui->addWidget(edit);
}

void DragTreeWidget::dragEnterEvent(QDragEnterEvent *e)
{
	// 如果要配置mimeData层级树的类型,必须是“application/x-qabstractitemmodeldatalist”,具体原因不清楚
    if (e->mimeData()->hasText() || e->mimeData()->hasFormat("application/x-qabstractitemmodeldatalist"))
    {
        QByteArray data = e->mimeData()->data("application/x-qabstractitemmodeldatalist");
        QTreeWidgetItem *item = (QTreeWidgetItem*)((void*)QVariant(data).toInt());

        m_nPos = e->pos();
        QTreeWidgetItem *pItem = ui->treeWidget->itemAt(m_nPos);

        if(children().contains(e->source()))
        {
            e->setDropAction(Qt::CopyAction);
            e->accept();
        }
        else
            e->acceptProposedAction();
    }
}

void DragTreeWidget::dragMoveEvent(QDragMoveEvent *e)
{
    if (e->mimeData()->hasText() || e->mimeData()->hasFormat("application/x-qabstractitemmodeldatalist"))
    {
        if(children().contains(e->source()))
        {
            e->setDropAction(Qt::CopyAction);
            e->accept();
        }
        else
            e->acceptProposedAction();
    }
}

void DragTreeWidget::dropEvent(QDropEvent *e)
{
    if (e->mimeData()->hasFormat("application/x-qabstractitemmodeldatalist"))
    {// 配置mimeData类型的层级树
        QByteArray data = e->mimeData()->data("application/x-qabstractitemmodeldatalist");

        QString strr;
        QByteArray strList;
        for (int i = 0; i < data.size(); i++)
        {
            if (data[i] != '\0')
            {
                strList.push_back(data[i]);
            }
        }
        strr = strList;
		
		// 创建自定义Label
        CustomDragLabel *label = new CustomDragLabel(strr, this);
		label->setStyleSheet("background-color: rgb(0,255,0);color: rgb(255,255,255);");
        label->move(e->pos());
        label->setAutoFillBackground(true);
        ui->verticalLayout->addWidget(label);
        label->show();

        if (children().contains(e->source()))
        {
            e->setDropAction(Qt::CopyAction);
            e->accept();
        }
        else
        {
            e->acceptProposedAction();
        }
    }
    else if (e->mimeData()->hasText())
    {// 配置mimeData类型的文本
        QStringList strList = e->mimeData()->text().split(QRegExp("\\s+"),QString::SkipEmptyParts);
        QPoint pos = e->pos();

        foreach(QString str, strList)
        {
            DragLabel *dragLabel = new DragLabel(str,this);
            dragLabel->move(pos);
            dragLabel->show();
            pos += QPoint(dragLabel->width(),0);
        }
        if (children().contains(e->source()))
        {
            e->setDropAction(Qt::MoveAction);
            e->accept();
        }
        else
        {
            e->acceptProposedAction();
        }
    }
    else
    {
        e->ignore();
    }
}

void DragTreeWidget::receiverSIGNAL()
{
    QPoint point = mapFromGlobal(QCursor::pos());
    //qDebug()<<point;  不要直接把mapFromGlobal(QCursor::pos())传进去,不然有BUG
    emit sendPoint(point);
}

CustomDragLabel.cpp

CustomDragLabel::CustomDragLabel(const QString &str, QWidget* parent)
    : QLabel(str, parent)
{
	m_dragFlag = false;
	m_leftFlag = false;
	m_rightFlag = false;
	this->setMouseTracking(true);

	setMinimumSize(50, 50);
	setMaximumSize(800, 800);
	
	// 创建可拖拽、可缩放窗体
    DragProxy *m_pDragProxy = new DragProxy(this);
    m_pDragProxy->SetBorderWidth(8,8,8,8);
}

CustomDragLabel::~CustomDragLabel()
{

}

DragProxy.cpp

// 自定义可拖拽、可缩放窗体
DragProxy::DragProxy(QWidget *parent)
    : QObject((QObject*)parent)
{
    m_proxyWidget = parent;
    m_top = m_right = m_bottom = m_left = 0;

    m_proxyWidget->setMouseTracking(true);
    m_proxyWidget->installEventFilter(this); // 代理窗体事件

    m_mousePressed = false;
    m_regionPressed = Unknown;

    m_cursorTimerId = 0;
}

DragProxy::~DragProxy()
{
}

void DragProxy::SetBorderWidth(int top, int right, int bottom, int left)
{
    m_top = top;
    m_right = right;
    m_bottom = bottom;
    m_left = left;

    MakeRegions();
}

void DragProxy::UpdateGeometry(int x, int y, int w, int h)
{
    int minWidth = m_proxyWidget->minimumWidth();
    int minHeight = m_proxyWidget->minimumHeight();
    int maxWidth = m_proxyWidget->maximumWidth();
    int maxHeight = m_proxyWidget->maximumHeight();

    if (w < minWidth || w > maxWidth || h < minHeight || h > maxHeight)
    {
        return;
    }

    m_proxyWidget->setGeometry(x, y, w, h);
}

bool DragProxy::eventFilter(QObject* obj, QEvent* event)
{
    QEvent::Type eventType = event->type();
    if (eventType == QEvent::MouseMove)
    {
        QMouseEvent* mouseEvent = (QMouseEvent*)event;
        QPoint curPosLocal = mouseEvent->pos();
        DragProxy::WidgetRegion regionType = HitTest(curPosLocal);

        QPoint curPosGlobal = m_proxyWidget->mapToGlobal(curPosLocal);

        if (!m_mousePressed)    // 鼠标未按下
        {
            switch (regionType)
            {
            case Top:
            case Bottom:
                m_proxyWidget->setCursor(Qt::SizeVerCursor);
                break;
            case TopRight:
            case LeftBottom:
                m_proxyWidget->setCursor(Qt::SizeBDiagCursor);
                break;
            case Right:
            case Left:
                m_proxyWidget->setCursor(Qt::SizeHorCursor);
                break;
            case RightBottom:
            case LeftTop:
                m_proxyWidget->setCursor(Qt::SizeFDiagCursor);
                break;
            default:
                m_proxyWidget->setCursor(Qt::ArrowCursor);
                break;
            }

            StartCursorTimer();
        }
        else    // 鼠标已按下
        {
            QRect geo = m_proxyWidget->geometry();

            if (m_regionPressed == Inner)
            {
                m_proxyWidget->move(m_originGeo.topLeft() + curPosGlobal - m_originPosGlobal);
            }
            else if (m_regionPressed == Top)
            {
                int dY = curPosGlobal.y() - m_originPosGlobal.y();
                UpdateGeometry(m_originGeo.x(), m_originGeo.y() + dY, m_originGeo.width(), m_originGeo.height() - dY);
            }
            else if (m_regionPressed == TopRight)
            {
                QPoint dXY = curPosGlobal - m_originPosGlobal;
                UpdateGeometry(m_originGeo.x(), m_originGeo.y() + dXY.y(), m_originGeo.width() + dXY.x(), m_originGeo.height() - dXY.y());
            }
            else if (m_regionPressed == Right)
            {
                int dX = curPosGlobal.x() - m_originPosGlobal.x();
                UpdateGeometry(m_originGeo.x(), m_originGeo.y(), m_originGeo.width() + dX, m_originGeo.height());
            }
            else if (m_regionPressed == RightBottom)
            {
                QPoint dXY = curPosGlobal - m_originPosGlobal;
                UpdateGeometry(m_originGeo.x(), m_originGeo.y(), m_originGeo.width() + dXY.x(), m_originGeo.height() + dXY.y());
            }
            else if (m_regionPressed == Bottom)
            {
                int dY = curPosGlobal.y() - m_originPosGlobal.y();
                UpdateGeometry(m_originGeo.x(), m_originGeo.y(), m_originGeo.width(), m_originGeo.height() + dY);
            }
            else if (m_regionPressed == LeftBottom)
            {
                QPoint dXY = curPosGlobal - m_originPosGlobal;
                UpdateGeometry(m_originGeo.x() + dXY.x(), m_originGeo.y(), m_originGeo.width() - dXY.x(), m_originGeo.height() + dXY.y());
            }
            else if (m_regionPressed == Left)
            {
                int dX = curPosGlobal.x() - m_originPosGlobal.x();
                UpdateGeometry(m_originGeo.x() + dX, m_originGeo.y(), m_originGeo.width() - dX, m_originGeo.height());
            }
            else if (m_regionPressed == LeftTop)
            {
                QPoint dXY = curPosGlobal - m_originPosGlobal;
                UpdateGeometry(m_originGeo.x() + dXY.x(), m_originGeo.y() + dXY.y(), m_originGeo.width() - dXY.x(), m_originGeo.height() - dXY.y());
            }
        }
    }
    else if (eventType == QEvent::MouseButtonPress)
    {
        QMouseEvent* mouseEvent = (QMouseEvent*)event;
        if (mouseEvent->button() == Qt::LeftButton)
        {
            m_mousePressed = true;

            QPoint curPos = mouseEvent->pos();
            m_regionPressed = HitTest(curPos);

            m_originPosGlobal = m_proxyWidget->mapToGlobal(curPos);
            m_originGeo = m_proxyWidget->geometry();

            StopCursorTimer();
        }
    }
    else if (eventType == QEvent::MouseButtonRelease)
    {
        m_mousePressed = false;
        m_regionPressed = Unknown;

        m_proxyWidget->setCursor(Qt::ArrowCursor);
    }
    else if (eventType == QEvent::Resize)
    {
        MakeRegions();
    }
    else if (eventType == QEvent::Leave)
    {
        m_proxyWidget->setCursor(Qt::ArrowCursor);
        StopCursorTimer();
    }
    else if (eventType == QEvent::Timer)
    {
        QTimerEvent* timerEvent = (QTimerEvent*)event;
        if (timerEvent->timerId() == m_cursorTimerId)
        {
            if (m_regions[Inner].contains(m_proxyWidget->mapFromGlobal(QCursor::pos())))
            {
                m_proxyWidget->setCursor(Qt::ArrowCursor);
                StopCursorTimer();
            }
        }
    }

    return QObject::eventFilter(obj, event);
}

void DragProxy::StartCursorTimer()
{
    StopCursorTimer();
    m_cursorTimerId = m_proxyWidget->startTimer(50);
}

void DragProxy::StopCursorTimer()
{
    if (m_cursorTimerId != 0)
    {
        m_proxyWidget->killTimer(m_cursorTimerId);
        m_cursorTimerId = 0;
    }
}

void DragProxy::MakeRegions()
{
    int width = m_proxyWidget->width();
    int height = m_proxyWidget->height();

    m_regions[Top]          = QRect(m_left, 0, width - m_left - m_right, m_top);
    m_regions[TopRight]     = QRect(width - m_right, 0, m_right, m_top);
    m_regions[Right]        = QRect(width - m_right, m_top, m_right, height - m_top - m_bottom);
    m_regions[RightBottom]  = QRect(width - m_right, height - m_bottom, m_right, m_bottom);
    m_regions[Bottom]       = QRect(m_left, height - m_bottom, width - m_left - m_right, m_bottom);
    m_regions[LeftBottom]   = QRect(0, height - m_bottom, m_left, m_bottom);
    m_regions[Left]         = QRect(0, m_top, m_left, height - m_top - m_bottom);
    m_regions[LeftTop]      = QRect(0, 0, m_left, m_top);
    m_regions[Inner]        = QRect(m_left, m_top, width - m_left - m_right, height - m_top - m_bottom);
}

DragProxy::WidgetRegion DragProxy::HitTest(const QPoint& pos)
{
    for (int i = 0; i < 9; i++)
    {
        const QRect rect = m_regions[i];
        if (rect.contains(pos))
        {
            return DragProxy::WidgetRegion(i);
        }
    }

    return Unknown;
}

猜你喜欢

转载自blog.csdn.net/QIJINGBO123/article/details/89671842
今日推荐