【Qt】Qt中的拖放操作实现——拖放文件以及自定义拖放操作


文章参考《Qt Creator快速入门(第三版)》。


Qt的拖放操作

拖放操作分为拖动Drag和放下Drop,Qt提供了强大的拖放机制,可在帮助文档中通过Drag and Drop关键字查看。

在Qt中,数据拖动时会被存储为MIME类型(Multipurpose Internet Mail Extensions)。Qt提供QMimeData类表示MIME类型的数据,并使用QDrag类完成数据的转移,整个拖放操作是在几个鼠标事件和拖放事件中完成的。

拖放事件:

  • dragEnterEvent() 拖动进入事件;
  • dropEvent()放下事件;

使用拖放打开文件

当鼠标拖拽一个数据进入主窗口时, 会触发dragEnterEvent()事件,可以使用event->mimeData()获取其中的MIME数据;然后使用QMimeData::hasUrls()可以查看是否包含URL路径,如果是拖入文件实际就是拖入了它的路径。如果包含URL就接收event->acceptProposedAction(),否则就忽略该事件event->ignore()

acceptProposedAction()表示将放置操作设置位建议的操作。

当松开鼠标左键,将数据放到主窗口中就会触发dropEvent()事件,使用event->mimeData()获取MIME数据,判断数据中是否有URL,如果有的话就获取URL列表。获取到URL后就可以使用QFile文件操作读取文件并显示到textEdit中。

注意需要在构造函数中添加一行语句:setAcceptDrops(true);表示当前部件接收放下事件。

具体实现步骤:

首先在头文件中添加拖放事件的声明。

    void dragEnterEvent(QDragEnterEvent *event) override;
    void dropEvent(QDropEvent *event) override;

在.cpp文件中实现这两个函数。

void Widget::dragEnterEvent(QDragEnterEvent *event)
{
    // 拖动进入事件
    if(event->mimeData()->hasUrls())  // 数据中是否包含URL
    {
        event->acceptProposedAction();  // 如果数据中包含URL,就接收动作
    }
    else
    {
        event->ignore();  // 如果数据中不包含URL,就忽略该事件
    }
}

void Widget::dropEvent(QDropEvent *event)
{
    // 放下事件
    const QMimeData *mimeData = event->mimeData();  // 获取MIME数据
    if(mimeData->hasUrls())  // 如果数据中包含URL
    {
        QList<QUrl> urlList = mimeData->urls();  // 获取URL列表
        // 将其中的第一个URL表示为本地文件路径
        QString fileName = urlList.at(0).toLocalFile();  // toLocalFile()转换未本地文件路径
        if(!fileName.isEmpty())
        {
            // 文件路径不为空
            QFile file(fileName);
            if(!file.open(QIODevice::ReadOnly))
                return;
            QTextStream in(&file);
            ui->textEdit->setText(in.readAll());  // 将文件中的所有内容读入到控件中
        }
    }
}

QMimeData类提供了几个函数处理常见的MIME数据,如下:

测试函数获取函数设置函数MIME类型
hasText()text()setText()text/plain
hasHtml()html()setHtml()text/plain
hasUrls()urls()setUrls()text/url-list
hasImage()imageData()setImageData()image/*
hasColor()colorData()setColorData()application/x-color

自定义拖放操作

如下实例实现随意移动窗口中的图片。

首先,重写几个事件处理函数。

    // 重写事件处理函数
    void mousePressEvent(QMouseEvent *event) override;  // 鼠标按下事件
    void dragEnterEvent(QDragEnterEvent *event) override;  // 拖动进入事件
    void dragMoveEvent(QDragMoveEvent *event) override;  // 拖动事件
    void dropEvent(QDropEvent *event) override;  // 放下事件

然后,在构造函数中编写如下代码。

    // 设置窗口部件可以接收拖入操作
    setAcceptDrops(true);
    // 标签添加图片
    QPixmap pix(":/logo");
    ui->label->setPixmap(pix);
    // 标签大小设置为图片大小
    ui->label->resize(pix.size());
    // 移动标签
    ui->label->move(100,100);
    // 设置当窗口关闭时销毁图片
    ui->label->setAttribute(Qt::WA_DeleteOnClose);

最后,实现上面的几个事件处理函数

Ⅰ、先实现鼠标按下事件,在鼠标按下事件处理函数中主要实现自定义的MIME类型数据,并执行拖动操作。处理过程大致分为6步:

  1. 获取鼠标指针所在处的部件的指针,并强制转换为QLabel,使用inherits()判断是否是QLabel标签,如果不是直接返回。
  2. 在拖动的数据中包含图片数据和它的位置信息,需要使用自定义的MIME类型,其中位置信息是当前鼠标指针的坐标减去图片左上角的坐标得到的差值。
  3. 创建QMimeData类对象指针,使用自定义的MIME类型,将要拖动的数据放入QMimeData对象中。
  4. 要移动数据,必须创建QDrag类对象,然后为QDrag对象添加QMimeData数据;使移动中一直显示图片,需要使用setPixmap()函数为QDrag设置,并且使用setHotSpot()指定鼠标在图片上单机的位置,如果不设置这个,在拖动图片过程中指针会位于图片的左上角。
  5. 在移动图片的过程中希望原来的图片有所改变可以看出来正在被操作,所以添加一层阴影。
  6. 执行拖动操作,需要使用QDrag::exec(),它不会影响主事件循环,这时的界面不会被冻结,exec()函数可以设定支持的放下动作和默认的放下动作,比如支持移动Qt::MoveAction,支持复制操作Qt::CopyAction。调用acceptProposedAction()函数时使用的默认的操作。exec()的返回值由dropEvent()中的设置决定。

示例代码如下:

void MainWindow::mousePressEvent(QMouseEvent *event)  // 鼠标按下事件
{
    // 1. 获取图片
    // 将鼠标指针所在位置的部件强制转换为QLabel类型
    QLabel *child = static_cast<QLabel*>(childAt(event->pos()));  // childAt()返回指定坐标处的可见子部件,如果指定位置没有可见的子部件,就返回nullptr
    if(child == Q_NULLPTR)
    {
        qDebug() << tr("位置(%1,%2)处没有子部件!").arg(event->pos().x()).arg(event->pos().y());
        return;
    }
    if(!child->inherits("QLabel"))  // 判断得到的这个部件是不是继承QLabel
    {
        qDebug() << tr("当前部件不是QLabel标签部件");
        return;
    }
    // 获取QLabel中的标签
    QPixmap pix = *child->pixmap();
    // 2. 自定义MIME类型
    QByteArray itemData;  // 创建字节数组
    QDataStream dataStream(&itemData,QIODevice::WriteOnly);  // 创建数据流
    // 将图片信息、位置信息输入到字节数组中
    dataStream <<  pix << 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(pix);
    //设置拖动时鼠标指针的位置不变
    drag->setHotSpot(event->pos() - child->pos());

    // 5. 给原始图片添加阴影
    QPixmap tmpPixmap = pix;
    QPainter painter;
    painter.begin(&tmpPixmap);
    // 在图片的外界觉醒中添加一层透明的淡黑形成阴影效果
    painter.fillRect(pix.rect(),QColor(127,127,127,127));
    painter.end();
    // 在移动图片过程中,让原图片添加一层黑色阴影
    child->setPixmap(tmpPixmap);

    // 6. 执行拖放操作
    // 设置拖放可以是移动和复制操作,默认是复制操作
    if(Qt::MoveAction == drag->exec(Qt::CopyAction | Qt::MoveAction,Qt::CopyAction))
    {
        child->close();  // 如果是移动操作,拖放完成后关闭原标签
    }
    else
    {
        child->show();  // 如果是复制操作,拖放完成后显示标签
        child->setPixmap(pix);  // 显示原标签,不再使用阴影
    }
}

Ⅱ、再处理拖动进入和拖动事件,在这两个事件处理函数中,先判断拖动的数据中是否有自定义的MIME类型的数据,如果有,就执行移动动作。示例代码:

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();
    }
}

Ⅲ、最后处理放下事件,使用字节数组获取拖放的数据,然后获取图片数据和位置信息,并使用这些数据设置新建的标签。示例代码:

void MainWindow::dropEvent(QDropEvent *event)  // 放下事件
{
    // 放下事件,判断是否有自定义的MIME类型的数据,如果有就获取这些数据并将图片显示出来
    if(event->mimeData()->hasFormat("myimage/png"))
    {
        QByteArray itemData = event->mimeData()->data("myimage/png");
        QDataStream out(&itemData,QIODevice::ReadOnly);
        QPixmap pix;
        QPoint ptOffset;
        // 将MIME数据读入到QPixmap和QPoint中
        out >> pix >> ptOffset;
        // 新建标签,添加图片,并根据图片大小设置标签大小
        QLabel *label = new QLabel(this);
        label->setPixmap(pix);
        label->resize(pix.size());
        // 让图片移动到放下的位置,如果不设置,图片会默认显示在窗口左上角
        label->move(event->pos() - ptOffset);
        label->show();
        label->setAttribute(Qt::WA_DeleteOnClose);
        event->setDropAction(Qt::MoveAction); // 设置移动过程中的操作,Qt::MoveAction是移动,如果需要复制操作,改成Qt::CopyAction
        event->accept();
    }
    else
    {
        event->ignore();
    }
}

运行程序显示结果:

在这里插入图片描述

将拖动进入、拖动、放下事件中的Qt::MoveAction改为Qt::CopyAction运行后显示结果:

在这里插入图片描述

完整代码可下载:https://download.csdn.net/download/sinat_41752325/87378359

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值