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

news2025/1/20 13:34:49

文章目录

  • 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。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/155127.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ArcGIS基础实验操作100例--实验78按栅格分区统计路网

本实验专栏参考自汤国安教授《地理信息系统基础实验操作100例》一书 实验平台&#xff1a;ArcGIS 10.6 实验数据&#xff1a;请访问实验1&#xff08;传送门&#xff09; 高级编辑篇--实验78 按栅格分区统计路网 目录 一、实验背景 二、实验数据 三、实验步骤 &#xff08;…

【数据结构】队列详解

前言 前面我们学习了一种数据结构&#xff1a;栈&#xff0c;栈是一种只允许在一端尽进行插入删除的数据结构&#xff0c;而今天我们将学习另一种数据结构&#xff1a;队列&#xff0c;队列是一种支持在一端进行插入&#xff0c;在另一端进行删除的数据结构。 一、队列的介绍…

PHP反序列化字符串逃逸

PHP反序列化字符串逃逸 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录PHP反序列化字符串逃逸前言一、关于反序列化和序列化二、[0ctf 2016]unserialize二、prize_p5[NSSCTF]前言 例如&#xff1a;最近日常刷题玩…

常用的传输码介绍

文章目录前导知识1.AMI码2.HDB3码3.PST码4.数字双相码5.CMI码6.nBmB码前导知识 在介绍常用的传输码之前&#xff0c;先简单介绍一下直流分量。 信号的直流分量就是信号的平均值&#xff0c;它是一个与时间无关的常数&#xff0c;直流分量的数学公式表示为&#xff1a; 判断有…

基于轻量级YOLOv5+Transformer的汽车车损检测识别分析系统

将传统NLP领域提出来的Transformer技术与yolo目标检测模型融合已经成为一种经典的做法&#xff0c;早在之前的很多论文里面就有这种组合应用的出现了&#xff0c;本文主要是借鉴前文的思路&#xff0c;开发基于yolov5transformer的汽车车损检测识别模型&#xff0c;首先看下效果…

光流相关总结

基于图像亮度恒定假设&#xff0c; 图像亮度&#xff1a;I(x⃗,t)I(\vec x, t)I(x,t), 其中x⃗[x,y]\vec x[x,y]x[x,y]&#xff0c;那么亮度恒定假设&#xff1a; I(x⃗,t)I(x⃗δx⃗,tδt)(1)I(\vec x,t)I(\vec x \delta \vec x, t \delta t) (1)I(x,t)I(xδx,tδt)(1) 对上式…

2022年值得记录的一年,事与愿违的一年

年初带着对生活的不满、怀才不遇的傲慢&#xff1b; 愿即将到来的30岁不留遗憾&#xff1b; 你放下所有去追求向往的样子&#xff1b; 那时所有的空气都是清新的&#xff0c;即使它满是灰尘&#xff1b; 不再年少的你依然充满新奇&#xff1b; 用尽力气把自己钉在那个不属…

前端与后端的技术通性

一、后端的JDK相当于前端的Node.js, 后端的JVM相当于前端的V8引擎【作用示例图&#xff0c;如下所示】 【Nodejs、JDK分别是前后端的运行环境】 二、后端的Maven&#xff08;基于项目对象模型-Project Object Model-POM的项目管理机制&#xff09;相当于前端的npm&#xff08;n…

FlinkCDC

目录1、CDC 简介1.1、什么是CDC1.2、CDC的种类1.3、Flink-CDC2、Flink CDC 网址3、运行原理5、简要安装6、开发案例7、扩展1、CDC 简介 1.1、什么是CDC CDC 是 Change Data Capture&#xff08;变更数据获取&#xff09;的简称。核心思想是&#xff0c;监测并捕获数据库的变动…

js实现网页特效

文章目录一、元素偏移量offest系列&#x1f947;offset与style的区别&#x1f393;案例1&#x1f9b9;&#x1f3fd;‍♂️案例2&#x1f43c;案例3二、元素可视区client系列三、元素滚动scroll系列&#x1f3c2;&#x1f3ff;案例4&#xff1a;&#x1f52d;补充 mouseenter事…

大数据分析案例-基于KNN算法对茅台股票进行预测

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

一个精美的主界面窗口功能的设计和实现原来如此简单,万字肝爆

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 秩沅 原创 收录于专栏 玩归玩闹归闹&#xff0c;别拿java开玩笑 —————————————————— ⭐相关文章⭐ -通过窗口看…

数据结构与算法:栈和队列的学习

1.栈 1.栈的定义 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶&#xff0c;另一端称为栈底。栈中的数据元素遵守后进先出LIFO&#xff08;Last In First Out&#xff09;的原则。 压栈&am…

AirServer2023免费无线Mac和PC电脑屏幕镜像投屏工具

AirServer2023是适用于 Mac 和 PC 的先进的屏幕镜像接收器。 它允许您接收 AirPlay 和 Google Cast 流&#xff0c;类似于 Apple TV 或 Chromecast 设备。AirServer 可以将一个简单的大屏幕或投影仪变成一个通用的屏幕镜像接收器 &#xff0c;是一款十分强大的投屏软件。AirSer…

Ansys Zemax | 眼科镜片设计

本文介绍了眼科镜片的设计原理&#xff0c;并讨论了镜片、眼睛和视觉环境中对镜片设计十分关键的参数&#xff0c;其中包括了常见镜片材料&#xff08;涵盖了玻璃和聚合物&#xff09;的玻璃目录。本文不包括渐进式镜片设计&#xff0c;尽管渐进式镜片时常根据一般的镜片曲率原…

【实际开发04】- XxxMapper.xml/java - 批量处理

目录 1. Model : XxxMapper.xml 1. IotTypeMapper.xml 基础 3 tips 2. Model : XxxMapper.java 1. IotTypeMapper.java 基础 3 tips 3. Others info 1. 模糊查询 2. 模糊查询 name 导致的异常 --> name 3. 连接查询 Where 限制主表 , 谨慎 : 使用副表限制 - ★ 4…

Java程序员如何使用代码来计算最大公约数和最小公倍数?

沉淀、分享、成长&#xff0c;让自己和他人都能有所收获&#xff01;&#x1f604; 一、前言 嘿&#xff0c;怎么突然讲到最大公约数了&#xff1f; 因为RSA算法&#xff0c;对于与欧拉结果计算的互为质数的公钥e&#xff0c;其实就需要使用到辗转相除法来计算出最大公约数。…

Java文件IO操作

目录 一、了解什么是文件 狭义的文件&#xff1a; 广义的文件&#xff1a; 二、文件的路径 ①文件的绝对路径 ②文件的相对路径 三、Java对于文件的操作 File类的构造方法 File类的普通方法 四、对于文件的内容操作 ①FileInputStream&#xff08;文件输入流&#xf…

ES索引备份还原

ES索引备份还原一、规划二、备份方案一&#xff1a;备份到集群共享目录方案二&#xff1a;备份到HDFSES还原一、规划 es数据出于线上数据安全考虑&#xff0c;对于es已有的索引数据可以进行安全备份&#xff0c;通常可以将es备份到共享文件目录或者一些其它的数据存储的文件系…

Splashtop Personal 安装教程

splashtop Personal 安装教程1. Splashtop Personal 概述2. splashtop Personal 安装步骤2.1 主控端&#xff08;Splashtop Business app&#xff09;2.2 被控端&#xff08;Splashtop Streamer&#xff09;2.3 打开主控端结束语1. Splashtop Personal 概述 Splashtop Persona…