Qt5.14.2 大神的拖放艺术,优雅而强大的交互体验
作为图形界面软件,良好的用户交互体验是制胜的关键。而在Qt大神们的绝世编程之道中,拖放操作无疑占据着非常重要的一席之地。它不仅操作简单直观,而且可以完成大量看似复杂的任务,是提升用户体验质量的利器。今天,就让我们一同欣赏Qt大神们在这一领域的绝妙功力吧!
一、拖放之根本
作为操作系统级的基本功能,拖放(Drag and Drop)的使用原理看似简单,实则背后学问非浅。
简单地说,拖放操作由两个核心部分构成:
-
拖放源(Drag Source) - 用户开始拖动某个元素时,该元素所在的窗口、视图或控件就是拖放源。
-
拖放目标(Drop Target) - 当用户完成拖动并在其他区域释放元素时,这个新区域就是拖放目标。
拖放操作的本质,就是在源和目标之间传递一些数据(可视元素或底层数据)。因此Qt通过专门的API,让我们可以自由自在地控制这一过程。
在Qt中,拖放源和拖放目标是通过事件处理机制来确定的。让我们来分析一下相关的类和函数:
1、拖放源
要启动一次拖放操作,需要从QWidget继承的对象发出mousePressEvent()
和mouseMoveEvent()
事件。在mousePressEvent()
中,我们创建一个QDrag对象,并使用QDrag::exec()
函数启动拖放操作。
void MyWidget::mousePressEvent(QMouseEvent *event)
{if (event->button() == Qt::LeftButton)m_dragStartPosition = event->pos();
}void MyWidget::mouseMoveEvent(QMouseEvent *event)
{if (!(event->buttons() & Qt::LeftButton))return;if ((event->pos() - m_dragStartPosition).manhattanLength() < QApplication::startDragDistance())return;QDrag *drag = new QDrag(this);QMimeData *mimeData = new QMimeData;// 设置要传输的数据mimeData->setText(m_dragData);drag->setMimeData(mimeData);// 执行拖放操作Qt::DropAction dropAction = drag->exec(Qt::CopyAction | Qt::MoveAction);// 处理dropAction
}
在上面的代码中,mousePressEvent()
记录了鼠标按下的初始位置,而mouseMoveEvent()
则在鼠标移动一定距离后创建QDrag对象并启动拖放操作。我们可以在QDrag对象中设置要传输的MIME数据。
2、拖放目标
接收拖放操作的对象就是拖放目标。我们需要重新实现dragEnterEvent()
和dropEvent()
函数:
void MyWindow::dragEnterEvent(QDragEnterEvent *event)
{event->acceptProposedAction();
}void MyWindow::dropEvent(QDropEvent *event)
{const QMimeData *mimeData = event->mimeData();// 从mimeData获取数据if (mimeData->hasText()) {m_dropData = mimeData->text();// 处理拖放数据event->acceptProposedAction();}
}
在dragEnterEvent()
中,我们调用acceptProposedAction()
来允许这个窗口作为拖放目标。
在dropEvent()
中,根据QMimeData获取拖放源传输的数据,如果数据格式可接受,就调用acceptProposedAction()
接收这个拖放操作。
除了上述必需的事件处理函数外,Qt还提供了dragMoveEvent()
和dragLeaveEvent()
等函数,允许我们对拖放过程进行更细致的控制。
通过上面的分析,我们可以看到拖放源和拖放目标是由事件驱动的,通过重新实现相关的事件处理函数,我们就可以自定义拖放操作的各个环节。Qt框架已为我们提供了现成的基础设施,使得实现灵活且强大的拖放功能变得很容易。
3、灵活可控的动作类型
我们知道在不同场景下,拖放操作意味着不同的行为,比如移动、复制、链接等等。而Qt大神们也充分考虑到了这一点,为我们提供了Qt::DropAction枚举,允许我们精确指定拖放的行为类型:
Qt::CopyAction // 复制
Qt::MoveAction // 移动
Qt::LinkAction // 链接
Qt::ActionMask // 忽略该部分
Qt::IgnoreAction // 无操作
Qt::TargetMoveAction // 移动到目标
通过设置QDrag和QDragMoveEvent中的dropAction属性,我们就可以在发生拖放时明确告知目标需要执行何种操作。
QDrag *drag = new QDrag(this);
QMimeData *mimeData = new QMimeData;
// 设置mimeData内容
drag->setMimeData(mimeData);
drag->setPixmap(QPixmap("://images/drag_icon.png")); // 设置可视化预览// 默认传输模式是复制
Qt::DropAction dropAction = drag->exec(Qt::CopyAction | Qt::MoveAction);
通过上面这些精巧的安排,Qt让拖放操作变得极富弹性,既可以作为简单的移动形式,也可扮演复制、链接等更高级角色,应用领域广阔无垠。
二、拖放打开文件,文件管理新体验
文件管理是日常工作中最常见的场景,而有了拖放操作,我们就可以提供一种全新的交互方式,让用户在拖动文件图标的同时直接打开文件,效率大幅提升。比如:
// 主窗口部件
void MainWindow::dragEnterEvent(QDragEnterEvent *event)
{ event->acceptProposedAction(); // 接受拖放文件的操作
}void MainWindow::dropEvent(QDropEvent *event)
{const QMimeData *mimeData = event->mimeData();// 检查是否包含文件URL列表if (mimeData->hasUrls()) {QList<QUrl> urlList = mimeData->urls();// 遍历打开每个文件for (int i = 0; i < urlList.size(); ++i) {openFile(urlList.at(i).toLocalFile());}}
}// 文件选择框
void FileDialog::dragEnterEvent(QDragEnterEvent *event)
{if (event->mimeData()->hasUrls())event->acceptProposedAction();
}void FileDialog::dropEvent(QDropEvent *event)
{QList<QUrl> urls = event->mimeData()->urls();QString fileName;// 逐个打开文件foreach(QUrl url, urls) { fileName = url.toLocalFile();// 处理文件数据...}
}
上面的代码给出了在主窗口和文件对话框中拖放打开文件的基本实现思路。
首先我们需要重载dragEnterEvent和dropEvent方法,在前者中调用acceptProposedAction()允许此窗口作为拖放目标。
一旦在dropEvent中接收到文件拖放,就可以通过QMimeData解析出URL列表,并对每个URL调用对应的打开文件的操作。
这样一来,用户就可以在任意位置选中想要打开的文件,只需拖动拽入应用窗口即可完成打开操作了。无疑这比传统的文件菜单方式要高效得多,体验也更为直观自然。
三、拖放操作中传输其他类型的数据
Qt的拖放机制支持传输各种类型的数据,不仅限于文本。通过QMimeData类,我们可以封装自定义的数据类型,并在拖放操作中进行传输。
以一个简单的示例来说明,假设我们需要在拖放操作中传输自定义的Person对象,代码如下:
// person.h
class Person
{
public:Person(const QString &name, int age) : m_name(name), m_age(age) {}QString name() const { return m_name; }int age() const { return m_age; }private:QString m_name;int m_age;
};Q_DECLARE_METATYPE(Person)
首先,我们定义了一个简单的Person类,包含name和age两个属性。关键是最后一行,我们使用Q_DECLARE_METATYPE宏注册了Person类型,使其可以在Qt的元对象系统中使用。
// drag source
void MyWidget::mouseMoveEvent(QMouseEvent *event)
{...QDrag *drag = new QDrag(this);QMimeData *mimeData = new QMimeData;Person person("John Doe", 30);mimeData->setData("application/x-person", QByteArray((const char*)&person, sizeof(Person)));drag->setMimeData(mimeData);drag->exec(Qt::CopyAction);
}// drop target
void MyWindow::dropEvent(QDropEvent *event)
{const QMimeData *mimeData = event->mimeData();if (mimeData->hasFormat("application/x-person")) {QByteArray personData = mimeData->data("application/x-person");Person *person = (Person*)personData.data();qDebug() << "Name:" << person->name() << "Age:" << person->age();}
}
在拖放源的mouseMoveEvent中,我们创建了一个Person对象,并使用QMimeData::setData将其序列化为QByteArray,关键是指定了一个自定义的MIME类型"application/x-person"。
在拖放目标的dropEvent中,我们检查QMimeData是否包含"application/x-person"类型的数据,如果有就从QByteArray中反序列化出Person对象。
通过这种方式,我们就实现了在拖放操作中传输自定义数据类型的需求。Qt的拖放机制非常灵活强大,只要注册了自定义类型,我们就可以在拖放操作中自由传输,而不局限于纯文本数据。
此外,Qt内置的模型/视图类也已经对拖放操作提供了深度支持,我们可以很方便地基于QAbstractItemModel实现各种基于拖放的操作,比如结构化数据的移动、复制等。总之,Qt框架在保证扩展性的同时,也给予了我们充分发挥创意的空间,让我们能创造出无与伦比的优秀用户体验。
四、小心有"状态"
如果你以为拖放交互就这么简单,那可就太天真了。Qt大神们可是将各种可能的细节都考虑进去,并提供了强大的API让我们能对拖放过程进行无缝掌控。
比如QDragEnterEvent和QDragMoveEvent,就允许我们根据不同的鼠标状态做出对应的响应。我们来看个例子:
void MyWindow::dragEnterEvent(QDragEnterEvent *event)
{// 只接受纯文本数据if (event->mimeData()->hasFormat("text/plain")) {event->acceptProposedAction();}
}void MyWindow::dragMoveEvent(QDragMoveEvent *event)
{// 拖拽到窗口边缘时拒绝释放QRect rect(0, 0, width(), height());if (!rect.contains(event->pos())) {event->ignore();} else {event->acceptProposedAction();}
}
这里dragEnterEvent确保了只接受纯文本的拖放数据,而dragMoveEvent则通过检查鼠标位置,拒绝用户在窗口边缘释放数据。
通过处理不同的鼠标状态,我们就能智能地控制何时可以、何时不可以完成拖放操作,给用户以足够的反馈和暗示。
除此之外,Qt还提供了dragLeaveEvent()
让我们在拖放操作离开目标时进行必要的清理工作。
五、视觉反馈,种种淋漓
对于用户来说,视觉反馈是拖放交互体验的关键。幸运的是,我们都晓得Qt大神们可是老到家的UI专家,自然也在这方面下足了功夫。
首先,Qt的QDrag类允许我们方便地设置要显示的拖动图像:
QDrag *drag = new QDrag(this);
QMimeData *mime = new QMimeData;// 设置mime data
mime->setText(str);
drag->setMimeData(mime);// 设置拖动时显示的图像
drag->setPixmap(QPixmap(":/images/drag_icon.png"));// 设置热点,即图像的拖动锚点
drag->setHotSpot(QPoint(15, 15)); drag->exec();
上面代码中,setPixmap和setHotSpot分别用来指定要显示的像素图和图像拖动锚点,让用户可以直观地看到自己在拖拽什么。
其次,QDrag还允许我们设置期间要显示的各种指示光标类型,如Qt::ClosedHandCursor用于拖动、Qt::ForbiddenCursor用于禁止操作等等。
// 设置拖动过程中显示的光标
drag->setDragCursor(Qt::ClosedHandCursor);
当然,除了QDrag提供的默认视觉效果外,Qt还支持我们自定义渲染,以实现个性十足的拖动效果。QDrag提供了便捷的paintEvent,支持QPainter绘图,我们只需像绘制普通界面一样,对拖动图像做个性化处理即可。
void MyWindow::paintEvent(QPaintEvent *event)
{QPainter painter(this);painter.fillRect(...); // 自定义渲染效果
}
看到这里,你是不是也开始佩服Qt大神们的用心良苦了?细微之处处处精雕细琢,就是为了给用户带来无可挑剔的拖放体验。但说时过往,Qt的魔力远不止于此,我们还有更多惊喜要一一呈现!
相关文章:
Qt5.14.2 大神的拖放艺术,优雅而强大的交互体验
作为图形界面软件,良好的用户交互体验是制胜的关键。而在Qt大神们的绝世编程之道中,拖放操作无疑占据着非常重要的一席之地。它不仅操作简单直观,而且可以完成大量看似复杂的任务,是提升用户体验质量的利器。今天,就让…...
python3将exe 转支持库错误 AssertionError: None does not smell like code
exe -> pyc包(*.exe_extracted) 安装反编译工具 exe反编译工具:pyinstxtractor.py下载:https://sourceforge.net/projects/pyinstallerextractor/ python pyinstxtractor.py hello.exe包反编译 懒的写!!! 这有详…...
[EFI]Dell Inspiron 15 5567 电脑 Hackintosh 黑苹果efi引导文件
硬件型号驱动情况主板 Dell Inspiron 15R 5567 处理器Intel Core i7-7500U 2.50 GHz Processor (4M Cache, up to 3.50 GHz)已驱动内存8GB, 2400MHz, DDR4, up to 16GB已驱动硬盘东芝 NVMe 512G已驱动显卡Intel HD Graphics 620已驱动声卡ALC3246 Analog (ALC256)已驱动网卡无无…...
大学 Python 程序设计实验报告:判断密码是否符合要求
目录: 利用 string 模块判断使用正则表达式判断 密码强度判断,输入一个密码,判断密码是否符合要求。 要求密码长度8-12位,密码中必须包含大写字母、小写字母和数字,不能含有其他符号。 如果符合要求输出"密码符合…...
基于SpringBoot的农产品直卖平台
采用技术 基于SpringBoot的农产品直卖平台的设计与实现~ 开发语言:Java 数据库:MySQL 技术:SpringBootMyBatis 工具:IDEA/Ecilpse、Navicat、Maven 页面展示效果 用户功能 农产品信息 确认下单 农产品订单 购物车 商家功…...
DevSecOps平台架构系列-微软云Azure DevSecOps平台架构
目录 一、概述 二、Azure DevOps和黄金管道 2.1 概述 2.2 Azure DevOps架构说明 2.2.1 架构及管道流程图 2.2.2 架构内容 2.2.2.1 Azure Boards 2.2.2.2 Azure Repos 2.2.2.3 Azure Test Plans 2.2.2.4 Azure Pipelines 2.2.2.5 Azure Application Insights 2.2.2.6…...
操作系统:管程与进程通信机制解析
✨✨ 欢迎大家来访Srlua的博文(づ ̄3 ̄)づ╭❤~✨✨ 🌟🌟 欢迎各位亲爱的读者,感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢,在这里我会分享我的知识和经验。&am…...
inno setup 卸载程序 删除整个安装目录
业务场景:有次客户反馈说,卸载应用程序没有卸载干净,安装目录下残留很多文件,特别是一些配置文件和数据库文件,涉及到一些数据安全机密。卸载程序应该把安装目录都要清除。操作如下: 卸载应用程序…...
【Vue3源码学习】— CH2.5 reactiveEffect.ts:Vue 3响应式系统的核心
reactiveEffect.ts:Vue 3响应式系统的核心 1. 什么是 reactiveEffect?2. 核心机制2.1 依赖收集(Track)2.2 触发更新(Trigger)2.3 效果范围(effectScope) 3. 源码解析 —— track3.1 …...
K8S的mountPath和subPath
1 mountPath mountPath是容器内部文件系统的挂载点,它定义了容器内部将外部存储卷(如 PersistentVolume、ConfigMap、Secret 等)挂载到哪个路径下。通过 mountPath,容器可以访问这些挂载的数据或配置。 2 subPath subPath 是 m…...
notepad++里安装32位和64位的16进制编辑器Hex-Editor
这个16进制编辑器确实是个好东西,平时工作种会经常用到, 这是hex-editor的官网。这个里边只能下载32位的(64位的看最下边),选一个合适的版本,我当时选的是最新的版本 https://sourceforge.net/projects/npp-plugins/files/Hex%20E…...
Python类的基本结构
当我们在Python中定义类时,我们实际上是在创建一种新的数据类型。类允许我们定义对象的属性和方法,从而构建更复杂的程序。让我们深入探讨一下关于类的一些重要概念。 定义类:基本结构 一个类的基本结构包括以下部分: 类名&…...
利用HIVE的窗口函数进行SQL查询中出现的问题记录
student_info部分数据 score_info部分数据 course_info 1、问题复现 --完整SQL selectsti.stu_id,sti.stu_name,concat_ws(",",collect_set(ci.course_name)) over(partition by sti.stu_id) fromstudent_info sti left joinscore_info sci onsti.stu_idsci.stu_id l…...
更改chatglm认知
ChatGLM-Efficient-Tuning 下载源代码 下载ChatGLM-Efficient-Tuning 解压 创建虚拟环境 conda create --prefixD:\CondaEnvs\chatglm6btrain python3.10 cd D:\ChatGLM-Efficient-Tuning-main conda activate D:\CondaEnvs\chatglm6btrain安装所需要的包 pip install -r…...
WPF 界面命令绑定(MVVM结构)
1.创建模型数据类(M) /// <summary>/// 数据模型/// </summary>public class LoginDataModel{// 用户名private string _userName;public string UserName{get { return _userName; }set{_userName value;}}// 密码private string _passWor…...
常见手撕项目C++
常见手撕项目C 设计模式单例模式饿汉模式懒汉模式 策略模式策略接口实现具体的策略(虚函数重写)定义上下文用户调用 设计模式 单例模式 单例模式是一种常用的软件设计模式,其目的是确保一个类只有一个实例,并提供一个全局访问点来…...
创建一个批处理作业来处理大量数据,例如从数据库中读取数据并进行处理
创建一个批处理作业来处理大量数据,例如从数据库中读取数据并进行处理 要创建一个批处理作业来处理大量数据,您可以使用Spring Batch。Spring Batch是一个用于大规模批处理的框架,它提供了丰富的功能来处理复杂的批处理任务,如读…...
LeetCode 2.两数相加
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。 请你将两个数相加,并以相同形式返回一个表示和的链表。 你可以假设除了数字 0 之外,这两个数都不会以 0 …...
如何利用ChatGPT提升学术论文写作效率
ChatGPT无限次数:点击直达 如何利用ChatGPT提升学术论文写作效率 ChatGPT 是一种基于大规模预训练模型的自然语言处理工具,可以在各种文本生成任务中发挥作用,包括学术论文写作。利用ChatGPT,可以提高学术论文写作的速度和质量,帮…...
LLMs之Mistral:Mistral 7B v0.2的简介、安装和使用方法、案例应用之详细攻略
LLMs之Mistral:Mistral 7B v0.2的简介、安装和使用方法、案例应用之详细攻略 导读:Mistral AI首个7B模型发布于2023年9月,在基准测试中超越Llama 2 13B,一下子声名大振。Mistral 7B v0.2对应的指令调优版本Mistral-7B-Instruct-v0…...
深入解析Oracle数据库中的WITH AS(CTE)原理
Oracle数据库中的WITH AS子句(也称为公用表表达式CTE(Common Table Expression))是一种高级查询构造工具,它允许在一条SQL语句的开始部分定义临时的结果集(或称子查询),这个结果集可以被随后的查询主体多次…...
Linux 环境安装 Elasticsearch 8.X
安装前说明 首先确定操作系统,在Linux发行版上执行uname -a查看具体系统。我是Ubuntu系统,可以用直接用apt-get安装,也可以下载tar.gz包手动安装。使用apt-get安装更方便快速,但不同的文件会被安装到不同的目录,不方便…...
Java零基础-集合:函数式接口
哈喽,各位小伙伴们,你们好呀,我是喵手。 今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。 我是一名后…...
Redis Scan指令解析与使用示例
Redis Scan指令解析与使用示例 概念 想要从redis key列表中找到某个key,redis提供了一个简单粗暴的指令keys用来列出满足查询条件的所有key。 keys redis* keys redis*keykey指令非常简单,只要提供一个简单的正则表达式即可,但是有两个明显的…...
Qt+OpenGL入门教程(三)——绘制三角形
通过前两篇文章的学习,我想大家应该有了基本的理解,我们接下来实操一下。 创建Qt OpenGL窗口 QOpenGLWidget QGLWidget是传统QtOpenGL模块的一部分,与其他QGL类一样,应该在新的应用程序中避免使用。相反,从Qt5.4开始…...
springcloud基本使用(搭建eureka服务端)
创建springbootmaven项目 next next finish创建成功 删除项目下所有文件目录,只保留pox.xml文件 父项目中的依赖: springboot依赖: <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-s…...
第十二章:预处理命令
文章目录 第十二章:预处理命令宏定义无参宏定义带参数的宏定义 文件包含处理 第十二章:预处理命令 作用:由编译预处理程序对程序中的特殊命令作出解释,以产生新的源程序对其进行正式编译 C语言与其他语言的重要区别就是可以使用预…...
Game Audio Programming
音频编程时游戏开发中最容易忽略,学习资源又是很少的环节。接下来,你将和我探索人耳的工作机制。 what is sound? 我们可以解释电视机是如何通过眼睛传递视觉信息的,但却往往无法对听觉信息做出类似的解释。 对声音的科学研究被称为声学&…...
高风险IP来自哪里:探讨IP地址来源及其风险性质
在网络安全领域,高风险IP地址是指那些可能涉及恶意活动或网络攻击的IP地址。了解这些高风险IP地址的来源可以帮助网络管理员更好地识别和应对潜在的安全威胁。本文将探讨高风险IP地址的来源及其风险性质,并提供一些有效的应对措施。 风险IP查询…...
【每日跟读】常用英语500句(300~400)
【每日跟读】常用英语500句 I had to take a shower. 我洗了个澡 Go on in. 赶紧进去吧 Hold up. 等一下 They seem like nice people. 他们看起来像好人 Such a wonderful age. 如此美好的年纪 That’s very impressive. 真厉害 I can see that. 看得出来 You should …...
wordpress完整安装包/网址安全检测中心
目录 JQuery初级 概念快速入门JQuery对象和JS原生对象的区别选择器DOM操作 内容操作属性操作CRUD操作 案例 JQuery高级 动画遍历事件绑定插件 Ajax 概念实现方式 原生JS实现(了解)JQuery实现方式 $.ajax()$.get()$.post() json 概念语法 基本规则获取数据遍历 …...
网站建设容易吗/百度竞价seo排名
点击上方“程序员小灰”,选择“置顶公众号” 有趣有内涵的文章第一时间送达! 上一次送书活动刚刚完结,有9位幸运的小伙伴得到了想要的图书,但是更多的读者们并没有获奖。 不过大家不必遗憾,慷慨的博文视点再次赞助了10…...
精密模具东莞网站建设/鸿星尔克网络营销
javascript的一个不足之处是不能处理二进制数据,于是node中引入了Buffer类型。这个类型以一个字节(即8位)为单位,给数据分配存储空间。它的使用类似于Array,但是与Array又有不同:Buffer在定义的时候必须明确知道其长度…...
做p2p网站的公司/东莞做一个企业网站
知识章节参考:【六】Java面向对象...
曲靖程序网站建设/色盲测试图第六版
给定一个十进制数M,以及需要转换的进制数N,将十进制数M,转换成N进制数 输入为一行,M是(32)位整数,N(2<N<16),用空格隔开 eg:输入 7 2输出 111输出描述:为了每个测…...
网站换域名后需要多长时间才能收录恢复正常/今日新闻头条大事
微信小程序常用的功能之一就是:使用微信登录微信小程序,但是登录之后拿到用户信息之后,缺少用户的唯一标识。 通常使用openid作为微信用户的唯一主键。受限于微信小程序的限制,不能直接通过小程序前端拿到openid,具体…...