Qt-拖放

概述

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

本文档描述了基本的拖放机制,并概述了在自定义控件中启用它的方法。Qt的许多控件也支持拖放操作,如item views和graphics view框架,以及Qt窗口组件和Qt Quick的编辑控件。关于项目视图和图形视图的更多信息可以在使用拖放项目视图和图形视图框架中找到。

拖放类

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

QDrag

支持基于mime的拖放数据传输

QDragEnterEvent

事件,当拖放操作进入widget时被发送给widget

QDragLeaveEvent

当拖放操作离开widget时发送给widget的事件

QDragMoveEvent

事件,在拖放操作进行时被发送

QDropEvent

事件,在拖放操作完成时发送

配置

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

  • startDragTime()描述了用户在拖动对象之前必须按下鼠标按钮的时间(以毫秒为单位)。
  • startDragDistance()表示用户在按住鼠标按钮的同时移动鼠标的距离,直到移动被解释为拖动。
  • startDragVelocity()表示用户移动鼠标以多快(以像素/秒为单位)开始拖动。值为0意味着没有这种限制。

如果你在控件中提供拖放支持,这些数量提供了合理的默认值,这些默认值与底层窗口系统兼容。

Qt Quick 拖放

文档的其余部分主要关注如何用c++实现拖放。对于在Qt快速场景中使用拖放,请阅读Qt快速拖放,DragEvent,和DropArea项目的文档,以及Qt快速拖放的例子。

draging拖

要开始拖动,创建一个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()函数是一个阻塞函数,返回多个值中的一个。这些表示操作如何结束,将在下文更详细地描述。

注意,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()函数来粗略估计鼠标单击发生的位置和当前光标位置之间的距离。此函数以准确性换取速度,通常适用于此目的。

droping

为了能够接收在小部件上丢弃的媒体,请为小部件调用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, QComboBox中填充了用于描述数据的MIME类型列表:

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中的首选操作调用event对象的setDropAction()。这确保使用替换放置操作,而不是建议的操作。

对于更复杂的应用程序,重新实现dragMoveEvent()和dragLeaveEvent()可以让部件的某些部分对放置事件敏感,从而对应用程序中的拖放有更多的控制。

子类化复杂的部件

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

拖放操作

在最简单的情况下,拖放操作的目标接收到被拖动数据的副本,而源决定是否删除原始数据。这由CopyAction操作描述。目标也可以选择处理其他操作,特别是MoveAction和LinkAction操作。如果源调用QDrag::exec(),并且返回MoveAction,则源负责删除任何原始数据,如果它选择这样做。由source widget创建的QMimeData和QDrag对象不应该被删除——它们会被Qt销毁。目标负责获取拖放操作中发送的数据的所有权;这通常通过保留数据的引用来实现。

如果目标理解了LinkAction动作,它应该存储自己对原始信息的引用;数据源不需要对数据进行任何进一步处理。最常使用的拖放操作是在同一个部件中进行移动时;有关此功能的更多信息,请参阅关于放置操作的部分。

拖动操作的另一个主要用途是使用text/uri-list等引用类型时,拖动的数据实际上是对文件或对象的引用。

添加新的拖放类型

拖放不仅限于文本和图像。任何类型的信息都可以在拖放操作中传递。要在应用程序之间拖动信息,应用程序必须能够相互指示它们可以接受哪些数据格式,可以生成哪些数据格式。这是通过使用MIME类型实现的。由源构造的QDrag对象包含一个用来表示数据的MIME类型的列表(从最合适到最不合适排序),并且drop目标使用其中的一个来访问数据。对于常见的数据类型,便利函数可以透明地处理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对象中的数据量。
注意,项目视图中使用的自定义数据类型必须声明为元对象,并且必须实现它们的流操作符。

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的矩形

小部件的dragMoveEvent()可以用来将放置限制在小部件的某些部分,即当光标位于这些区域内时,只接受建议的放置操作。例如,下面的代码在光标停留在子部件(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()信号通知应用程序它包含的数据发生了变化。例如,我们可以通过将这个信号连接到widget中的一个插槽来监视剪贴板:

connect(clipboard, &QClipboard::dataChanged,this, &ClipWindow::updateClipboard);

连接到这个信号的插槽可以使用一种MIME类型读取剪贴板上的数据,可以用来表示它:

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

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

例子

  • Draggable Icons 可拖放图标
  • Draggable Text 可拖放文本
  • Drop Site 删除网站
  • Fridge Magnets 自由拖放widget
  • Drag and Drop Puzzle 拖放拼图

与其他应用程序互操作

在X11上使用公共的XDND协议,而在Windows上Qt使用OLE标准,而在macOS上Qt使用Cocoa拖动管理器。在X11上,XDND使用MIME,因此不需要转换。Qt API与平台无关。在Windows上,支持MIME的应用程序可以通过使用MIME类型的剪贴板格式名称进行通信。已经有一些Windows应用程序为其剪贴板格式使用MIME命名约定。

可以通过在Windows上重新实现QWinMime或在macOS上重新实现QMacPasteboardMime来注册用于转换私有剪贴板格式的自定义类。

Drag and Drop | Qt GUI 5.15.17

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

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

相关文章

C++速通LeetCode中等第3题-盛最多水的容器

双指针法&#xff1a;两个指针分别指向左右边界&#xff0c;记录最大面积&#xff0c;由于面积由短板决定&#xff0c;两个指针中较短的短指针向内移动一格&#xff0c;再次记录最大面积&#xff0c; 直到两指针相遇&#xff0c;得出答案。 class Solution { public:int maxAr…

安卓13设置动态修改设置显示版本号 版本号增加信息显示 android13增加序列号

总纲 android13 rom 开发总纲说明 文章目录 1.前言2.问题分析3.代码分析4.代码修改5.编译6.彩蛋1.前言 设置 =》关于平板电脑 =》版本号 在这里显示了系统的一些信息,但是这里面的信息并不包含序列号之类的信息,我们修改下系统设置,在这里增加上相关的序列号。 2.问题分析…

打造以太坊数据监控利器:InfluxDB与Grafana构建Geth可视化分析平台

前言 以太坊客户端收集大量数据&#xff0c;这些数据可以按时间顺序数据库的形式读取。为了简化监控&#xff0c;这些数据可以输入到数据可视化软件中。在此页面上&#xff0c;将配置 Geth 客户端以将数据推送到 InfluxDB 数据库&#xff0c;并使用 Grafana 来可视化数据。 一…

多比特AI事业部VP程伟光受邀为第四届中国项目经理大会演讲嘉宾

全国项目经理专业人士年度盛会 武汉市多比特信息科技有限公司AI事业部VP程伟光先生受邀为PMO评论主办的全国项目经理专业人士年度盛会——2024第四届中国项目经理大会演讲嘉宾&#xff0c;演讲议题为“AI对于项目经理工作的影响和变化解析”。大会将于10月26-27日在北京举办&am…

深度学习02-pytorch-04-张量的运算函数

在 PyTorch 中&#xff0c;张量&#xff08;tensor&#xff09;运算是核心操作之一&#xff0c;PyTorch 提供了丰富的函数来进行张量运算&#xff0c;包括数学运算、线性代数、索引操作等。以下是常见的张量运算函数及其用途&#xff1a; 1. 基本数学运算 加法运算&#xff1a…

计算机组成体系与组成结构错题解析【软考】

目录 前言进制转换码制补码 CPU的组成输入/输出技术中断相关概念输入/输出技术的三种方式比较周期相关知识 主存编址计算流水线技术层次化存储体系可靠性 前言 本文专门用来记录本人在做软考中有关计算机上组成体系与组成结构的错题&#xff0c;我始终认为教学相长是最快提高的…

如何利用 opencv 进行 ROI(感兴趣)获取和实现 VR(虚拟现实) 演播室的播放

我是从事医疗软件的开发的。 经常需要从拍摄的医疗视频中获取出病理区域。并计算病理区域的周长和面积。 用 opencv 的术语,这就是感兴趣区域的获取。 (因为都是实时视频,所以速度很关键。代码效率很重要) 有时,需要标注出病理区域,并将非病理区域从视频中去除掉。 如果将…

[教程]如何在iPhone上启用中国移动/联通/电信RCS消息

目前 苹果已经在 iOS 18 中带来 RCS 富媒体消息的支持&#xff0c;该消息基于网络传递&#xff0c;用户可以通过 RCS 免费将消息发送到其他 iPhone 或 Android 设备。在苹果面向测试版用户推出的 iOS 18.1 Beta 版中&#xff0c;中国网络运营商包括中国移动、中国联通、中国电信…

STL-常用算法 遍历/查找/排序/拷贝和替换/算数生成/集合算法

STL常用算法 常用的遍历算法 for_each #define _CRT_SECURE_NO_WARNINGS #include<iostream> using namespace std; #include<vector> #include<algorithm>void myPrint(int v) {cout << v << " "; }class MyPrint { public:void op…

产品经理需要了解的AI模型

随着人工智能技术的迅猛发展&#xff0c;AI已经成为各行各业不可或缺的一部分。对于产品经理而言&#xff0c;了解AI模型不仅能促进产品的创新&#xff0c;还能帮助我们更好地理解用户需求&#xff0c;提升产品价值。 下面我将详细介绍四类重要的AI模型——自然语言处理&#…

【云原生安全篇】一文掌握Harbor集成Trivy应用实践

【云原生安全篇】一文掌握Harbor集成Trivy应用实践 目录 1 概念 1.1 什么是 Harbor 和 Trivy&#xff1f; 1.1.1 Harbor 1.1.2 Trivy 1.2 Harbor 与 Trivy 的关系 Trivy 在 Harbor 中的作用&#xff1a; 1.3 镜像扫描工作流程 2 实战案例&#xff1a;在Harbor 配置 Trivy …

2018年国赛高教杯数学建模D题汽车总装线的配置问题解题全过程文档及程序

2018年国赛高教杯数学建模 D题 汽车总装线的配置问题 一&#xff0e;问题背景   某汽车公司生产多种型号的汽车&#xff0c;每种型号由品牌、配置、动力、驱动、颜色5种属性确定。品牌分为A1和A2两种&#xff0c;配置分为B1、B2、B3、B4、B5和B6六种&#xff0c;动力分为汽油…

Golang | Leetcode Golang题解之第416题分割等和子集

题目&#xff1a; 题解&#xff1a; func canPartition(nums []int) bool {n : len(nums)if n < 2 {return false}sum, max : 0, 0for _, v : range nums {sum vif v > max {max v}}if sum%2 ! 0 {return false}target : sum / 2if max > target {return false}dp …

fastadmin 根据选择数据来传参给selectpage输入框

文章目录 js代码php代码&#xff1a;完结 js代码 $(document).on(change,#table .bs-checkbox [type"checkbox"],function(){let url$(#chuancan).attr(data-url)urlurl.split(?)[0]let idsTable.api.selectedids(table)if(ids.length){let u_id[]ids.forEach(eleme…

Seata学习笔记

目录 Seata的三大角色 角色 相关流程 相关事务模式 AT 模式&#xff08;默认模式&#xff09; 概述 整体机制 分析 XA 模式 概述 机制 分析 TCC 模式 概述 机制 分析 SAGA 模式 概述 机制 分析 参考&#xff1a; Seata的三大角色 角色 TC (Transaction Co…

虚拟机:4、配置12.5的cuda和gromacs

前言&#xff1a;本机环境是win11&#xff0c;通过wsl2安装了ubuntu实例并已实现gpu直通&#xff0c;现在需要下载12.5的cuda 一、查看是否有gpu和合适的cuda版本 在ubuntu实例中输入 nvidia-smi输出如下&#xff1a; 说明该实例上存在gpu驱动&#xff0c;且适合的CUDA版本…

智能新突破:AIOT 边缘计算网关让老旧水电表图像识别

数字化高速发展的时代&#xff0c;AIOT&#xff08;人工智能物联网&#xff09;技术正以惊人的速度改变着我们的生活和工作方式。而其中&#xff0c;AIOT 边缘计算网关凭借其强大的功能&#xff0c;成为了推动物联网发展的关键力量。 这款边缘计算网关拥有令人瞩目的 1T POS 算…

VS Code 技巧

在编程世界里&#xff0c;工具的好坏取决于使用者的水平。Visual Studio Code&#xff08;VS Code&#xff09;就像一把锋利的刀&#xff0c;它功能强大&#xff0c;但需要熟练的技巧才能发挥出色。然而&#xff0c;对于初学者来说&#xff0c;它可能显得有些复杂&#xff0c;因…

9.Branch-and-Bound 方法

Branch-and-Bound 方法 Branch-and-Bound&#xff08;分支限界&#xff09;是一种用于解决优化问题的算法框架&#xff0c;尤其适用于组合优化问题&#xff0c;如整数规划、旅行商问题&#xff08;TSP&#xff09;、指派问题等。该方法通过系统地搜索解空间树来找到问题的最优…

[spring]springboot日志

文章目录 一. 日志的用途二. 打印日志三. 日志框架门面模式(外观模式)SLF4J框架介绍 四. 日志格式日志级别配置日志级别日志持久化配置日志文件分割配置日志格式 五. 更简单的日志输出 一. 日志的用途 二. 打印日志 得到日志对象: 需要使用日志工厂LoggerFactory RestControl…