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…...

python打卡day49
知识点回顾: 通道注意力模块复习空间注意力模块CBAM的定义 作业:尝试对今天的模型检查参数数目,并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...

shell脚本--常见案例
1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件: 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...

【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例
文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...

linux arm系统烧录
1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 (忘了有没有这步了 估计有) 刷机程序 和 镜像 就不提供了。要刷的时…...
数据链路层的主要功能是什么
数据链路层(OSI模型第2层)的核心功能是在相邻网络节点(如交换机、主机)间提供可靠的数据帧传输服务,主要职责包括: 🔑 核心功能详解: 帧封装与解封装 封装: 将网络层下发…...
【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统
目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索(基于物理空间 广播范围)2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...

GC1808高性能24位立体声音频ADC芯片解析
1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率,…...
在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?
uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件,用于在原生应用中加载 HTML 页面: 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题
在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件,这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下,实现高效测试与快速迭代?这一命题正考验着…...

MySQL 知识小结(一)
一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库,分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷,但是文件存放起来数据比较冗余,用二进制能够更好管理咱们M…...