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

Qt学习之路(54): 自定义拖放数据对象

版权声明:原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://devbean.blog.51cto.com/448512/288742
前面的例子都是使用的系统提供的拖放对象 QMimeData 进行拖放数据的存储,比如使用 QMimeData::setText() 创建文本,使用 QMimeData::urls() 创建 URL 对象。但是,如果你希望使用一些自定义的对象作为拖放数据,比如自定义类等等,单纯使用 QMimeData 可能就没有那么容易了。为了实现这种操作,我们可以从下面三种实现方式中选择一个:

1. 将自定义数据作为 QByteArray 对象,使用 QMimeData::setData() 函数作为二进制数据存储到 QMimeData 中,然后使用 QMimeData::Data() 读取;
2. 继承 QMimeData,重写其中的 formats() 和 retrieveData() 函数操作自定义数据;
3. 如果拖放操作仅仅发生在同一个应用程序,可以直接继承 QMimeData,然后使用任意合适的数据结构进行存储。

第一种方法不需要继承任何类,但是有一些局限:即是拖放不会发生,我们也必须将自定义的数据对象转换成 QByteArray 对象;如果你希望支持很多种拖放的数据,那么每种类型的数据都必须使用一个 QMimeData 类,这可能会导致类爆炸;如果数据很大的话,这种方式可能会降低系统的可维护性。然而,后两种实现方式就不会有这些问题,或者说是能够减小这种问题,并且能够让我们有完全控制权。
我们先来看一个应用,使用 QTableWidget 来进行拖放操作,拖放的类型包括 plain/text,plain/html 和 plain/csv。如果使用第一种实现方法,我们的代码将会如下所示:

1. void MyTableWidget::mouseMoveEvent(QMouseEvent *event)
2. {
3. if (event->buttons() & Qt::LeftButton) {
4. int distance = (event->pos() - startPos).manhattanLength();
5. if (distance >= QApplication::startDragDistance())
6. performDrag();
7. }
8. QTableWidget::mouseMoveEvent(event);
9. }
10.
11. void MyTableWidget::performDrag()
12. {
13. QString plainText = selectionAsPlainText();
14. if (plainText.isEmpty())
15. return;
16.
17. QMimeData *mimeData = new QMimeData;
18. mimeData->setText(plainText);
19. mimeData->setHtml(toHtml(plainText));
20. mimeData->setData("text/csv", toCsv(plainText).toUtf8());
21.
22. QDrag *drag = new QDrag(this);
23. drag->setMimeData(mimeData);
24. if (drag->exec(Qt::CopyAction | Qt::MoveAction) == Qt::MoveAction)
25. deleteSelection();
26. }

对于这段代码,我们应该已经很容易的理解:在 performDrag() 函数中,我们调用 QMimeData 的 setText() 和 setHTML() 函数存储 plain/text 和 plain/html 数据,使用 setData() 将 text/csv 类型的数据作为二进制 QByteArray 类型存储。

1. QString MyTableWidget::toCsv(const QString &plainText)
2. {
3. QString result = plainText;
4. result.replace("\\", "\\\\");
5. result.replace("\"", "\\\"");
6. result.replace("\t", "\", \"");
7. result.replace("\n", "\"\n\"");
8. result.prepend("\"");
9. result.append("\"");
10. return result;
11. }
12.
13. QString MyTableWidget::toHtml(const QString &plainText)
14. {
15. QString result = Qt::escape(plainText);
16. result.replace("\t", "<td>");
17. result.replace("\n", "\n<tr><td>");
18. result.prepend("<table>\n<tr><td>");
19. result.append("\n</table>");
20. return result;
21. }

toCsv() 和 toHtml() 函数将数据取出并转换成我们需要的 csv 和 html类型的数据。例如,下面的数据

Red Green Blue
Cyan Yellow Magenta

转换成 csv 格式为:

"Red", "Green", "Blue"
"Cyan", "Yellow", "Magenta"

转换成 html 格式为:
<table>
<tr><td>Red<td>Green<td>Blue
<tr><td>Cyan<td>Yellow<td>Magenta
</table>
在放置的函数中我们像以前一样使用:

1. void MyTableWidget::dropEvent(QDropEvent *event)
2. {
3. if (event->mimeData()->hasFormat("text/csv")) {
4. QByteArray csvData = event->mimeData()->data("text/csv");
5. QString csvText = QString::fromUtf8(csvData);
6. // ...
7. event->acceptProposedAction();
8. } else if (event->mimeData()->hasFormat("text/plain")) {
9. QString plainText = event->mimeData()->text();
10. // ...
11. event->acceptProposedAction();
12. }
13. }

虽然我们接受三种数据类型,但是在这个函数中我们只接受两种类型。至于 html 类型,我们希望如果用户将 QTableWidget 的数据拖到一个 HTML 编辑器,那么它就会自动转换成 html 代码,但是我们不计划支持将外部的 html 代码拖放到 QTableWidget 上。为了让这段代码能够工作,我们需要在构造函数中设置 setAcceptDrops(true) 和 setSelectionMode(ContiguousSelection)。
好了,上面就是我们所说的第一种方式的实现。这里并没有给出完整的实现代码,大家可以根据需要自己实现一下试试。下面我们将按照第二种方法重新实现这个需求。

1. class TableMimeData : public QMimeData
2. {
3. Q_OBJECT
4.
5. public:
6. TableMimeData(const QTableWidget *tableWidget,
7. const QTableWidgetSelectionRange &range);
8.
9. const QTableWidget *tableWidget() const { return myTableWidget; }
10. QTableWidgetSelectionRange range() const { return myRange; }
11. QStringList formats() const;
12.
13. protected:
14. QVariant retrieveData(const QString &format,
15. QVariant::Type preferredType) const;
16.
17. private:
18. static QString toHtml(const QString &plainText);
19. static QString toCsv(const QString &plainText);
20.
21. QString text(int row, int column) const;
22. QString rangeAsPlainText() const;
23.
24. const QTableWidget *myTableWidget;
25. QTableWidgetSelectionRange myRange;
26. QStringList myFormats;
27. };

为了避免存储具体的数据,我们存储 table 和选择区域的坐标的指针。

1. TableMimeData::TableMimeData(const QTableWidget *tableWidget,
2. const QTableWidgetSelectionRange &range)
3. {
4. myTableWidget = tableWidget;
5. myRange = range;
6. myFormats << "text/csv" << "text/html" << "text/plain";
7. }
8.
9. QStringList TableMimeData::formats() const
10. {
11. return myFormats;
12. }

构造函数中,我们对私有变量进行初始化。formats() 函数返回的是被 MIME 数据对象支持的数据类型列表。这个列表是没有先后顺序的,但是最佳实践是将“最适合”的类型放在第一位。对于支持多种类型的应用程序而言,有时候会直接选用第一个符合的类型存储。

1. QVariant TableMimeData::retrieveData(const QString &format,
2. QVariant::Type preferredType) const
3. {
4. if (format == "text/plain") {
5. return rangeAsPlainText();
6. } else if (format == "text/csv") {
7. return toCsv(rangeAsPlainText());
8. } else if (format == "text/html") {
9. return toHtml(rangeAsPlainText());
10. } else {
11. return QMimeData::retrieveData(format, preferredType);
12. }
13. }


函数 retrieveData() 将给出的 MIME 类型作为 QVariant 返回。参数 format 的值通常是 formats() 函数返回值之一,但是我们并不能假定一定是这个值之一,因为并不是所有的应用程序都会通过 formats() 函数检查 MIME 类型。一些返回函数,比如 text(), html(), urls(), imageData(), colorData() 和 data() 实际上都是在 QMimeData 的 retrieveData() 函数中实现的。第二个参数 preferredType 给出我们应该在 QVariant 中存储哪种类型的数据。在这里,我们简单的将其忽略了,并且在 else 语句中,我们假定 QMimeData 会自动将其转换成所需要的类型。

1. void MyTableWidget::dropEvent(QDropEvent *event)
2. {
3. const TableMimeData *tableData =
4. qobject_cast<const TableMimeData *>(event->mimeData());
5.
6. if (tableData) {
7. const QTableWidget *otherTable = tableData->tableWidget();
8. QTableWidgetSelectionRange otherRange = tableData->range();
9. // ...
10. event->acceptProposedAction();
11. } else if (event->mimeData()->hasFormat("text/csv")) {
12. QByteArray csvData = event->mimeData()->data("text/csv");
13. QString csvText = QString::fromUtf8(csvData);
14. // ...
15. event->acceptProposedAction();
16. } else if (event->mimeData()->hasFormat("text/plain")) {
17. QString plainText = event->mimeData()->text();
18. // ...
19. event->acceptProposedAction();
20. }
21. QTableWidget::mouseMoveEvent(event);
22. }

在放置的函数中,我们需要按照我们自己定义的数据类型进行选择。我们使用 qobject_cast 宏进行类型转换。如果成功,说明数据来自同一应用程序,因此我们直接设置 QTableWidget 相关 数据,如果转换失败,我们则使用一般的处理方式。

本文出自 “豆子空间” 博客,请务必保留此出处http://devbean.blog.51cto.com/448512/288742

相关文章:

  • 创建 Silverlight 应用程序以访问 SharePoint 2010 数据
  • Qt学习之路(53): 拖放技术之二
  • OPPM 一页纸项目管理 One-Page Project Management
  • Qt学习之路(52): 拖放技术之一
  • C# 图片识别(支持21种语言)【转】
  • Qt学习之路(tip): Qt容器和算法拾遗
  • 使用 vimdiff 來呈現 Git diff 差異
  • android 开发Eclipse 快捷键
  • Qt学习之路(51): QByteArray和QVariant
  • 我该如何奋斗?
  • Qt学习之路(50): QString
  • 原创:C语言贪吃蛇代码
  • Qt学习之路(49): 通用算法
  • 【转】解决:SecureCRT在Linux下vim显示utf-8编码的文件乱码
  • Qt学习之路(47): 自定义Model之三
  • [译] 怎样写一个基础的编译器
  • 【跃迁之路】【641天】程序员高效学习方法论探索系列(实验阶段398-2018.11.14)...
  • Android优雅地处理按钮重复点击
  • Asm.js的简单介绍
  • canvas 五子棋游戏
  • CSS进阶篇--用CSS开启硬件加速来提高网站性能
  • gcc介绍及安装
  • javascript 总结(常用工具类的封装)
  • JavaWeb(学习笔记二)
  • js中的正则表达式入门
  • Logstash 参考指南(目录)
  • node-sass 安装卡在 node scripts/install.js 解决办法
  • PAT A1017 优先队列
  • Spring声明式事务管理之一:五大属性分析
  • spring学习第二天
  • 闭包--闭包作用之保存(一)
  • 如何用vue打造一个移动端音乐播放器
  • 一些关于Rust在2019年的思考
  • 正则表达式小结
  • Java总结 - String - 这篇请使劲喷我
  • ​​​​​​​GitLab 之 GitLab-Runner 安装,配置与问题汇总
  • (6)【Python/机器学习/深度学习】Machine-Learning模型与算法应用—使用Adaboost建模及工作环境下的数据分析整理
  • (c语言)strcpy函数用法
  • (Matlab)遗传算法优化的BP神经网络实现回归预测
  • (附源码)计算机毕业设计ssm-Java网名推荐系统
  • (教学思路 C#之类三)方法参数类型(ref、out、parmas)
  • (三)Honghu Cloud云架构一定时调度平台
  • (三)uboot源码分析
  • (十)【Jmeter】线程(Threads(Users))之jp@gc - Stepping Thread Group (deprecated)
  • (转)Spring4.2.5+Hibernate4.3.11+Struts1.3.8集成方案一
  • (转)关于如何学好游戏3D引擎编程的一些经验
  • (转)母版页和相对路径
  • (转载)跟我一起学习VIM - The Life Changing Editor
  • **PHP分步表单提交思路(分页表单提交)
  • .NET 设计一套高性能的弱事件机制
  • .NET 使用 ILMerge 合并多个程序集,避免引入额外的依赖
  • .Net中的集合
  • .Net组件程序设计之线程、并发管理(一)
  • .py文件应该怎样打开?
  • /var/lib/dpkg/lock 锁定问题