Qt学习------自定义拖放操作

原理:

  • 自定义MIME类型
  • 重写相应的事件函数

示例:

效果:

在窗口上有一个图片,可以任意拖动它。

代码:

  • 新建QWigets应用,基类选择QMainWindow
  • 在mainwindow.h中添加如下内容:
protected:
    void mousePressEvent(QMouseEvent * event);     // 鼠标按下事件
    void dragEnterEvent(QDragEnterEvent * event);  // 拖动进入事件
    void dragMoveEvent(QDragMoveEvent * event);    // 拖动事件
    void dropEvent(QDropEvent * event);            // 拖动放下事件
  • 在mainwindow.cpp中添加如下头文件:
#include <QLabel>
#include <QMouseEvent>
#include <QDragEnterEvent>
#include <QDragMoveEvent>
#include <QDropEvent>
#include <QPainter>
#include <QMimeData>
#include <QDrag> 
  • 在构造函数添加如下代码:
setAcceptDrops(true);                   // 设置窗口部件可以接收拖入,默认不可以接收拖入动作的
    QLabel * label = new QLabel(this);      // 创建标签
    QPixmap pix("../5-8/zoom.png");         // 这里使用了相当对路径,你也可以用 Qt资源文件
    label->setPixmap(pix);                  // 设置图片
    label->resize(pix.size());              // 设置标签大小为图片的大小
    label->move(100, 100);
    label->setAttribute(Qt::WA_DeleteOnClose);    // 当窗口关闭时销毁图片
  • 添加上述重写实现的事件函数定义:
void MainWindow::mousePressEvent(QMouseEvent *event)     // 鼠标按下事件
{
    // 第1步:获取图片
    // 将鼠标指针所在位置的部件强制转为 QLabel 类型,childAt(event->pos())返回鼠标指针在位置的可见子部件,没有返回0
    QLabel * child = static_cast <QLabel *> (childAt(event->pos()));
    if(!child->inherits("QLabel")) return;              // 如果部件不是QLabel则直接返回
    QPixmap pixmap = * child->pixmap();                   // 获取QLabel中的图片
    // 第2步:自定义MIME类型
    QByteArray itemData;
    QDataStream dataStream(&itemData, QIODevice::WriteOnly);  // 创建数据流,只写模式
    // 将图片信息,位置信息写入到字节数组中
    dataStream << pixmap << QPoint(event->pos() - child->pos());
    // 第3步:将数据放入QMimeData中
    QMimeData * mimeData = new QMimeData;                     // 创建QMimeData用来存放要移动的数据
    // 将字节数组放入QMimeData中,这里的MIME类型是我们自己定义的
    mimeData->setData("myimage/png", itemData);
    // 第4步:将QMimeData数据放入QDrag中
    QDrag * drag = new QDrag(this);                        // 创建QDrag,它用来移动数据
    drag->setMimeData(mimeData);
    drag->setPixmap(pixmap);                               // 在移动过程中显示图片,若不设置则默认显示一个小矩形
    drag->setHotSpot(event->pos() - child->pos());         // 拖动时鼠标指针的位置不变,设置鼠标指针相对于图片左上角位置
    // 第5步:给原图片添加阴影
    QPixmap tempPixmap = pixmap;                           // 使原图片添加阴影
    QPainter painter;                                      // 创建QPainter,用来绘制QPixmap
    painter.begin(&tempPixmap);
    // 在图片的外接矩形中添加一层透明的淡黑色形成阴影效果
    painter.fillRect(pixmap.rect(), QColor(127, 127, 127, 127));   // rgb + 透明度
    painter.end();
    child->setPixmap(tempPixmap);      // 在图片移动过程中,让原图片添加一层黑色阴影
    // 第6步:执行拖放操作
    if(drag->exec(Qt::CopyAction | Qt::MoveAction, Qt::CopyAction)
            == Qt::MoveAction)  // 设置拖放可以使移动和复制操作,默认是复制操作
        child->close();         // 如果是移动操作,那么拖放完成后关闭原标签
    else {
        child->show();         // 如果是复制操作,那么拖放完成后显示标签
        child->setPixmap(pixmap);     //  显示原图片,不再使用阴影
    }
}
  • 对于mousePressEvent( )中第6步的QDrag类的exec( )函数,它不会影响主事件循环,所以这时界面不会被冻结。
    这个函数设置了支持复制和移动动作,设置复制为默认放下动作。
    exec( )函数的返回类型由dropEvent( )中的设置决定。
    这里判断了到底进行了什么操作,如果是移动操作,那么就删除原来的图片;如果是复制操作,就恢复原来的图片。
void MainWindow::dragEnterEvent(QDragEnterEvent *event)   // 拖动进入事件
{
    // 如果有我们定义的MIME类型数据,则进行移动操作
    if(event->mimeData()->hasFormat("myimage/png")) {
        event->setDropAction(Qt::MoveAction);
        event->accept();
    } else {
        event->ignore();
    }
}

void MainWindow::dragMoveEvent(QDragMoveEvent *event)    // 拖动事件
{
    // 如果有我们定义的MIME类型数据,则进行移动操作
    if(event->mimeData()->hasFormat("myimage/png")) {
        event->setDropAction(Qt::MoveAction);
        event->accept();
    } else {
        event->ignore();
    }
}
  • 在这2个事件处理函数中,先判断拖动的数据中是否包含自定义的MIME类型的数据,如果有,则执行移动动作Qt::MoveAction
void MainWindow::dropEvent(QDropEvent *event)           // 拖动放下事件
{
    if(event->mimeData()->hasFormat("myimage/png")) {
        QByteArray itemData = event->mimeData()->data("myimage/png");
        QDataStream dataStream(&itemData, QIODevice::ReadOnly);     // 只读模式
        QPixmap pixmap;
        QPoint offset;
        // 使用数据流将字节数组中的数据读入到QPixmap和QPoint
        dataStream >> pixmap >> offset;
        // 新建标签,为其添加图片,并根据图片的大小设置标签的大小
        QLabel * newLabel = new QLabel(this);
        newLabel->setPixmap(pixmap);
        newLabel->resize(pixmap.size());
        // 让图片移动到放下的位置,不设置的话,图片会默认显示在(0, 0)点,即窗口左上角
        newLabel->move(event->pos() - offset);
        newLabel->show();
        newLabel->setAttribute(Qt::WA_DeleteOnClose);
        event->setDropAction(Qt::MoveAction);
        event->accept();
    } else {
        event->ignore();
    }
}

几点说明:

  • 这个例子是对图片进行移动,如果想对图片进行复制,则只需要将dragEnterEvent( )、dragMoveEvent( )、dropEvent( )这三个函数中的event->setDropAction( )函数中的参数改为Qt::CopyAction即可。
  • 对于拖放操作的其他应用,比如根据移动的距离判断是否开始一个拖放操作,还有剪贴板QClipboard类,都可以在帮助中通过Drag and Drop关键字查看。
  • 上述代码中使用了很多相对位置,读者如果有一些疑惑,可以尝试使用qDebug()输出相关坐标信息,或者不使用相对坐标看看会怎么样,效果是很直观的,这都是一些小技巧!
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值