【Qt开发流程】之拖放操作1:介绍

描述

Drag and drop 提供了一种简单的可视化机制,用户可以使用它在应用程序之间和应用程序内部传输信息。拖放的功能类似于剪贴板的剪切和粘贴机制。

本文描述了基本的拖放机制,并概述了在自定义控件中启用该机制的方法。Qt的许多控件也支持拖放操作,例如the item views and graphics view framework项目视图和图形视图框架,以及Qt Widgets和Qt Quick的编辑控件。

Drag 和 Drop 类

以下类处理拖放和必要的mime类型编码和解码。

描述解释
QDragSupport for MIME-based drag and drop data transfer支持基于MIME的拖放数据传输
QDragEnterEventEvent which is sent to a widget when a drag and drop action enters it当拖放操作进入窗口部件时发送的事件
QDragLeaveEventEvent that is sent to a widget when a drag and drop action leaves it当拖放操作离开窗口部件时发送的事件
QDragMoveEventEvent which is sent while a drag and drop action is in progress在拖放操作进行中时发送的事件
QDropEventEvent which is sent when a drag and drop action is completed当拖放操作完成时发送的事件

配置

QStyleHints对象提供与拖放操作相关的一些属性:

  • QStyleHints::startDragTime()描述用户在按住鼠标按钮在一个对象上多长时间后,拖动操作开始的毫秒数。
  • QStyleHints::startDragDistance()指示用户在按住鼠标按钮时移动鼠标的距离,这之后移动才会被解释为拖动。
  • QStyleHints::startDragVelocity()指示用户必须移动鼠标的速度(以像素/秒计)以启动拖动。值为0意味着没有这样的限制。

这些量提供了合理的默认值,如果控件中提供拖放支持,可以使用它们,这些值符合底层窗口系统的要求。

Drag

要开始拖动,请创建一个QDrag对象,并调用其exec()函数。在大多数应用程序中,最好只在鼠标按钮被按下并且光标移动一定距离后才开始拖放操作。但是,启用从小部件拖动的最简单方法是重新实现小部件的mousePressEvent()函数并开始拖放操作:

  void MainWindow::mousePressEvent(QMouseEvent *event){if (event->button() == Qt::LeftButton&& iconLabel->geometry().contains(event->pos())) {QDrag *drag = new QDrag(this);QMimeData *mimeData = new QMimeData;mimeData->setText(commentEdit->toPlainText());drag->setMimeData(mimeData);drag->setPixmap(iconPixmap);Qt::DropAction dropAction = drag->exec();...}}

尽管用户可能需要一些时间来完成拖放操作,但对于应用程序而言,exec()函数是一个阻塞函数,返回下面几个值之一。这些值表示操作的结束方式,并在下面更详细地描述。

常量描述解释
Qt::CopyAction0x1Copy the data to the target.将数据复制到目标。
Qt::MoveAction0x2Move the data from the source to the target.将数据从源移到目标。
Qt::LinkAction0x4Create a link from the source to the target.从源到目标创建一个链接。
Qt::ActionMask0xff
Qt::IgnoreAction0x0Ignore the action (do nothing with the data).忽略动作(不对数据执行任何操作)。
Qt::TargetMoveAction0x8002On Windows, this value is used when the ownership of the D&D data should be taken over by the target application, i.e., the source application should not delete the data. On X11 this value is used to do a move. TargetMoveAction is not used on the Mac.在Windows上,当目标应用程序应该接管D&D数据的所有权时使用此值,即,源应用程序不应删除数据。在X11上,此值用于执行移动操作。TargetMoveAction在Mac上不使用。

请注意,exec()函数不会阻塞主事件循环。
对于需要区分鼠标点击和拖动的小部件,重新实现小部件的mousePressEvent()函数以记录拖动的起始位置非常有用:

 void DragWidget::mousePressEvent(QMouseEvent *event){if (event->button() == Qt::LeftButton)dragStartPosition = event->pos();}

随后,在mouseMoveEvent()中,我们可以确定是否应该开始拖动,并构造拖动对象来处理操作:

  void DragWidget::mouseMoveEvent(QMouseEvent *event){if (!(event->buttons() & Qt::LeftButton))return;if ((event->pos() - dragStartPosition).manhattanLength()< QApplication::startDragDistance())return;QDrag *drag = new QDrag(this);QMimeData *mimeData = new QMimeData;mimeData->setData(mimeType, data);drag->setMimeData(mimeData);Qt::DropAction dropAction = drag->exec(Qt::CopyAction | Qt::MoveAction);...}

这种方法使用QPoint::manhattanLength()函数来获取鼠标单击发生位置和当前光标位置之间距离的大致估计值。此函数以精度为代价提高了速度,并且通常适用于此目的。

Drop

要能够接收放置在小部件上的媒体,为小部件调用setAcceptDrops(true),并重新实现dragEnterEvent()dropEvent()事件处理程序函数。
例如,以下代码在QWidget子类的构造函数中启用了放置事件,使其可以有用地实现放置事件处理程序:

  Window::Window(QWidget *parent): QWidget(parent){...setAcceptDrops(true);}

dragEnterEvent()函数通常用于通知Qt小部件接受的数据类型。如果想在dragMoveEvent()dropEvent()的重新实现中接收QDragMoveEvent或QDropEvent,则必须重新实现此函数。
以下代码显示了如何重新实现dragEnterEvent()以告知拖放系统我们仅能处理纯文本:

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

dropEvent()用于解包放置的数据并以适合于您的应用程序的方式处理它。
在以下代码中,事件中提供的文本被传递到QTextBrowser,并使用描述数据的MIME类型列表填充QComboBox:

  void Window::dropEvent(QDropEvent *event){textBrowser->setPlainText(event->mimeData()->text());mimeTypeCombo->clear();mimeTypeCombo->addItems(event->mimeData()->formats());event->acceptProposedAction();}

在这种情况下,我们接受所建议的操作而不检查它是什么。在现实世界的应用程序中,如果操作不相关可能需要从dropEvent()函数中返回而不接受所建议的操作或处理数据。例如,如果我们的应用程序不支持链接到外部源,我们可以选择忽略Qt::LinkAction操作。

覆盖建议动作

我们也可以忽略建议的操作,对数据执行其他操作。为此,我们会在调用 accept()之前使用 Qt::DropAction 中的首选操作调用事件对象的 setDropAction()。这样确保使用替换的放置操作而不是建议的操作。
对于更复杂的应用程序,重新实现 dragMoveEvent() dragLeaveEvent()将使您能够使组件的某些部分对拖放事件敏感,并在应用程序中更好地控制拖放。

子类化控件

某些标准的 Qt 组件提供了自己的拖放支持。当子类化这些组件时,除了重新实现 dragEnterEvent() dropEvent()之外,可能还需要重新实现 dragMoveEvent(),以防止基类提供默认的拖放处理,并处理感兴趣的任何特殊情况。

Drag and Drop 动作

在最简单的情况下,拖放操作的目标将接收被拖动的数据的副本,源则决定是否删除原始数据。这由 CopyAction 操作描述。目标还可以选择处理其他操作,特别是 MoveAction LinkAction 操作。如果源调用 QDrag::exec(),并且返回 MoveAction,则源负责删除任何原始数据(如果选择删除)。源窗口小部件创建的 QMimeData QDrag 对象不应被删除 - 它们将被 Qt 销毁。目标负责接管拖放操作中发送的数据的所有权;通常是通过保留对数据的引用来完成的。
如果目标理解 LinkAction 操作,则应将自己的引用存储到原始信息中;源无需进一步处理数据。拖放操作最常见的用法是在同一个小部件中执行 Move 操作;
拖动操作的另一个主要用途是使用ext/uri-list等引用类型,其中拖动的数据实际上是文件或对象的引用。

添加新的 Drag and Drop 类型

拖放并不仅限于文本和图像。任何类型的信息都可以在拖放操作中传输。要在应用程序之间拖动信息,这些应用程序必须能够互相指示它们可以接受哪些数据格式和可以生成哪些数据格式。这是使用 MIME 类型实现的。源构建的QDrag对象包含一个 MIME 类型列表,它用于表示数据(从最合适到最不合适的顺序),而放置目标使用其中一个来访问数据。对于通用数据类型,方便函数会透明地处理使用的 MIME 类型,但对于自定义数据类型,必须明确说明它们。
要为QDrag方便函数未涵盖的信息类型实现拖放操作,首要重要的步骤是查找适合的现有格式:互联网分配的数字机构(IANA)在信息科学研究所(ISI)提供了一种层次结构的 MIME 媒体类型列表。使用标准 MIME 类型可以最大化您的应用程序与现在和将来的其他软件的互操作性。
要支持其他一种媒体类型,只需使用setData()函数在QMimeData对象中设置数据,提供完整的 MIME 类型和一个 QByteArray,其中包含适当格式的数据。以下代码从标签中获取一个 pixmap,并将其存储为可携带网络图形(PNG)文件在 QMimeData 对象中:

	  QByteArray output;QBuffer outputBuffer(&output);outputBuffer.open(QIODevice::WriteOnly);imageLabel->pixmap()->toImage().save(&outputBuffer, "PNG");mimeData->setData("image/png", output);

当然,对于该情况,可以简单地使用setImageData()来提供各种格式的图像数据:

mimeData->setImageData(QVariant(*imageLabel->pixmap()));

在这种情况下,QByteArray 方法仍然很有用,因为它可以更好地控制存储在QMimeData对象中的数据量。
请注意,item view 中使用的自定义数据类型必须声明为元对象,并且必须实现它们的流操作符。

Drop动作

在剪贴板模型中,用户可以剪切或复制源信息,然后稍后粘贴它。同样,在拖放模型中,用户可以拖动信息的副本或者他们可以将信息本身拖动到一个新位置(移动它)。对于程序员来说,拖放模型有一个额外的复杂性:在操作完成之前,程序不知道用户是要剪切还是复制这些信息。当在应用程序之间拖动信息时,通常这没有区别,但在应用程序内部,检查使用了哪些放置操作很重要。
可以重新实现小部件的mouseMoveEvent()方法,并使用可能的放置操作组合来启动拖放操作。例如,可能希望确保拖动对象时,在小部件中始终移动对象:

  void DragWidget::mouseMoveEvent(QMouseEvent *event){if (!(event->buttons() & Qt::LeftButton))return;if ((event->pos() - dragStartPosition).manhattanLength()< QApplication::startDragDistance())return;QDrag *drag = new QDrag(this);QMimeData *mimeData = new QMimeData;mimeData->setData(mimeType, data);drag->setMimeData(mimeData);Qt::DropAction dropAction = drag->exec(Qt::CopyAction | Qt::MoveAction);...}

如果信息被拖放到另一个应用程序中,exec() 函数返回的操作可能默认为 CopyAction。但是如果它被拖放到同一应用程序中的另一个小部件中,则可能会获得不同的放置操作。
建议的放置操作可以在小部件的 dragMoveEvent() 函数中进行过滤。但是,可以在 dragEnterEvent() 中接受所有建议的操作,让用户稍后决定想要接受哪个:

  void DragWidget::dragEnterEvent(QDragEnterEvent *event){event->acceptProposedAction();}

当小部件中发生拖放时,会调用dropEvent()处理程序函数,可以依次处理每个可能的操作。首先,处理在同一小部件内的拖放操作:

void DragWidget::dropEvent(QDropEvent *event){if (event->source() == this && event->possibleActions() & Qt::MoveAction)return;

在这种情况下,拒绝处理移动操作。会逐一检查和处理每种接受的拖放操作:

      if (event->proposedAction() == Qt::MoveAction) {event->acceptProposedAction();// Process the data from the event.} else if (event->proposedAction() == Qt::CopyAction) {event->acceptProposedAction();// Process the data from the event.} else {// Ignore the drop.return;}...}

请注意,上面的代码中检查了各个单独的拖放操作。如上面在关于重写拖放操作的建议中提到的,有时需要重写所建议的拖放操作,并从可能的拖放操作中选择不同的操作。为此,需要检查事件possibleActions()返回值中是否存在每个操作,使用setDropAction()设置拖放操作,并调用accept()函数。

Drop 区域

widgetdragMoveEvent()可以用于通过仅在光标位于那些区域时接受建议的放置操作,来限制放置操作到小部件的某些部分。例如,以下代码在光标悬停在子组件(dropFrame)上时接受任何建议的放置操作:

  void Window::dragMoveEvent(QDragMoveEvent *event){if (event->mimeData()->hasFormat("text/plain")&& event->answerRect().intersects(dropFrame->geometry()))event->acceptProposedAction();}

如果需要在拖放操作期间给予可视反馈、滚动窗口或进行其他适当的操作,dragMoveEvent()也可以用于此目的。

剪切

应用程序还可以通过将数据放入剪贴板来彼此通信。要访问此内容,需要从QApplication对象获取QClipboard对象。
QMimeData类用于表示传输到和自剪贴板的数据。为了将数据放入剪贴板,可以使用setText()、setImage()setPixmap()方便函数来处理常见的数据类型。这些函数类似于QMimeData类中的函数,但它们还采用一个附加参数,用于控制数据存储的位置:如果指定了Clipboard,则数据被放置在剪贴板上;如果指定了Selection,则数据被放置在鼠标选择中(仅在X11上)。默认情况下,数据被放入剪贴板。
例如,可以使用以下代码将QLineEdit的内容复制到剪贴板中:

QGuiApplication :: clipboard()->setText(lineEdit->text(),QClipboard :: Clipboard);

还可以在剪贴板上放置具有不同MIME类型的数据。构造一个QMimeData对象,并按照前面部分所述的方式使用setData()函数设置数据;然后可以使用setMimeData()函数将此对象放入剪贴板。
QClipboard类可以通过其dataChanged()信号通知应用程序其所包含数据的更改。例如,可以通过将此信号连接到窗口小部件中的插槽来监视剪贴板:

connect(clipboard,SIGNAL(dataChanged()),this,SLOT(updateClipboard()));

连接到此信号的槽可以使用可用于表示其的MIME类型之一之一读取剪贴板上的数据:

void ClipWindow :: updateClipboard()
{QStringList formats = clipboard->mimeData()->formats();QByteArray data = clipboard->mimeData()->data(format);}

在X11上,可以使用selectionChanged()信号来监视鼠标选择。

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

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

相关文章

torch.nn.batchnorm1d,torch.nn.batchnorm2d,torch.nn.LayerNorm解释:

批量归一化是一种加速神经网络训练和提升模型泛化能力的技术。它对每个特征维度进行标准化处理&#xff0c;即调整每个特征的均值和标准差&#xff0c;使得它们的分布更加稳定。 Batch Norm主要是为了让输入在激活函数的敏感区。所以BatchNorm层要加在激活函数前面。 1.torch.…

如何用低代码的思路设计文字描边渐变组件

前言 文字特效设计一直是困扰 Web 前端 Css 世界多年的问题, 比如如何用纯 Css 实现文字描边, 渐变, 阴影等, 由于受限于浏览器兼容性的问题, 我们不得不使用其他替代方案来实现. 平时工作中我们使用 PS 等设计工具能很容易的实现文字渐变等特效, 但是随着可视化技术的成熟, 我…

Swagger在php和java项目中的应用

Swagger在php和java项目中的应用 Swagger简介Swagger在java项目中的应用步骤常用注解 Swagger在php项目中的应用 Swagger简介 Swagger 是一个规范和完整的框架&#xff0c;用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。 总体目标是使客户端和文件系统作为服务器以…

MyBatis的功能架构,MyBatis的框架架构设计,Mybatis都有哪些Executor执行器,Mybatis中如何指定使用哪一种Executor执行器

文章目录 MyBatis的功能架构是怎样的把Mybatis的功能架构分为三层&#xff1a; **MyBatis的框架架构设计**是怎么样的架构图如下Mybatis都有哪些Executor执行器&#xff1f;它们之间的区别是什么&#xff1f;Mybatis中如何指定使用哪一种Executor执行器&#xff1f; MyBatis的功…

python统计字符串中大小写字符个数的性能实测与分析

给定一个字符串&#xff0c;统计字符串中大写字符个数&#xff0c;有如下三种方法&#xff1a; # method1 s1 len(re.findall(r[A-Z],content)) # method2 s2 sum(1 for c in content if c.isupper()) # method3 s3 0 for c in content:if c.isupper()True:s31经过多次实测…

SASS的导入文件详细教程

文章目录 前言导入SASS文件使用SASS部分文件默认变量值嵌套导入原生的CSS导入后言 前言 hello world欢迎来到前端的新世界 &#x1f61c;当前文章系列专栏&#xff1a;Sass和Less &#x1f431;‍&#x1f453;博主在前端领域还有很多知识和技术需要掌握&#xff0c;正在不断努…

人工智能入门教学——AI代理(AI Agent)

目录 一、简介 二、特征 三、结构 四、工作流程 五、类型 六、应用 一、简介 AI代理 (Artificial Intelligence Agent)是指使用人工智能技术和算法来执行特定任务、解决问题或实现目标的程序或系统。这些代理可以是简单的程序&#xff0c;也可以是复杂的系统&#xff0c…

面向对象编程:Rust的面向对象特性

欢迎关注我的公众号lincyang新自媒体&#xff0c;回复关键字【程序员经典书单】&#xff0c;领取程序员的100本经典书单 大家好&#xff01;我是lincyang。 今天我们将深入探讨Rust语言中的面向对象编程&#xff08;OOP&#xff09;特性&#xff0c;并将其与其他流行编程语言进…

vs动态库生成过程中还存在静态库

为什么VS生成动态库dll同时还会生成lib静态库 动态库与静态库&#xff08;Windows环境下&#xff09; ​ 动态库和静态库都是一种可执行代码的二进制形式&#xff0c;可以被操作系统载入内存执行。 ​ 静态库实际上是在链接时被链接到exe的&#xff0c;编译后&#xff0c;静态…

常见遍历方法 for循环、forEach、map、filter、find、findIndex、some、every

for循环 来自于远古的遍历方式&#xff0c;并且涵盖多种手段&#xff0c;例如for in 和for of。 for(let i 1; i<5; i){ console.log("这是第"i"次""循环") } for循环 中使用break和continue语句&#xff08;终止和跳过本次循环&#x…

基于U-Net的视网膜血管分割(Pytorch完整版)

基于 U-Net 的视网膜血管分割是一种应用深度学习的方法&#xff0c;特别是 U-Net 结构&#xff0c;用于从眼底图像中分割出视网膜血管。U-Net 是一种全卷积神经网络&#xff08;FCN&#xff09;&#xff0c;通常用于图像分割任务。以下是基于 U-Net 的视网膜血管分割的内容&…

mysql高级知识点

一、mysql架构 连接层&#xff1a;负责接收客户端的连接请求&#xff0c;可以进行授权、认证(验证账号密码)。服务层&#xff1a;负责调用sql接口&#xff0c;对sql语法进行解析&#xff0c;对查询进行优化&#xff0c;缓存。引擎层&#xff1a;是真正进行执行sql的地方&#x…

Linux面试题(二)

目录 17、怎么使一个命令在后台运行? 18、利用 ps 怎么显示所有的进程? 怎么利用 ps 查看指定进程的信息&#xff1f; 19、哪个命令专门用来查看后台任务? 20、把后台任务调到前台执行使用什么命令?把停下的后台任务在后台执行起来用什么命令? 21、终止进程用什么命令…

Vue框架学习笔记——事件修饰符

文章目录 前文提要事件修饰符prevent&#xff08;常用&#xff09;stop&#xff08;不常用&#xff09;事件冒泡stop使用方法三层嵌套下的stop三层嵌套看出的stop&#xff1a; once&#xff08;常用&#xff09;capture&#xff08;不常用&#xff09;self&#xff08;不常用&a…

C++期末考试选择题题库100道C++期末判断题的易错知识点复习

今天备考C&#xff0c;看到了一些好的复习资料&#xff0c;整合一起给大家分享一下 选择题 对于常数据成员&#xff0c;下面描述正确的是 【 B 】 A. 常数据成员必须被初始化&#xff0c;并且不能被修改 B. 常数据成员可以不初始化&#xff0c;并且不能被修改 C. 常数据成…

Vue轻松入门,附带学习笔记和相关案例

目录 一Vue基础 什么是Vue&#xff1f; 补充&#xff1a;mvvm框架 mvvm的组成 详解 Vue的使用方法 1.直接下载并引入 2.通过 CDN 使用 Vue 3.通过npm安装 4.使用Vue CLI创建项目 二插值表达式 什么是插值表达式&#xff1f; 插值表达式的缺点 解决方法 相关代…

高精度/单精度

#include<cstdio> using namespace std; int a[10000];//用来模拟笔算的数组(我一般习惯开大一点) int y;//除数 int l0;//记录被除数有多少位 int yushu;//模拟到每一位的运算时的余数 int shang;//模拟到每一位的运算时的商 int f0;//输出时记录是否应该输出当前这…

【数据结构】树与二叉树(廿五):树搜索指定数据域的结点(算法FindTarget)

文章目录 5.3.1 树的存储结构5. 左儿子右兄弟链接结构 5.3.2 获取结点的算法1. 获取大儿子、大兄弟结点2. 搜索给定结点的父亲3. 搜索指定数据域的结点a. 算法FindTargetb. 算法解析c. 代码实现a. 使用指向指针的指针b. 直接返回找到的节点 4. 代码整合 5.3.1 树的存储结构 5.…

VUE限制文件上传大小和上传格式

<el-form-item label"图片&#xff1a;" prop"tempImagePath"><el-uploadclass"upload"accept"image/jpeg":show-file-list"false"list-type"picture-card":headers"{ token: token}":action&…

linux的netstat命令和ss命令

1. 网络状态 State状态LISTENING监听中&#xff0c;服务端需要打开一个socket进行监听&#xff0c;侦听来自远方TCP端口的连接请求ESTABLISHED已连接&#xff0c;代表一个打开的连接&#xff0c;双方可以进行或已经在数据交互了SYN_SENT客户端通过应用程序调用connect发送一个…