1 前言
Qt 的拖放数据是指在一个应用程序中拖动一个对象并将其放置到同一个应用程序或另一个应用程序中的目标部件上。一般情况下,拖放可分为四个阶段:拖起(dragging)、进入(entering)、移动(moving)和释放(releasing),涉及到的类主要有 QDrag、QMimeData 和 QWidget。在 Qt 中,支持多种方法实现数据的拖放操作,其中最常用的方式是自定义 MIME 类型以及通过实现鼠标事件函数进行控制。
2 效果
3 使用setData()函数
- 在mousePressEvent函数里准备并设置数据(也可以在mouseMoveEvent里)
void DragDropButton::mousePressEvent(QMouseEvent *event)
{
Q_UNUSED(event)
QDrag *drag = new QDrag(this);
QMimeData *mimeData = new QMimeData;
QIcon icon("../ColorBridge.jpg");
QString text("CCC");
QByteArray data;
// 使用数据流把数据 icon 和 text 写入 data 中
QDataStream in(&data, QIODevice::WriteOnly);
in << icon << text;
// 设置自定义的 MIME 类型数据,其中"MyData"为自定义的 MIME 类型
mimeData->setData("MyData", data);
drag->setMimeData(mimeData);
drag->exec(Qt::CopyAction); // 启动拖放操作
}
- 指示目标部件可以接受操作类型
void DragDropButton::dragEnterEvent(QDragEnterEvent *event)
{
// 指示目标部件可以接受操作类型,并将该操作类型设置为建议的操作类型。
event->acceptProposedAction();
/* 注:目标部件仅能在 dragEnterEvent() 和 dragMoveEvent() 函数中对建议操作类型进行决策,
* 并且必须在 dropEvent() 函数中执行实际的数据操作。
*/
}
- 执行拖放后数据操作
void DragDropButton::dropEvent(QDropEvent *event)
{
const QMimeData *mimeData = event->mimeData();
// 判断是否是有自定义 MIME 类型
if (mimeData->hasFormat("MyData")) {
// 读取自定义 MIME 类型 MyData 的数据,并保存在 data 中
QByteArray data = mimeData->data("MyData");
QIcon icon;
QString text;
QDataStream out(&data, QIODevice::ReadOnly);
out >> icon >> text;
this->setIcon(icon);
this->setText(text);
}
event->accept();
}
4 子类化QMimeData
1、MIME类型数据存储
MIME 类型不属于 Qt 类型或 C++类型,与这些类型关联的数据需要被存储,为此需要指定一个存储MIME 类型的 Qt 类型(或 C++类型),并使用一个对象来存储该类型的数据,比如对于 MIME 类型XXX,与 XXX 关联的数据为两个字符串"AAA"和"BBB",那么"AAA"和"BBB"需要被存储在应用程序中,比如可以使用 QByteArray 类型的对象来存储它们(也可使用 QList 或其他类来存储),那么与MIME 类型相关联的类型是 QByteArray 类型。
2、QMimeData 的本质
QMimeData 的本质就是用于把需要用于传输的数据保存在该类的对象中,因此最小的子类化MimeData 类只需在该类中定义一些保存数据的成员变量就可以了。并不需要重新实现 hasFormat()、formats()、retrieveData()等虚函数。
- 子类化QMimeData
class MyMimeData : public QMimeData
{
Q_OBJECT
public:
QIcon icon;
QString text;
};
- mousePressEvent函数
m_mimeData->icon = QIcon("../ColorBridge.jpg");
m_mimeData->text = QString("CCC");
drag->setMimeData(m_mimeData);
drag->exec(Qt::CopyAction); // 启动拖放操作
- dropEvent函数
const MyMimeData *mimeData = dynamic_cast<const MyMimeData*>(event->mimeData());
this->setIcon(mimeData->icon);
this->setText(mimeData->text);
5 重新实现QMimeData类虚函数
说明:
QMimeData 类中的虚函数 hasFormat()、formats()、retrieveData()主要用于对 MIME 类型进行判别,但并未提供存储数据的功能,仅仅实现这些虚函数是无法存储数据的。要使数据被存储,可以在QMimeData 中声明成员变量,并把数据存储在其中,或者可以像 QMimeData 一样,再定义一些读取函数、设置函数和测试函数。
- 封装一个存储数据的类并重写QMimeData类的虚函数
class MyData
{
public:
QIcon icon;
QString text;
};
class MyMimeData : public QMimeData
{
Q_OBJECT
public:
MyData data;
QStringList mimeList;
// 存储MIME类型的数据
inline void setMyDataMime(const QString &mime, const MyData &data)
{
if (!hasFormat(mime))
mimeList << mime;
// 存储数据
this->data.icon = data.icon;
this->data.text = data.text;
}
inline MyData myDataMime(const QString &mime) const
{
if (hasFormat(mime)) {
return this->data;
} else {
return MyData();
}
}
QStringList formats() const override { return mimeList; }
bool hasFormat(const QString &mimetype) const override
{
if (mimeList.contains(mimetype)) {
return true;
} else {
return QMimeData::hasFormat(mimetype);
}
}
protected:
QVariant retrieveData(const QString &mimetype, QVariant::Type preferredType) const override;
};
- 在mousePressEvent()函数
MyData myData;
myData.icon = QIcon("../ColorBridge.jpg");
myData.text = QString("CCC");
m_mimeData->setMyDataMime("MyMime", myData);
m_drag->setMimeData(m_mimeData);
m_drag->exec(Qt::CopyAction); // 启动拖放操作
- dropEvent()函数
const MyMimeData *mimeData = dynamic_cast<const MyMimeData*>(event->mimeData());
if (mimeData->hasFormat("MyMime")) {
this->setIcon(mimeData->myDataMime("MyMime").icon);
this->setText(mimeData->myDataMime("MyMime").text);
}
event->accept();