QT官方示例 - fridgeMagnets控件拖动

官方示例地址
官方实现了一个自定义Label控件,在widget控件容器当中对label进行随意拖动。控件拖动实现主要围绕着几个关键事件,其中包括 mousePressEvent 、 dragEnterEvent 、 dragMoveEvent 和 dropEvent ,执行顺序是点击控件->拖动进入事件->拖动移动事件->拖动放下事件。

以下是代码分析:

mousePressEvent

首先,创建鼠标点击事件,头文件当中,+通过重写父类控件实现。

void DragWidget::mousePressEvent(QMouseEvent *event){}

鼠标点击事件块内,通过childAt()方法,传入事件位置,定位获取到当前控件。

 DragLabel *child = static_cast<DragLabel*>(childAt(event->pos()));
    if (!child)
        return;

获取热点,通过鼠标点击点相对于父控件的坐标,减去子控件左上角相对于父控件的坐标 。热点的目的是为了拖动时显示的图像中,哪个点应该与鼠标指针对齐。否则,默认是控件的左上角为鼠标指针对齐。

QPoint hotSpot = event->pos() - child->pos();

我们使用MimeData存储控件信息,方便再拖动时获取。

QByteArray itemData;
QDataStream dataStream(&itemData, QIODevice::WriteOnly);
dataStream << child->labelText() << QPoint(hotSpot);

QMimeData *mimeData = new QMimeData;
mimeData->setData(frigetMagnetsMimeType(), itemData);
mimeData->setText(child->labelText());

 

我们创建一个QDrag类代表一次拖动操作,并绑定MimeData,设置QDrag拖动时图片为当前控件,并设置热点。

QDrag *drag = new QDrag(this);
drag->setMimeData(mimeData);
drag->setPixmap(child->pixmap(Qt::ReturnByValue));
drag->setHotSpot(hotSpot);

当鼠标点击时,隐藏当前控件,模拟"控件被提起操作",发起拖动操作,等待用户在目标位置释放。 如果返回结果是 Qt::MoveAction(即拖动完成且为移动操作), 否则显示当前控件,不执行其他操作。

child->hide();

if (drag->exec(Qt::MoveAction | Qt::CopyAction, Qt::CopyAction) == Qt::MoveAction)
    child->clear();
else
    child->show();

dragEnterEvent

拖动进入事件,代码就比较简单,首先是判断当前mimeData是否为指定类型,再判断是否为当前子控件,如果是那么设置拖动动作为Qt::MoveAction,否则接收默认动作。如果mimeData是包含文本内容控件,则执行默认行为,否则忽略。

void DragWidget::dragEnterEvent(QDragEnterEvent *event)
{
    if (event->mimeData()->hasFormat(frigetMagnetsMimeType())) {
        if (children().contains(event->source())) {
            event->setDropAction(Qt::MoveAction);
            event->accept();
        } else {
            event->acceptProposedAction();
        }
    } else if (event->mimeData()->hasText()) {
        event->acceptProposedAction();
    } else {
        event->ignore();
    }
}

dragMoveEvent

拖动移动事件内容与进入事件一致,没有额外操作。

void DragWidget::dragMoveEvent(QDragMoveEvent *event)
{
    if (event->mimeData()->hasFormat(frigetMagnetsMimeType())) {
        if (children().contains(event->source())) {
            event->setDropAction(Qt::MoveAction);
            event->accept();
        } else {
            event->acceptProposedAction();
        }
    } else if (event->mimeData()->hasText()) {
        event->acceptProposedAction();
    } else {
        event->ignore();
    }
}

dropEvent

拖动放下事件,获取当前拖动对象的mimeData数据,并重新实例化一个Label对象替换之前存在的对象。

void DragWidget::dropEvent(QDropEvent *event)
{
    if (event->mimeData()->hasFormat(frigetMagnetsMimeType())) {
        const QMimeData *mime = event->mimeData();

        QByteArray itemData = mime->data(frigetMagnetsMimeType());
        QDataStream dataStream(&itemData, QIODevice::ReadOnly);

        QString text;
        QPoint offset;
        dataStream >> text >> offset;

        DragLabel *newLabel = new DragLabel(text, this);
        newLabel->move(event->pos() - offset);
        newLabel->show();
        newLabel->setAttribute(Qt::WA_DeleteOnClose);

        if (event->source() == this) {
            event->setDropAction(Qt::MoveAction);
            event->accept();
        } else {
            event->acceptProposedAction();
        }
    } else if (event->mimeData()->hasText()) {
        QStringList pieces = event->mimeData()->text().split(
            QRegularExpression(QStringLiteral("\\s+")), Qt::SkipEmptyParts);
        QPoint position = event->pos();

        for (const QString &piece : pieces) {
            DragLabel *newLabel = new DragLabel(piece, this);
            newLabel->move(position);
            newLabel->show();
            newLabel->setAttribute(Qt::WA_DeleteOnClose);

            position += QPoint(newLabel->width(), 0);
        }

        event->acceptProposedAction();
    } else {
        event->ignore();
    }
}

 

posted @ 2025-05-24 14:19  IceCoke_lulu  阅读(27)  评论(0)    收藏  举报