Qt实现拖拽功能(支持拖放文件、拖放操作)

news2024/9/27 5:45:26

目录

  • 拖放
  • Qt程序接受其他程序的拖拽
  • 部件/控件之间相互拖放
  • 总结


拖放

拖放是在一个应用程序内或者多个应用程序之间传递信息的一种直观的现代操作方式。除了为剪贴板提供支持外,通常它还提供数据移动和复制的功能。

拖放操作包括两个截然不同的动作:拖动和放下。Qt窗口部件可以作为拖动点(darg site)、放下点(drop site)或者同时作为拖动点和放下点。

Qt程序接受其他程序的拖拽

我们经常将文本文件推拽到notepate++等类型文本编辑器软件中。那么如何让Qt程序也能够支持这种操作呢?

我们需要在主窗口重新实现了来自父类的dragEnterEvent()dropEvent()函数。

protected:
    virtual void dragEnterEvent(QDragEnterEvent* event) override;
    virtual void dropEvent(QDropEvent* event) override;

在构造函数中,创建了一个QTextEdit并且把它设置为中央窗口部件。默认情况下,QTextEdit可以接受来自其他应用程序文本的拖动,并且如果用户在它上面放下一个文件,它将会把这个文件的内容填充到QTextEdit部件中。

由于拖放事件是从子窗口部件传递给父窗口部件的,所以通过禁用QTextEdit上的放下操作以及启用主窗口上的放下操作,就可以在整个MainWindow窗口中获得放下事件。

#include <QMimeData>
#include <QTextStream>
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    
 
    textEdit = new QTextEdit(this);
    setCentralWidget(textEdit);

    textEdit->setAcceptDrops(false);
    setAcceptDrops(true);

    setWindowTitle("Text Editor");
}

当用户把一个对象拖动到这个窗口部件上时,就会调用dragEnterEvent()。如果对这个事件调用acceptProposedAction(),就表明用户可以在这个窗口部件上拖放对象。默认情况下,窗口部件是不接受拖动的。Qt会自动改变光标来向用户说明这个窗口部件是不是有效的放下点

这里,我们希望用户拖动的只能是文件,而非其他类型的东西。为了实现这一点,我们可以检查拖动的MIME类型。

MIME类型中的 text/uri-list 用于存储一系列的统一资源标识符(Universal Re-source Identifier ,URI),它们可以是文件名、统―资源定位器(Uniform Resource Locator , URL,如 HTTP或者FTP路径),或者其他全局资源标识符。标准的MIME类型是由国际因特网地址分配委员会(Internet Assigned Numbers Authority , IANA)定义的,它们由类型、子类型信息以及分隔两者的斜线组·成。MME类通常由剪贴板和拖放系统使用,以识别不同类型的数据。可以从下面的网站得到正式的 MIME类型列表: http://www.iana.org/assignments/media-types/

void MainWindow::dragEnterEvent(QDragEnterEvent *event)
{
    if(event->mimeData()->hasFormat("text/uri-list"))
    {
        event->acceptProposedAction();
    }
}

当用户在窗口部件上放下一个对象时,就会调用dropEvent()。.我们调用函数QMimeData::urls()来获得QUrl列表。通常,用户一次只拖动一个文件,但是通过拖动一个选择区域来同时拖动多个文件也是可能的。如果要拖放的URL不止一个,或者要拖放的URL,不是一个本地文件名,则会立即返回到原调用处。
QWidget也提供 dragMoveEvent()和 dragLeaveEvent()函数,但是在绝大多数应用程序中并不需要重新实现它们。

void MainWindow::dropEvent(QDropEvent *event)
{
    QList<QUrl> urls = event->mimeData()->urls();
    if(urls.empty())
        return;

    QString fileName = urls.first().toLocalFile();
    if(fileName.isEmpty())
        return;

    if(ReadFile(fileName))
    {
        setWindowTitle(QString("%1-%2").arg(fileName).arg("Drag File"));
    }
}
bool MainWindow::ReadFile(const QString &filename)
{
    QFile file(filename);
    file.open(QIODevice::ReadOnly | QIODevice::Text | QIODevice::Truncate);
    if(false == file.isOpen())
    {
        return false;
    }
    QTextStream stream(&file);
    textEdit->insertPlainText(stream.readAll());
    file.flush();
    file.close();
    return true;
}

看看效果吧
在这里插入图片描述

部件/控件之间相互拖放

我们将实现类似于一个下图的效果,但不需要向左向右的按键,通过拖拽目标实现移动。
在这里插入图片描述

思路:创建一个支持拖拽的QListWidget子类ProjectListWidget,并且作为该界面的一个部件。

在ProjectListWidget类中需要重写父类的五个事件,和一个私有方法以及一个坐标记录

protected:
    virtual void mousePressEvent(QMouseEvent* event) override;
    virtual void mouseMoveEvent(QMouseEvent* event) override;
    virtual void dragEnterEvent(QDragEnterEvent* event) override;
    virtual void dragMoveEvent(QDragMoveEvent* event) override;
    virtual void dropEvent(QDropEvent* event) override;
private:
    void performDrag();

private:
    QPoint startPos;

在构造函数中,我们使列表框上的放下生效。

#include <QApplication>
#include <QDrag>
#include <QMimeData>
ProjectListWidget::ProjectListWidget(QWidget* parent)
    :QListWidget{parent}
{
    setAcceptDrops(true);
}

当用户按下鼠标左键,就把鼠标位置保存到statPos私有变量中。然后我们正常调用QListWidget 中mousePressEvent()

void ProjectListWidget::mousePressEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton)
    {
        startPos = event->pos();
    }
    QListWidget::mousePressEvent(event);
}

当用户按住鼠标左键并移动鼠标光标时,就认为这是一个拖动的开始。我们计算当前鼠标位置和原来鼠标左键按下的点之间的距离-—这个“曼哈顿长度”(Manhattan Length)其实是从坐标原点到该矢量长度快速计算的近似值。如果这个距离大于或等于QApplication推荐的拖动起始距离值(通常是4个像素),那么就调用私有函数performDrag()以启动拖动操作。这可以避免用户因为手握鼠标抖动而产生拖动。

void ProjectListWidget::mouseMoveEvent(QMouseEvent *event)
{
    if(event->buttons() & Qt::LeftButton)
    {
        if(int distance = (event->pos() - startPos).manhattanLength();
            distance >= QApplication::startDragDistance())
        {
            performDrag();
        }
    }
    QListWidget::mouseMoveEvent(event);
}

perfomDrag()中,创建了一个类型为QDrag的对象,并且把this作为它的父对象。这个QDrag对象将数据存储在QMimeData对象中。在这个实例中,我们利用QMineData::setText()提供了作为text/plain字符串的数据。QMimeData 提供了一些可用于处理最常用拖放类型(诸如图像、URL、颜色,等等)的函数,同时也可以处理任意由QByteArrays表宗的MiME类型。QDrag::setPiximap()调用则可以在拖放发生时使图标随光标移动。
QDrag::exec()调用启动并执行拖动操作;直到用户放下或取消此次拖动操作才会停止。它把所有支持的“拖放动作"(如 Qi: : CopyAction, Qt : : MoveAction和 Qt: : LinkAction)的组合作为其参数,并且返回被执行的拖放动作(如果没有执行任何动作,则返回Qt: IgnoreAction)。至于执行的是哪`个动作,取决于放下发生时源窗口部件是否允许、目标是否支持及按下了哪些组合键。在 exec()调用后,Qt拥有拖动对象的所有权并且可以在不需要它的时候删除它。

void ProjectListWidget::performDrag()
{
    if(QListWidgetItem* item = currentItem();
        nullptr != item)
    {
        QMimeData* mineData = new QMimeData();
        mineData->setText(item->text());

        QDrag* drag = new QDrag(this);
        drag->setMimeData(mineData);
        drag->setPixmap(QPixmap(":/icon.jpg"));

        if(drag->exec(Qt::MoveAction) == Qt::MoveAction)
        {
            delete item;
            item = nullptr;
        }
    }
}	

ProjectListWidget窗口部件不仅能发起拖动,还可以接收同一个应用程序中来自另外一个ProjectListWidget部件的拖动。如果窗口部件是同个应用程序的一部分,QDragEnterEvent::source()返回一个启动这个拖动的窗口部件的指针;否则,返回一个空指针值。

void ProjectListWidget::dragEnterEvent(QDragEnterEvent *event)
{
    ProjectListWidget* source = reinterpret_cast<ProjectListWidget*>(event->source());
    if(nullptr != source && source != this)
    {
        event->setDropAction(Qt::MoveAction);
        event->accept();
    }
}

dragMoveEvent()中的代码与dragEnterEvent()中编写的代码基本相同。因为需要重写QListWidget的函数实现(实际上是 QAbstractItemView的函数实现)。

void ProjectListWidget::dragMoveEvent(QDragMoveEvent *event)
{
    ProjectListWidget* source = reinterpret_cast<ProjectListWidget*>(event->source());
    if(nullptr != source && source != this)
    {
        event->setDropAction(Qt::MoveAction);
        event->accept();
    }
}

在dropEvent()中,我们使用QMimeData::text()重新找回拖动的文本并随文本创建一个拖动项。还需要将事件作为“移动动作”来接受,从而告诉源窗口部件现在可以删除原来的拖动项了。

void ProjectListWidget::dropEvent(QDropEvent *event)
{
    ProjectListWidget* source = reinterpret_cast<ProjectListWidget*>(event->source());
    if(nullptr != source && source != this)
    {
        addItem(event->mimeData()->text());
        event->setDropAction(Qt::MoveAction);
        event->accept();
    }
}

效果如下
在这里插入图片描述

总结

拖放是在应用程序之间传递数据的有力机制。但是在某些情况下;,有可能在执行拖放时并未使用Qt的拖放工具。如果只是想在一个应用程序的窗口部件中移动数据,通常只要重新实现mousePressEvent()和 mouseReleaseEvent()函数就可以了。

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

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

相关文章

津津乐道设计模式 - 享元模式详解(以影院座位举例让你快速掌握)

&#x1f604; 19年之后由于某些原因断更了三年&#xff0c;23年重新扬帆起航&#xff0c;推出更多优质博文&#xff0c;希望大家多多支持&#xff5e; &#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Mi…

itext 7批量生成pdf文件并以压缩包形式下载

itext 7批量生成pdf文件并以压缩包形式下载 引入jar <dependency><groupId>com.itextpdf</groupId><artifactId>itext7-core</artifactId><version>7.0.3</version><type>pom</type></dependency>代码实现–生成…

逻辑漏洞小结之SRC篇(值得收藏,反复看!)

​​​​​最近在挖各大src&#xff0c;主要以逻辑漏洞为主&#xff0c;想着总结一下我所知道的一些逻辑漏洞分享一下以及举部分实际的案例展示一下&#xff0c;方便大家理解。 主要从两个方面看&#xff0c;业务方面与漏洞方面。&#xff08;接下来就从拿到网站的挖掘步骤进行…

如何使用模板化消息进行客户服务?(参考salesmartly)

如何使用模板化消息进行客户服务&#xff1f;&#xff08;参考salesmartly&#xff09; 一整天一遍又一遍地重复相同的答案可能会很乏味&#xff0c;尤其是对于您的客户服务团队而言。模板化消息&#xff0c;也称为预制回复或回复模板&#xff0c;已成为许多客户服务团队必备功…

Kong(Without DB)的安装和基本使用

下载和安装 Docs 这里以Centos为例 sudo yum install kong-enterprise-edition-3.3.0.0.rpm配置 ​ Kong的官网提供了两个配置模式一个是 Using a database 另一个是使用 yaml配置文件的形式&#xff0c;安装好后默认配置文件默认是/etc/kong/kong.conf.default 二者对比 …

React V6实现类似与vue的eventBus

功能背景 想要实现类似于vue的eventBus的功能&#xff0c;由一个组件通知其他一个或多个组件。应用场景&#xff1a;比如一个可视化大屏的界面&#xff0c;当筛选条件变化的时候&#xff0c;要同时通知到大屏中所有图表一起变化。&#xff08;当然使用store也是可以的&#xff…

逻辑回归算法实现

目录 1.关于逻辑回归的原理解析和准备工作 2.关于激活函数 3.关于数据集 4.编写LogisticsRegression类 5.逻辑回归测试 6.结果 1.关于逻辑回归的原理解析和准备工作 逻辑回归原理相关内容&#xff0c;请参考博主的另一篇文章&#xff1a;机器学习&#xff08;二&#xff…

菜鸟重磅推出多款科技新品,“工业大脑”PLC国产化获突破

“决策参谋”供应链计划、“工业大脑”PLC、“智能制造”科技解决方案……6月28日&#xff0c;在2023全球智慧物流峰会上&#xff0c;菜鸟自研的一批新产品、新方案正式曝光。菜鸟物流科技深耕制造业的成绩单也在峰会期间公布&#xff0c;华晨宝马等一批头部汽车企业已与其展开…

六种提高自己工作效率的方法!

为什么同样的时间&#xff0c;同样的都在休息都在玩&#xff0c;而别人工作却在玩的同时已经完成了一大半了。究竟是怎么做到的呢&#xff1f; 不仅仅是因为别人的工作效率高&#xff0c;而是因为他们会巧用工具。 那么你肯定想知道&#xff0c;这款工具是什么样的呢&#xf…

hutool工具类实现excel上传 支持03和07

一直感觉excel表的导入有很多代码&#xff0c;写一次忘一次&#xff0c;类太多&#xff0c;要知道怎么获取Workbook、Sheet、Cell、row等等&#xff0c;这么多类不可能一直记的住&#xff0c;都是写过之后保存&#xff0c;使用的时候拿出来改改&#xff0c;更烦人的是针对offic…

Vue Router replace 编程式导航 缓存路由组件

6.9.路由跳转的 replace 方法 作用&#xff1a;控制路由跳转时操作浏览器历史记录的模式浏览器的历史记录有两种写入方式&#xff1a;push和replace push是追加历史记录replace是替换当前记录&#xff0c;路由跳转时候默认为push方式 开启replace模式 <router-link :replac…

松翰单片机keil环境芯片包

松翰单片机keil环境芯片包&#xff08;SN8F5700系列&#xff09;&#xff1a;安装时与Keil安装位置相同可以直接使用。 安装后依次点击可查看芯片包具体型号&#xff1a; 芯片包下载链接&#xff1a;阿里云盘分享https://www.aliyundrive.com/s/TnHchMhYeh1

Baumer工业相机堡盟工业相机如何通过BGAPISDK进行定序器编程:根据每次触发信号移动感兴趣区域(C++)

Baumer工业相机堡盟工业相机如何通过BGAPISDK进行定序器编程:根据每次触发信号移动感兴趣区域&#xff08;C&#xff09; Baumer工业相机Baumer工业相机BGAPISDK和定序器编程的技术背景Baumer工业相机通过BGAPISDK进行定序器编程功能1.引用合适的类文件2.Baumer工业相机通过BGA…

laravel+vue共用一个域名,使用目录区分接口和项目的nginx配置

1、打包好的项目&#xff1a; 首先将打包好的项目放置public下&#xff0c;如下图 2、nginx配置文件 不带注释的伪静态&#xff08;推荐&#xff09; 备注&#xff1a;若在 location /admin 中的 admin 后面不加 “斜杠/”&#xff0c;则会出现访问 /admin-user 路由&#x…

Oracle Net Services 配置:LISTENER:没有为主机 VM-16-7-centos 返回有效的 IP 地址

问题描述&#xff1a; [rootVM-16-7-centos oracle]# /etc/init.d/oracledb_ORCLCDB-21c configure Configuring Oracle Database ORCLCDB. 准备执行数据库操作 已完成 8% 复制数据库文件 已完成 31% 已完成 100% [FATAL] 正在对命令行参数进行语法分析: 参数"silent&quo…

CLIP论文详细解析

论文链接&#xff1a;Learning Transferable Visual Models From Natural Language Supervision&#xff08;通过自然语言处理的监督信号&#xff0c;学习可迁移的视觉模型&#xff09;. 代码链接&#xff1a;CLIP. CLIP 摘要1.引言2.方法3.实验4.与人对比实验5.数据集去重6.L…

汽车远程启动程序APP的设计与实现(源码+文档+报告+任务书)

以 CAN (Controller Local Network&#xff0c;简称 CAN&#xff09;为基础的车辆遥控起动技术&#xff0c;通过将车辆的 PBD接口与车辆的 CAN总线相连&#xff0c;并与相应的控制系统相连&#xff0c;实现对车辆遥控启动。 此系统主要使用了Java、Android Studio、 MySQL数据…

Visual studio(VS)运行障碍指北

文章目录 VS: ....Microsoft.CppCommon.targets: error MSB6006: “CL.exe”已退出-VS2017许可证过期VS下Visual Assist X番茄插件安装失败子工程引用&#xff08;无法解析的外部符号&#xff09;无法打开.ui文件&#xff08;qt&#xff09;VS中qt子工程无法加载 VS: …Microso…

云原生应用交付平台 Orbit 主要功能与核心能力

GitOps GitOps 于 2017 年首创&#xff0c;是一种管理由 Kubernetes 提供支持的云原生系统的现代方式。它利用策略即代码方法来定义和管理现代应用程序堆栈的每一层——基础设施、网络、应用程序代码和 GitOps 管道本身。Orbit 基于 GitOps 方法理念提供以下能力&#xff1a; …

Revit中用楼板创建散水和批量成板

​一、Revit中用楼板创建散水 在Revit中用楼板来创建散水&#xff0c;散水&#xff1a;散水是指房屋外墙四周的勒脚处(室外地坪上)用片石砌筑或用混凝土浇筑的有一定坡度的散水坡。散水的作用是迅速排走勒脚附近的雨水&#xff0c;避免雨水冲刷或渗透到地基&#xff0c;防止基础…