QTreeWidget 自身节点拖拽

本文详细介绍了如何通过子类化QTreeWidget实现节点的拖拽功能,包括自定义QMimeData类以传递数据类型,以及在拖拽过程中显示自定义图片。此外,还提供了一个拖拽操作选择界面,允许用户在拖动节点时选择不同的操作方式。

前言

通过一个例子学习 QTreeWidget 节点的拖拽功能。本示例仅实现树形结构的自我拖拽,用来更改上下级关系或者调整节点顺序。
在这里插入图片描述

子类化 QTreeWidget

  1. 概述
    通过子类化 QTreeWidget 重新实现相关事件,以实现自定义功能。
  2. 头文件
#ifndef MYTREEWIDGET_H
#define MYTREEWIDGET_H
#include <QTreeWidget>

class MyTreeWidget : public QTreeWidget
{
    Q_OBJECT
public:
    explicit MyTreeWidget( QWidget * parent = nullptr);
protected:
    //需要重新实现的事件
    void mousePressEvent(QMouseEvent *event);
    void mouseMoveEvent(QMouseEvent *event);
    void dragEnterEvent(QDragEnterEvent * event);
    void dropEvent(QDropEvent *event);
    void dragMoveEvent(QDragMoveEvent *event);
private:
    QPoint startDragPoint;
};

#endif // MYTREEWIDGET_H
  1. 源码文件
#include "mytreewidget.h"
#include <QMouseEvent>
#include <QApplication>
#include "dragimage.h"
#include "treeitemmimedata.h"
#include <QDrag>
#include "dragmodedialog.h"


MyTreeWidget::MyTreeWidget(QWidget *parent):QTreeWidget(parent)
{
    this->setSelectionMode(QAbstractItemView::SingleSelection);
    this->setDragEnabled(true);
    this->viewport()->setAcceptDrops(true);
    this->setDragDropMode(QAbstractItemView::InternalMove);
}

void MyTreeWidget::mousePressEvent(QMouseEvent *event)
{
    if( event->button() == Qt::LeftButton )
    {
        startDragPoint = event->pos();
    }
    QTreeWidget::mousePressEvent(event);
}

void MyTreeWidget::mouseMoveEvent(QMouseEvent *event)
{
    if( event->buttons() & Qt::LeftButton)//判断左键按下
    {
        int distance = (event->pos()-startDragPoint ).manhattanLength();
        if( distance > QApplication::startDragDistance())
        {
             QTreeWidgetItem * item = this->currentItem();
             DragImage *dragimag = new DragImage;
             dragimag->setShowText(item->text(0));
             dragimag->setShowIcon(item->icon(0));
             QPixmap pixmap = dragimag->grab();
             TreeItemMimeData *mimedata = new TreeItemMimeData;
             mimedata->SetDragData("ItemMimeData",item);

             QDrag *drag = new QDrag(this);
             drag->setMimeData(mimedata);
             drag->setPixmap(pixmap);
             drag->setHotSpot(QPoint(pixmap.width() / 2, pixmap.height() / 2));
             if( drag->exec(Qt::MoveAction) == Qt::MoveAction)
             {
                 delete  item;
                 item = nullptr;
             }
        }
    }
    QTreeWidget::mouseMoveEvent(event);
}

void MyTreeWidget::dragEnterEvent(QDragEnterEvent *event)
{
    if (event->mimeData()->hasFormat("ItemMimeData") &&  event->source() == this )
    {
        event->setDropAction(Qt::MoveAction);
        event->accept();
    }
    else
        event->ignore();
}

void MyTreeWidget::dropEvent(QDropEvent *event)
{
    if(event->mimeData()->hasFormat("ItemMimeData") && event->source() == this )
    {
        const TreeItemMimeData *pMimeData = (const TreeItemMimeData *)(event->mimeData());
        const QTreeWidgetItem *item = pMimeData->DragItemData();
        QTreeWidgetItem *pItem = item->clone();
        QTreeWidgetItem *currentItem = this->itemAt(event->pos());
        bool canceldrag = false;
        if( currentItem == nullptr )
        {
            this->addTopLevelItem(pItem);
        }
        else
        {
            QTreeWidgetItem * currenParent = currentItem->parent();
            DragModeDialog *dlg = new DragModeDialog;
            dlg->setShowText(pItem->text(0),currentItem->text(0));
            dlg->setShowPixm(pItem->icon(0).pixmap(20,20));
            if( dlg->exec() == QDialog::Accepted)
            {
                DragModeDialog::DragMode dragmode = dlg->selMode;
                switch (dragmode)
                {
                    case DragModeDialog::DragAsChild:
                        currentItem->addChild(pItem);
                        break;
                    case DragModeDialog::DragToUp:
                        currenParent == nullptr ? insertTopLevelItem(indexOfTopLevelItem(currentItem),pItem) : currenParent->insertChild(currenParent->indexOfChild(currentItem),pItem);
                        break;
                    case DragModeDialog::DragToDown:
                        currenParent == nullptr ? insertTopLevelItem(indexOfTopLevelItem(currentItem)+1,pItem) : currenParent->insertChild(currenParent->indexOfChild(currentItem)+1,pItem);
                        break;
                }
            }
            else
            {
                canceldrag = true;
            }
        }
        if( !canceldrag)
        {
            event->setDropAction(Qt::MoveAction);
            event->accept();
        }
        else
            event->ignore();
    }
    else
        event->ignore();

}

void MyTreeWidget::dragMoveEvent(QDragMoveEvent *event)
{
    if (event->mimeData()->hasFormat("ItemMimeData") && event->source() == this )
    {
         const TreeItemMimeData *pMimeData = (const TreeItemMimeData *)(event->mimeData());
         const QTreeWidgetItem *item = pMimeData->DragItemData();

         QTreeWidgetItem *currentItem = this->itemAt(event->pos());
         bool isok = true;
         while(currentItem)
         {
             if(currentItem == item)
             {
                 isok = false;
                 break;
             }
             currentItem = currentItem->parent();
         }
         if( isok )
         {
             event->setDropAction(Qt::CopyAction);
             event->accept();
         }
         else
             event->ignore();
    }
    else
        event->ignore();
}

子类化 QMimeData

  1. 概述
    通过子类化 QMimeData 实现自定义数据类型的传递。
  2. 头文件
#ifndef TREEITEMMIMEDATA_H
#define TREEITEMMIMEDATA_H
#include <QMimeData>

class QTreeWidgetItem;
class TreeItemMimeData : public QMimeData
{
    Q_OBJECT
public:
    TreeItemMimeData():QMimeData(){};
    QStringList formats() const {return m_format ;}
    const QTreeWidgetItem * DragItemData() const { return  m_pDragItem;}
    void SetDragData(QString mimeType,QTreeWidgetItem * pItem);
private:
    QStringList m_format;
    const QTreeWidgetItem * m_pDragItem;
protected:
    QVariant retrieveData(const QString &mimetype, QVariant::Type preferredType) const ;
};

#endif // TREEITEMMIMEDATA_H
  1. 源码文件
#include "treeitemmimedata.h"



void TreeItemMimeData::SetDragData(QString mimeType, QTreeWidgetItem *pItem)
{
    m_format << mimeType;
    m_pDragItem = pItem;
}

QVariant TreeItemMimeData::retrieveData(const QString &mimetype, QVariant::Type preferredType) const
{
    if( mimetype == "ItemMimeData")
    {
        return m_pDragItem;
    }
    else
        return QMimeData::retrieveData(mimetype,preferredType);
}

拖拽过程图片显示

  1. 概述
    使用 QPushButton 可以同时显示图标和文字,设置边框样式后可以达到需要的效果。
  2. 头文件
#ifndef DRAGIMAGE_H
#define DRAGIMAGE_H

#include <QWidget>

namespace Ui {
class DragImage;
}

class DragImage : public QWidget
{
    Q_OBJECT

public:
    explicit DragImage(QWidget *parent = nullptr);
    ~DragImage();
    void setShowText(QString text);//拖动时显示的文字
    void setShowIcon(QIcon pix);//拖动时显示的图标

private:
    Ui::DragImage *ui;
};

#endif // DRAGIMAGE_H
  1. 源码文件
#include "dragimage.h"
#include "ui_dragimage.h"

DragImage::DragImage(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::DragImage)
{
    ui->setupUi(this);
    this->setWindowFlags(Qt::FramelessWindowHint);
    this->setAttribute(Qt::WA_TranslucentBackground, true);
}

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

void DragImage::setShowText(QString text)
{
    ui->pushButton->setText(text);
}

void DragImage::setShowIcon(QIcon pix)
{
     ui->pushButton->setIcon(pix);
}

拖拽操作选择界面

  1. 概述
    使用 QButtonGroup 创建一个单选按钮集合,以选择拖拽的操作方式。
  2. 头文件
#ifndef DRAGMODEDIALOG_H
#define DRAGMODEDIALOG_H

#include <QDialog>

namespace Ui {
class DragModeDialog;
}

class QLabel;
class QButtonGroup;
class DragModeDialog : public QDialog
{
    Q_OBJECT

public:
    explicit DragModeDialog(QWidget *parent = nullptr);
    ~DragModeDialog();
    void setShowText(const QString &dragname,const QString &desname);
    void setShowPixm(const QPixmap &pix);
    enum DragMode { DragAsChild,DragToUp,DragToDown };
    DragMode selMode;

private:
    Ui::DragModeDialog *ui;
    QLabel * iconLabel;
    QLabel * textlabel;
    QButtonGroup * group;
private slots:
    void buttonOkClick();
    void buttonCancelClick();
};

#endif // DRAGMODEDIALOG_H
  1. 源码文件
#include "dragmodedialog.h"
#include "ui_dragmodedialog.h"
#include <QButtonGroup>
#include <QRadioButton>
#include <QDialogButtonBox>
#include <QPushButton>
#include <QLabel>
#include <QGridLayout>


DragModeDialog::DragModeDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::DragModeDialog)
{
    ui->setupUi(this);
    //创建 QRadioButton 集合
    group = new QButtonGroup(this);
    QRadioButton * bnt1 = new QRadioButton(tr("子节点:添加到目标子节点末尾"),this);
    QRadioButton * btn2 = new QRadioButton(tr("同级节点:添加到目标上方位置"),this);
    QRadioButton * btn3 = new QRadioButton(tr("同级节点:添加到目标下方位置"),this);
    bnt1->setChecked(true);
    group->addButton(bnt1,1);
    group->addButton(btn2,2);
    group->addButton(btn3,3);
    //创建标准按钮框
    QDialogButtonBox * box = new QDialogButtonBox(this);
    QPushButton * buttonOk = new QPushButton(tr("确定"));
    QPushButton * buttonCancel = new QPushButton(tr("取消"));
    box->addButton(buttonCancel,QDialogButtonBox::RejectRole);
    box->addButton(buttonOk,QDialogButtonBox::YesRole);
    buttonOk->setDefault(true);

    iconLabel = new QLabel(this);
    textlabel = new QLabel(this);
    textlabel->setText(tr("选择拖动的行为:"));
    textlabel->setStyleSheet("color:blue");
    QFont ft;
    ft.setBold(true);
    ft.setPointSize(11);
    textlabel->setFont(ft);

    QLabel * tmp = new QLabel(this);
    tmp->setSizePolicy(QSizePolicy::Fixed,QSizePolicy::Fixed);
    QGridLayout *mainLayout = new QGridLayout(this);
    mainLayout->addWidget(iconLabel,0,0,1,1);
    mainLayout->addWidget(textlabel,0,1,1,3);
    mainLayout->addWidget(bnt1,1,0,1,4);
    mainLayout->addWidget(btn2,2,0,1,4);
    mainLayout->addWidget(btn3,3,0,1,4);
    mainLayout->addWidget(tmp,4,0,1,4);
    mainLayout->addWidget(box,5,0,1,4);
    mainLayout->setHorizontalSpacing(10);
    mainLayout->setVerticalSpacing(10);
    mainLayout->setContentsMargins(10,10,10,10);

    setLayout(mainLayout);
    this->setFixedSize(380,210);
    this->setWindowTitle(tr("拖动设置"));
    setWindowFlags(Qt::WindowStaysOnTopHint);

    connect(buttonOk,&QPushButton::clicked,this,&DragModeDialog::buttonOkClick);
    connect(buttonCancel,&QPushButton::clicked,this,&DragModeDialog::buttonCancelClick);
}

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

void DragModeDialog::setShowText(const QString &dragname, const QString &desname)
{
    textlabel->setText(QString(tr("拖动节点:%1,目标节点:%2,选择操作类型:")).arg(dragname).arg(desname));
}

void DragModeDialog::setShowPixm(const QPixmap &pix)
{
    iconLabel->setPixmap(pix);
}

void DragModeDialog::buttonOkClick()
{
    int selID = group->checkedId();
    switch (selID)
    {
        case 1:
            selMode = DragAsChild;
            break;
        case 2:
            selMode = DragToUp;
            break;
        case 3:
            selMode = DragToDown;
            break;
    }
    accept();
}

void DragModeDialog::buttonCancelClick()
{
    close();
}

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值