当前位置: 首页 > news >正文

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 大神的拖放艺术,优雅而强大的交互体验

作为图形界面软件&#xff0c;良好的用户交互体验是制胜的关键。而在Qt大神们的绝世编程之道中&#xff0c;拖放操作无疑占据着非常重要的一席之地。它不仅操作简单直观&#xff0c;而且可以完成大量看似复杂的任务&#xff0c;是提升用户体验质量的利器。今天&#xff0c;就让…...

python3将exe 转支持库错误 AssertionError: None does not smell like code

exe -> pyc包(*.exe_extracted) 安装反编译工具 exe反编译工具&#xff1a;pyinstxtractor.py下载&#xff1a;https://sourceforge.net/projects/pyinstallerextractor/ python pyinstxtractor.py hello.exe包反编译 懒的写&#xff01;&#xff01;&#xff01; 这有详…...

[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 程序设计实验报告:判断密码是否符合要求

目录&#xff1a; 利用 string 模块判断使用正则表达式判断 密码强度判断&#xff0c;输入一个密码&#xff0c;判断密码是否符合要求。 要求密码长度8-12位&#xff0c;密码中必须包含大写字母、小写字母和数字&#xff0c;不能含有其他符号。 如果符合要求输出"密码符合…...

基于SpringBoot的农产品直卖平台

采用技术 基于SpringBoot的农产品直卖平台的设计与实现~ 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBootMyBatis 工具&#xff1a;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的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…...

inno setup 卸载程序 删除整个安装目录

业务场景&#xff1a;有次客户反馈说&#xff0c;卸载应用程序没有卸载干净&#xff0c;安装目录下残留很多文件&#xff0c;特别是一些配置文件和数据库文件&#xff0c;涉及到一些数据安全机密。卸载程序应该把安装目录都要清除。操作如下&#xff1a; 卸载应用程序&#xf…...

【Vue3源码学习】— CH2.5 reactiveEffect.ts:Vue 3响应式系统的核心

reactiveEffect.ts&#xff1a;Vue 3响应式系统的核心 1. 什么是 reactiveEffect&#xff1f;2. 核心机制2.1 依赖收集&#xff08;Track&#xff09;2.2 触发更新&#xff08;Trigger&#xff09;2.3 效果范围&#xff08;effectScope&#xff09; 3. 源码解析 —— track3.1 …...

K8S的mountPath和subPath

1 mountPath mountPath是容器内部文件系统的挂载点&#xff0c;它定义了容器内部将外部存储卷&#xff08;如 PersistentVolume、ConfigMap、Secret 等&#xff09;挂载到哪个路径下。通过 mountPath&#xff0c;容器可以访问这些挂载的数据或配置。 2 subPath subPath 是 m…...

notepad++里安装32位和64位的16进制编辑器Hex-Editor

这个16进制编辑器确实是个好东西&#xff0c;平时工作种会经常用到&#xff0c; 这是hex-editor的官网。这个里边只能下载32位的(64位的看最下边)&#xff0c;选一个合适的版本&#xff0c;我当时选的是最新的版本 https://sourceforge.net/projects/npp-plugins/files/Hex%20E…...

Python类的基本结构

当我们在Python中定义类时&#xff0c;我们实际上是在创建一种新的数据类型。类允许我们定义对象的属性和方法&#xff0c;从而构建更复杂的程序。让我们深入探讨一下关于类的一些重要概念。 定义类&#xff1a;基本结构 一个类的基本结构包括以下部分&#xff1a; 类名&…...

利用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.创建模型数据类&#xff08;M&#xff09; /// <summary>/// 数据模型/// </summary>public class LoginDataModel{// 用户名private string _userName;public string UserName{get { return _userName; }set{_userName value;}}// 密码private string _passWor…...

常见手撕项目C++

常见手撕项目C 设计模式单例模式饿汉模式懒汉模式 策略模式策略接口实现具体的策略&#xff08;虚函数重写&#xff09;定义上下文用户调用 设计模式 单例模式 单例模式是一种常用的软件设计模式&#xff0c;其目的是确保一个类只有一个实例&#xff0c;并提供一个全局访问点来…...

创建一个批处理作业来处理大量数据,例如从数据库中读取数据并进行处理

创建一个批处理作业来处理大量数据&#xff0c;例如从数据库中读取数据并进行处理 要创建一个批处理作业来处理大量数据&#xff0c;您可以使用Spring Batch。Spring Batch是一个用于大规模批处理的框架&#xff0c;它提供了丰富的功能来处理复杂的批处理任务&#xff0c;如读…...

LeetCode 2.两数相加

给你两个 非空 的链表&#xff0c;表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的&#xff0c;并且每个节点只能存储 一位 数字。 请你将两个数相加&#xff0c;并以相同形式返回一个表示和的链表。 你可以假设除了数字 0 之外&#xff0c;这两个数都不会以 0 …...

如何利用ChatGPT提升学术论文写作效率

ChatGPT无限次数:点击直达 如何利用ChatGPT提升学术论文写作效率 ChatGPT 是一种基于大规模预训练模型的自然语言处理工具&#xff0c;可以在各种文本生成任务中发挥作用&#xff0c;包括学术论文写作。利用ChatGPT&#xff0c;可以提高学术论文写作的速度和质量&#xff0c;帮…...

LLMs之Mistral:Mistral 7B v0.2的简介、安装和使用方法、案例应用之详细攻略

LLMs之Mistral&#xff1a;Mistral 7B v0.2的简介、安装和使用方法、案例应用之详细攻略 导读&#xff1a;Mistral AI首个7B模型发布于2023年9月&#xff0c;在基准测试中超越Llama 2 13B&#xff0c;一下子声名大振。Mistral 7B v0.2对应的指令调优版本Mistral-7B-Instruct-v0…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)

宇树机器人多姿态起立控制强化学习框架论文解析 论文解读&#xff1a;交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架&#xff08;一&#xff09; 论文解读&#xff1a;交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...

Android15默认授权浮窗权限

我们经常有那种需求&#xff0c;客户需要定制的apk集成在ROM中&#xff0c;并且默认授予其【显示在其他应用的上层】权限&#xff0c;也就是我们常说的浮窗权限&#xff0c;那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习

禁止商业或二改转载&#xff0c;仅供自学使用&#xff0c;侵权必究&#xff0c;如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...

USB Over IP专用硬件的5个特点

USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中&#xff0c;从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备&#xff08;如专用硬件设备&#xff09;&#xff0c;从而消除了直接物理连接的需要。USB over IP的…...

Android第十三次面试总结(四大 组件基础)

Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成&#xff0c;用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机&#xff1a; ​onCreate()​​ ​调用时机​&#xff1a;Activity 首次创建时调用。​…...

MySQL 知识小结(一)

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

【从零学习JVM|第三篇】类的生命周期(高频面试题)

前言&#xff1a; 在Java编程中&#xff0c;类的生命周期是指类从被加载到内存中开始&#xff0c;到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。本文会深入探寻类的生命周期&#xff0c;让读者对此有深刻印象。 目录 ​…...

4. TypeScript 类型推断与类型组合

一、类型推断 (一) 什么是类型推断 TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式&#xff0c;自动确定它们的类型。 这一特性减少了显式类型注解的需要&#xff0c;在保持类型安全的同时简化了代码。通过分析上下文和初始值&#xff0c;TypeSc…...

WebRTC调研

WebRTC是什么&#xff0c;为什么&#xff0c;如何使用 WebRTC有什么优势 WebRTC Architecture Amazon KVS WebRTC 其它厂商WebRTC 海康门禁WebRTC 海康门禁其他界面整理 威视通WebRTC 局域网 Google浏览器 Microsoft Edge 公网 RTSP RTMP NVR ONVIF SIP SRT WebRTC协…...

5. TypeScript 类型缩小

在 TypeScript 中&#xff0c;类型缩小&#xff08;Narrowing&#xff09;是指根据特定条件将变量的类型细化为更具体的过程。它帮助开发者编写更精确、更准确的代码&#xff0c;确保变量在运行时只以符合其类型的方式进行处理。 一、instanceof 缩小类型 TypeScript 中的 in…...