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

简介:
+关注继续查看
前面的例子都是使用的系统提供的拖放对象 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;  
构造函数中,我们对私有变量进行初始化。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.     }  
 
函数 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);  
在放置的函数中,我们需要按照我们自己定义的数据类型进行选择。我们使用 qobject_cast 宏进行类型转换。如果成功,说明数据来自同一应用程序,因此我们直接设置 QTableWidget 相关 数据,如果转换失败,我们则使用一般的处理方式。


本文转自 FinderCheng 51CTO博客,原文链接:
http://blog.51cto.com/devbean/288742
相关文章
|
9月前
|
C++
C/C++ Qt 信号自定义槽函数
Qt中实现自定义信号与槽函数,信号用于发送并触发槽函数,槽函数则是具体的功能实现,如下我们以老师学生为例子简单学习一下信号与槽函数的使用方法。
94 0
|
9月前
|
C++
C/C++ Qt 自定义Dialog对话框组件应用
在上一篇博文 `《C/C++ Qt 标准Dialog对话框组件应用》` 中我给大家演示了如何使用Qt中内置的标准对话框组件实现基本的数据输入功能。
262 0
C/C++ Qt 自定义Dialog对话框组件应用
|
11月前
|
搜索推荐 C++ 索引
C/C++ Qt TableDelegate 自定义代理组件
TableDelegate 自定义代理组件的主要作用是对原有表格进行调整,例如默认情况下Table中的缺省代理就是一个编辑框,我们只能够在编辑框内输入数据,而有时我们想选择数据而不是输入,此时就需要重写编辑框实现选择的效果,代理组件常用于个性化定制Table表格中的字段类型。
212 0
C/C++ Qt TableDelegate 自定义代理组件
|
API 计算机视觉
Qt实用技巧:自定义窗口标题栏
Qt实用技巧:自定义窗口标题栏
Qt实用技巧:自定义窗口标题栏
Qt隐藏系统标题栏,使用自定义标题栏
Qt隐藏系统标题栏,使用自定义标题栏
Qt隐藏系统标题栏,使用自定义标题栏
Qt实现自定义图标功能
在上一节的基础上进行定义图标的功能
124 0
Qt实现自定义图标功能
|
智能硬件
Qt-网易云音乐界面实现-7 消息中心实现,主要是QListWidget 自定义Item 和QTabwidget使用
最近写的有点烦躁, 感觉内容真的很多!很多!很多。 目前真的想知道网易官方在出这款产品是,用了多少人和多长时间。
295 0
Qt-网易云音乐界面实现-7 消息中心实现,主要是QListWidget 自定义Item 和QTabwidget使用
Qt-网易云音乐界面实现-1 窗口隐藏拖拽移动,自定义标题栏
最近也换了公司,也换了新的工作,工资也象征性的涨了一点点,但是最近心里还是慌慌,不知道为什么,没有那种踏实感,感觉自己随时可以被抛弃的感觉。感觉自己在荒废时间,也感觉自己在浪费生命。
179 0
Qt-网易云音乐界面实现-1 窗口隐藏拖拽移动,自定义标题栏
|
搜索推荐 C++
Qt 个性化标题栏,自定义标题栏
目前还没有达到自己满意的地步,魔方别人写的的,先提供参考,后面在加入新的东西
175 0
Qt 个性化标题栏,自定义标题栏
Qt listwigwt item 加入自定义元素
Qt listwigwt item 加入自定义元素
108 0
相关实验场景
更多
推荐文章
更多
推荐镜像
更多