Qt实现拖拽功能(支持拖放文件、拖放操作)

目录

  • 拖放
  • Qt程序接受其他程序的拖拽
  • 部件/控件之间相互拖放
  • 总结


拖放

拖放是在一个应用程序内或者多个应用程序之间传递信息的一种直观的现代操作方式。除了为剪贴板提供支持外,通常它还提供数据移动和复制的功能。

拖放操作包括两个截然不同的动作:拖动和放下。Qt窗口部件可以作为拖动点(darg site)、放下点(drop site)或者同时作为拖动点和放下点。

Qt程序接受其他程序的拖拽

我们经常将文本文件推拽到notepate++等类型文本编辑器软件中。那么如何让Qt程序也能够支持这种操作呢?

我们需要在主窗口重新实现了来自父类的dragEnterEvent()dropEvent()函数。

protected:virtual void dragEnterEvent(QDragEnterEvent* event) override;virtual void dropEvent(QDropEvent* event) override;

在构造函数中,创建了一个QTextEdit并且把它设置为中央窗口部件。默认情况下,QTextEdit可以接受来自其他应用程序文本的拖动,并且如果用户在它上面放下一个文件,它将会把这个文件的内容填充到QTextEdit部件中。

由于拖放事件是从子窗口部件传递给父窗口部件的,所以通过禁用QTextEdit上的放下操作以及启用主窗口上的放下操作,就可以在整个MainWindow窗口中获得放下事件。

#include <QMimeData>
#include <QTextStream>
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);textEdit = new QTextEdit(this);setCentralWidget(textEdit);textEdit->setAcceptDrops(false);setAcceptDrops(true);setWindowTitle("Text Editor");
}

当用户把一个对象拖动到这个窗口部件上时,就会调用dragEnterEvent()。如果对这个事件调用acceptProposedAction(),就表明用户可以在这个窗口部件上拖放对象。默认情况下,窗口部件是不接受拖动的。Qt会自动改变光标来向用户说明这个窗口部件是不是有效的放下点

这里,我们希望用户拖动的只能是文件,而非其他类型的东西。为了实现这一点,我们可以检查拖动的MIME类型。

MIME类型中的 text/uri-list 用于存储一系列的统一资源标识符(Universal Re-source Identifier ,URI),它们可以是文件名、统―资源定位器(Uniform Resource Locator , URL,如 HTTP或者FTP路径),或者其他全局资源标识符。标准的MIME类型是由国际因特网地址分配委员会(Internet Assigned Numbers Authority , IANA)定义的,它们由类型、子类型信息以及分隔两者的斜线组·成。MME类通常由剪贴板和拖放系统使用,以识别不同类型的数据。可以从下面的网站得到正式的 MIME类型列表: http://www.iana.org/assignments/media-types/

void MainWindow::dragEnterEvent(QDragEnterEvent *event)
{if(event->mimeData()->hasFormat("text/uri-list")){event->acceptProposedAction();}
}

当用户在窗口部件上放下一个对象时,就会调用dropEvent()。.我们调用函数QMimeData::urls()来获得QUrl列表。通常,用户一次只拖动一个文件,但是通过拖动一个选择区域来同时拖动多个文件也是可能的。如果要拖放的URL不止一个,或者要拖放的URL,不是一个本地文件名,则会立即返回到原调用处。
QWidget也提供 dragMoveEvent()和 dragLeaveEvent()函数,但是在绝大多数应用程序中并不需要重新实现它们。

void MainWindow::dropEvent(QDropEvent *event)
{QList<QUrl> urls = event->mimeData()->urls();if(urls.empty())return;QString fileName = urls.first().toLocalFile();if(fileName.isEmpty())return;if(ReadFile(fileName)){setWindowTitle(QString("%1-%2").arg(fileName).arg("Drag File"));}
}
bool MainWindow::ReadFile(const QString &filename)
{QFile file(filename);file.open(QIODevice::ReadOnly | QIODevice::Text | QIODevice::Truncate);if(false == file.isOpen()){return false;}QTextStream stream(&file);textEdit->insertPlainText(stream.readAll());file.flush();file.close();return true;
}

看看效果吧
在这里插入图片描述

部件/控件之间相互拖放

我们将实现类似于一个下图的效果,但不需要向左向右的按键,通过拖拽目标实现移动。
在这里插入图片描述

思路:创建一个支持拖拽的QListWidget子类ProjectListWidget,并且作为该界面的一个部件。

在ProjectListWidget类中需要重写父类的五个事件,和一个私有方法以及一个坐标记录

protected:virtual void mousePressEvent(QMouseEvent* event) override;virtual void mouseMoveEvent(QMouseEvent* event) override;virtual void dragEnterEvent(QDragEnterEvent* event) override;virtual void dragMoveEvent(QDragMoveEvent* event) override;virtual void dropEvent(QDropEvent* event) override;
private:void performDrag();private:QPoint startPos;

在构造函数中,我们使列表框上的放下生效。

#include <QApplication>
#include <QDrag>
#include <QMimeData>
ProjectListWidget::ProjectListWidget(QWidget* parent):QListWidget{parent}
{setAcceptDrops(true);
}

当用户按下鼠标左键,就把鼠标位置保存到statPos私有变量中。然后我们正常调用QListWidget 中mousePressEvent()

void ProjectListWidget::mousePressEvent(QMouseEvent *event)
{if(event->button() == Qt::LeftButton){startPos = event->pos();}QListWidget::mousePressEvent(event);
}

当用户按住鼠标左键并移动鼠标光标时,就认为这是一个拖动的开始。我们计算当前鼠标位置和原来鼠标左键按下的点之间的距离-—这个“曼哈顿长度”(Manhattan Length)其实是从坐标原点到该矢量长度快速计算的近似值。如果这个距离大于或等于QApplication推荐的拖动起始距离值(通常是4个像素),那么就调用私有函数performDrag()以启动拖动操作。这可以避免用户因为手握鼠标抖动而产生拖动。

void ProjectListWidget::mouseMoveEvent(QMouseEvent *event)
{if(event->buttons() & Qt::LeftButton){if(int distance = (event->pos() - startPos).manhattanLength();distance >= QApplication::startDragDistance()){performDrag();}}QListWidget::mouseMoveEvent(event);
}

perfomDrag()中,创建了一个类型为QDrag的对象,并且把this作为它的父对象。这个QDrag对象将数据存储在QMimeData对象中。在这个实例中,我们利用QMineData::setText()提供了作为text/plain字符串的数据。QMimeData 提供了一些可用于处理最常用拖放类型(诸如图像、URL、颜色,等等)的函数,同时也可以处理任意由QByteArrays表宗的MiME类型。QDrag::setPiximap()调用则可以在拖放发生时使图标随光标移动。
QDrag::exec()调用启动并执行拖动操作;直到用户放下或取消此次拖动操作才会停止。它把所有支持的“拖放动作"(如 Qi: : CopyAction, Qt : : MoveAction和 Qt: : LinkAction)的组合作为其参数,并且返回被执行的拖放动作(如果没有执行任何动作,则返回Qt: IgnoreAction)。至于执行的是哪`个动作,取决于放下发生时源窗口部件是否允许、目标是否支持及按下了哪些组合键。在 exec()调用后,Qt拥有拖动对象的所有权并且可以在不需要它的时候删除它。

void ProjectListWidget::performDrag()
{if(QListWidgetItem* item = currentItem();nullptr != item){QMimeData* mineData = new QMimeData();mineData->setText(item->text());QDrag* drag = new QDrag(this);drag->setMimeData(mineData);drag->setPixmap(QPixmap(":/icon.jpg"));if(drag->exec(Qt::MoveAction) == Qt::MoveAction){delete item;item = nullptr;}}
}	

ProjectListWidget窗口部件不仅能发起拖动,还可以接收同一个应用程序中来自另外一个ProjectListWidget部件的拖动。如果窗口部件是同个应用程序的一部分,QDragEnterEvent::source()返回一个启动这个拖动的窗口部件的指针;否则,返回一个空指针值。

void ProjectListWidget::dragEnterEvent(QDragEnterEvent *event)
{ProjectListWidget* source = reinterpret_cast<ProjectListWidget*>(event->source());if(nullptr != source && source != this){event->setDropAction(Qt::MoveAction);event->accept();}
}

dragMoveEvent()中的代码与dragEnterEvent()中编写的代码基本相同。因为需要重写QListWidget的函数实现(实际上是 QAbstractItemView的函数实现)。

void ProjectListWidget::dragMoveEvent(QDragMoveEvent *event)
{ProjectListWidget* source = reinterpret_cast<ProjectListWidget*>(event->source());if(nullptr != source && source != this){event->setDropAction(Qt::MoveAction);event->accept();}
}

在dropEvent()中,我们使用QMimeData::text()重新找回拖动的文本并随文本创建一个拖动项。还需要将事件作为“移动动作”来接受,从而告诉源窗口部件现在可以删除原来的拖动项了。

void ProjectListWidget::dropEvent(QDropEvent *event)
{ProjectListWidget* source = reinterpret_cast<ProjectListWidget*>(event->source());if(nullptr != source && source != this){addItem(event->mimeData()->text());event->setDropAction(Qt::MoveAction);event->accept();}
}

效果如下
在这里插入图片描述

总结

拖放是在应用程序之间传递数据的有力机制。但是在某些情况下;,有可能在执行拖放时并未使用Qt的拖放工具。如果只是想在一个应用程序的窗口部件中移动数据,通常只要重新实现mousePressEvent()和 mouseReleaseEvent()函数就可以了。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MATLAB 之 Simulink 子系统及其封装

这里写目录标题 一、子系统及其封装1. 子系统的创建1.1 通过 Subsystem 模块建立子系统1.2 通过已有的模块建立子系统 2. 子系统的条件执行2.1 使能子系统2.2 触发子系统2.3 使能加触发子系统 3. 子系统的封装3.1 lcon & Ports 选项卡的参数设置3.2 Parameters & Dialo…

控制请求来源的HTML Meta标签 - Referrer详解

83. 控制请求来源的HTML Meta标签 - Referrer详解 在Web开发中&#xff0c;为了保护用户的隐私和安全&#xff0c;控制请求的来源信息是至关重要的。HTML中的<meta>标签提供了一种简单而有效的方式来控制请求的来源&#xff0c;其中包括Referrer&#xff08;引荐者&…

SpringMVC原理分析 | Controller配置、RestFul风格

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; Controller配置 控制器Controller 控制器复杂提供访问应用程序的行为&#xff0c;通常通过接口定义或注释定义的两种方法实现控制器负责解析用户的请求并将其转换为一…

SpringBoot整合定时任务技术Quartz

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ RequestMapping注解 &#x1f680;Quartz应用场…

在教育领域中使用ChatGPT有哪些优点?

人工智能在教育领域的应用正在迅速增加。OpenAI于2022年11月开发的聊天机器人ChatGPT在全球范围内广受欢迎。 由于其受欢迎程度以及生成类似人类问题的回答的能力&#xff0c;ChatGPT正在成为许多学习者和教育工作者值得信赖的伴侣。然而&#xff0c;与任何新兴技术一样&#x…

2019年全国硕士研究生入学统一考试管理类专业学位联考逻辑试题——纯享题目版

&#x1f3e0;个人主页&#xff1a;fo安方的博客✨ &#x1f482;个人简历&#xff1a;大家好&#xff0c;我是fo安方&#xff0c;考取过HCIE Cloud Computing、CCIE Security、CISP等证书。&#x1f433; &#x1f495;兴趣爱好&#xff1a;b站天天刷&#xff0c;题目常常看&a…

原码的表示

原码表示 定点整数源码与定点小数源码 源码表示例题 正数与负数转换直接将高位变为1即可 原码的性质 原码的优缺点 乘除法直接符号位异或&#xff0c;数值相乘除即可加法与减法需要先判断两个数值的大小然后确定符号位

2.SpringBoot运维实用篇

SpringBoot运维实用篇 ​ ​ 下面就从运维实用篇开始讲&#xff0c;在运维实用篇中&#xff0c;我给学习者的定位是玩转配置&#xff0c;为开发实用篇中做各种技术的整合做好准备工作。 主要分为以下内容&#xff1a; SpringBoot程序的打包与运行配置高级多环境开发日志 ​…

【需求实现】Tensorflow2的曲线拟合(二):进度条简化

文章目录 导读普通的输出方式上下求索TensorBoard是个不错的切入点与Callback参数对应的Callback方法官方的内置Callback官方进度条简单的猜测与简单的验证拼图凑齐了&#xff01; 导读 在训练模型的过程中往往会有日志一堆一堆的困扰。我并不想知道&#xff0c;因为最后我会在…

【域名详解】网络杂谈(13)之深入简出了解什么是域名

涉及知识点 什么是域名&#xff0c;域名的概念&#xff0c;域名的结构&#xff0c;域名地址的寻址过程&#xff0c;深入了解域名的寻址机制。 原创于&#xff1a;CSDN博主-《拄杖盲学轻声码》&#xff0c;更多内容可去其主页关注下哈&#xff0c;不胜感激 文章目录 涉及知识点…

基于Tars高并发IM系统的设计与实现-基础篇1

基于Tars高并发IM系统的设计与实现–基础篇1 作者简介 兰怀玉 毕业于中央民族大学计算机专业 先后供职国内外多家公司软件研发设计岗位&#xff0c;有丰富的软件研发经验。 从事IM领域设计研发十余年&#xff0c;先后领衔多个IM通讯系统设计与研发发&#xff0c;拥有丰富的IM…

linux下RabbitMQ的使用

文章目录 linux下RabbitMQ的使用首先docker启动网页打开网址&#xff1a;用户名和密码登录创建exchanges:创建Queues增加Queues的Bind linux下RabbitMQ的使用 首先docker启动 su rootsudo docker run -d --hostname rabbitsvr --name rabbit -p 5672:5672 -p 15672:15672 -p …