Qt-拖放

news2024/9/25 0:28:13

概述

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

本文档描述了基本的拖放机制,并概述了在自定义控件中启用它的方法。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.coloradmin.cn/o/2161893.html

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

相关文章

Python 字符串的常见方法

Python 字符串的常见方法 字符串是 Python 中非常重要的数据类型之一。在日常编程中&#xff0c;我们经常需要对字符串进行各种操作&#xff0c;比如分割、连接、替换等。Python 提供了丰富的字符串方法&#xff0c;使得这些操作变得简单而高效。本文将详细介绍一些常见的字符…

【Docker】Docker快速入门

Docker学习笔记 一、Docker概述 为什么会出现Docker? 安卓开发流程&#xff1a;apk(java开发的)发布到应用商店&#xff0c;用户安装apk即可使用。 后端开发流程&#xff1a; jar(java开发的)带上环境发布到Docker仓库&#xff0c;用户从Docker仓库拉取镜像并部署。 总结…

关于Python升级以后脚本不能运行的问题

近日将Python从3.11升级到了3.12&#xff0c;然后把几个包例如numpy等也通过pip给upgrade了一下&#xff0c;结果原来运行的好好的脚本&#xff0c;都运行不了了&#xff0c;还出现各种报错。怀疑是自己升级了环境导致的&#xff0c;因此通过搜索引擎检索了一下&#xff0c;有这…

【React】(推荐项目)使用 React、Socket.io、Nodejs、Redux-Toolkit、MongoDB 构建聊天应用程序 (2024)

使用 React、Socket.io、Nodejs、Redux-Toolkit、MongoDB 构建聊天应用程序 (2024) 学习使用 React、Socket.io、Node.js、Redux-Toolkit 和 MongoDB 构建响应式实时消息聊天应用程序。这个项目涵盖了从设置到实施的所有内容&#xff0c;提供了宝贵的见解和实用技能。无论您是…

地平线占用预测 FlashOcc 参考算法-V1.0

1.简介 3D Occupancy Networks 的基本思路是将三维空间划分成体素网格&#xff0c;并对每个网格进行各类感知任务的预测。目前以网格为中心的方法能够预测每个网格单元的占用率、语义类别、未来运动位移和实例信息。3D occupancy 可以对道路障碍物进行更细粒度的划分&#xff…

【Docker】解决Docker Engine stopped

解决Docker Engine stopped 解决Docker Engine stopped1.检查虚拟设置2 安装wslwindows安装wsl 解决Docker Engine stopped 在安装完docker之后不少用户会遇到Docker Engine stopped。下面就下给出解决方法让docker正常运行起来 1.检查虚拟设置 打开任务管理器查看cpu页面&a…

vue-入门速通

setup是最早的生命周期&#xff0c;在vue2里边的data域可以使用this调用setup里面的数据&#xff0c;但是在setup里边不能使用thisvue项目的可执行文件是index&#xff0c;另外运行前端需要npm run vue的三个模块内需要三个不同的结构&#xff0c;里边放置js代码&#xff0c;注…

LED驱动电路

LED驱动电路简介 摘要&#xff1a; LED照明是今年来快速兴起发展的一种新型光源&#xff0c;它的许多良好特点使得它的应用面越来越广。LED的单向导电特性使人一般认为应该用直流驱动&#xff0c;但是对直流恒压和限流的装置在保证比较好的限流特性时&#xff0c;自身功耗是很…

【MySQL 04】数据类型

目录 1.数据类型分类 2.数值类型 2.1 tinyint 类型 2.2 bit类型 2.3 float类型 2.4decimal 3.字符串类型 3.1 char类型 3.2 varchar类型 4.日期和时间类型 6. enum和set类型 6.1.enum和set类型简介&#xff1a; 6.2.enum和set的一般使用方法 6.3.用数字的方式…

Excel的基本应用__1

1. 模拟运算 1.1 单变量求解 1.1.1 步骤 1.1.1 效果 1.2 模拟运算表 1.2.1 步骤 1.2.2 效果 2.选择性粘贴--转至 3. Excel中如何使用和定义名称 使用 相当于全局变量&#xff0c;可以在不同表中调用 3.1名称中使用常量 3.2名称中使用函数 调用 可以在不同的表中调用 3.…

[vulnhub] SickOS1.1

https://www.vulnhub.com/entry/sickos-11,132/ 主机发现端口扫描 探测存活主机&#xff0c;136是靶机&#xff0c;因为靶机是我最后添加的 nmap -sP 192.168.75.0/24 // Starting Nmap 7.93 ( https://nmap.org ) at 2024-09-22 11:36 CST Nmap scan report for 192.168.75.1 …

vue-animate-onscroll动画库(可来回触发动画)

效果展示 ①触发一次动画 触发一次 ②触发多次动画 触发多次 1.什么是vue-animate-onscroll 它是一个 Vue 插件&#xff0c;用于在滚动时触发动画效果。它可以帮助开发者在用户滚动页面时&#xff0c;逐渐展示元素&#xff0c;增强用户体验。基本用法是通过在元素上添加特定的指…

IR21364的代替品SLM21364CF-DG:带过流保护、使能和故障反馈 高压三相半桥驱动芯片 神秘芯片背后的惊人力量

SLM21364CF-DG是一款高压、高速的三相功率MOSFET和IGBT驱动器。采用专有的高压集成电路和锁存免疫CMOS技术&#xff0c;提供可靠的单芯片驱动方案。逻辑输入电平与标准CMOS或LSTTL输出兼容&#xff0c;最低支持3.3V逻辑。通过检测外部电流电阻上电流&#xff0c;过流保护功能能…

nginx如何拦截未经授权的跳转

nginx如何拦截未经授权的跳转 背景准备好一个网站准备好引用网站配置nginx拦截效果 背景 在现实工作中往往有一些企业或人未取得授权但是转载或挂载我们的网址。那么有些要求严格或者有其他原因的情况下不希望这些链接正常访问。所以就有了这样的需求。前提是咱们的网站什么的是…

【深度学习】批量规范化

训练深层神经网络是十分困难的&#xff0c;光是之前简单的模型在简单的数据集上训练都不太轻松。 而批量规范化&#xff08;batch normalization&#xff09;是一种流行且有效的技术&#xff0c;可以帮助加快深层网络的收敛速度。 一、训练深层网络 我们回顾一下训练神经网络…

【第十三章:Sentosa_DSML社区版-机器学习之聚类】

目录 13.1 KMeans聚类 13.2 二分KMeans聚类 13.3 高斯混合聚类 13.4 模糊C均值聚类 13.5 Canopy聚类 13.6 Canopy-KMeans聚类 13.7 文档主题生成模型聚类 13.8 谱聚类 【第十三章&#xff1a;Sentosa_DSML社区版-机器学习之聚类】 13.1 KMeans聚类 1.算子介绍 KMeans…

【全新课程】正点原子《ESP32物联网项目实战》培训课程上线!

正点原子《ESP32物联网项目实战》全新培训课程上线啦&#xff01;正点原子工程师手把手教你学&#xff01;通过多个项目实战&#xff0c;掌握ESP32物联网项目的开发&#xff01; 一、课程介绍 本课程围绕物联网实战项目展开教学&#xff0c;内容循序渐进&#xff0c;涵盖了环…

后台管理系统开箱即用的组件库!!【送源码】

今天给大家推荐几款的后台管理系统开箱即用的组件库&#xff0c;基于ElementUI二次封装&#xff0c;开发必备 Headless UI Headless UI 是一款出色的前端组件库&#xff0c;专为与 Tailwind CSS 集成而设计。一组完全无样式、完全可访问的 UI 组件&#xff0c;可以自由的引入…

【Linux】nginx连接前端项目

文章目录 一、项目编译1.编译文件2.dist文件 二、Linux nginx配置三、启动nginx 一、项目编译 1.编译文件 2.dist文件 二、Linux nginx配置 在Xshell软件中&#xff0c;点击CtrlAltF进入文件传输找到地址&#xff1a;/usr/local/nginx/html将dist文件传入 找到nginx.conf&…

考研数据结构——C语言实现冒泡排序

冒泡排序是一种简单的排序算法&#xff0c;它重复地遍历要排序的列表&#xff0c;比较每对相邻元素&#xff0c;并在顺序错误的情况下交换它们。这个过程重复进行&#xff0c;直到没有需要交换的元素&#xff0c;这意味着列表已经排序完成。冒泡排序的名字来源于较小的元素会逐…