Qt 图标拖拽案例

QMimeData

  • 用于描述可储存在剪贴板中的信息,并支持通过拖放机制传输
  • 将它们所持有的数据与相应的 MIME 类型相关联,以确保信息可以在应用程序内,应用程序之间安全传输

QByteArray

  • 字节数组容器,相当于方便版的 const char*,或者8位的 QString
  • 可以用于存储原始的二进制数据,也可以在内存紧张的情况下节省内存,例如嵌入式开发
  • QMimeData 中自定义数据类型通常为 QByteArray

QPixmap

  • 与 QImage 类似,但是不如 QImage 操作灵活
  • QLabel 中的图片即使通过 QPixmap 储存的

QDrag

  • 可以设置 QMimeData,QPixmap 和 拖动位置等
  • 需要设置 QWidget::setAcceptDrops(true) 才能接受 dropEvent

拖拽的基本流程

  • mousePressEvent 中定义 QDrag 并开启拖拽(QDrag::exec()),同时还要完成拖拽后的后续处理
  • dragEnterEvent 中处理拖拽过程中的逻辑
  • dropEvent 中重建拖拽的对象,并确定 QDrag::exec() 的返回值

代码示例

#include "dragform.h"
#include "ui_dragform.h"

DragForm::DragForm(QWidget *parent) :
    QFrame(parent),
    ui(new Ui::DragForm)
{
    ui->setupUi(this);
    this->setAcceptDrops(true);
}

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

void DragForm::mousePressEvent(QMouseEvent *event)
{
    QWidget* child = this->childAt(event->position().toPoint());
    QLabel* label = qobject_cast<QLabel*>(child);
    if (label == nullptr)
    {
        return;
    }

    QPixmap pixmap = label->pixmap();
    QPoint relativePosition = event->position().toPoint() - child->pos();

    QByteArray itemData;
    QDataStream dataStream(&itemData, QIODevice::WriteOnly);
    dataStream << pixmap << relativePosition;

    QMimeData* mimeData = new QMimeData();
    mimeData->setData("Application/DragIcon", itemData);

    QDrag* drag = new QDrag(this);
    drag->setMimeData(mimeData);
    drag->setPixmap(pixmap);
    drag->setHotSpot(relativePosition);

    QPixmap maskedPixmap = pixmap;
    QPainter painter;
    painter.begin(&maskedPixmap);
    painter.fillRect(child->rect(), QColor(127, 127, 127, 127));
    painter.end();
    label->setPixmap(maskedPixmap);

    if (drag->exec(Qt::CopyAction | Qt::MoveAction, Qt::CopyAction) == Qt::MoveAction)
    {
        child->close();
    }
    else
    {
        label->setPixmap(pixmap);
    }
}

void DragForm::dragEnterEvent(QDragEnterEvent *event)
{
    if (event->mimeData()->hasFormat("Application/DragIcon"))
    {
        if (event->source() == this)
        {
            event->setDropAction(Qt::MoveAction);
            event->accept();
        }
        else
        {
            event->acceptProposedAction();
        }
    }
    else
    {
        event->ignore();
    }
}

void DragForm::dropEvent(QDropEvent *event)
{
    if (event->mimeData()->hasFormat("Application/DragIcon"))
    {
        QPixmap pixmap;
        QPoint relativePosition;
        QByteArray itemData = event->mimeData()->data("Application/DragIcon");
        QDataStream dataStream(&itemData, QIODevice::ReadOnly);
        dataStream >> pixmap >> relativePosition;

        qDebug() << relativePosition;

        QLabel* label = new QLabel(this);
        label->resize(64, 64);
        label->setPixmap(pixmap);
        label->move(event->position().toPoint() - relativePosition);
        label->show();

        if (event->source() == this)
        {
            event->setDropAction(Qt::MoveAction);
            event->accept();
        }
        else
        {
            event->acceptProposedAction();
        }
    }
    else
    {
        event->ignore();
    }
}