实现QGraphicsItem拖拽功能
Qt 的 Grpahics View 框架提供拖拽功能支持,如拖拽图片以移动滚动条。但如果需要更复杂的交互功能,就需要编写自己的 QGraphicsItem 类,重新实现相关方法。
拖拽简介
拖拽(Drag and Drop)是多种动作的组合,包括:
按住鼠标左键
移动鼠标
在另外一个位置释放鼠标
涉及两个对象:源对象和目标对象。
需要实现的Qt事件
源对象需要处理 mouse 事件,目标对象需要处理 drag 和 drop 事件。
源对象事件:
mousePressEvent:点击左键
mouseMoveEvent:拖动鼠标
mouseReleaseEvent:释放鼠标
目标对象事件:
dragEnterEvent:拖动对象进入目标对象
dragLeaveEvent:拖动对象离开目标对象
dropEvent:拖动对象到目标对象中
数据结构
拖拽是一种交互信息的方式,拖拽的其实是数据,当然这数据可以在拖拽时生成。拖拽时会生成一个 QMimeData 对象,保存拖拽的数据。我需要实现一种特殊的功能,保存某个对象的指针,默认的 QMimeData 中无法保存指针,需要自定义。根据文档,继承 QMimeData 需要实现三个方法:hasFormat()、formats()、retrieveData()。
接着在目标对象的 dropEvent 函数中,识别我定义的特殊 MimeData。通过 qobject_cast 判断 event 的 memeData() 是否能强制转换到自定义的 mime data。
const WorkSpaceGraphicsImageItemMimeData *mime_data =
qobject_cast<const WorkSpaceGraphicsImageItemMimeData *>(event->mimeData());
if (mime_data) {
// 使用自定义的方法获取 mime data 中的值
}我的 MimeData 定义如下:
class WorkSpaceGraphicsImageItemMimeData : public QMimeData
{
Q_OBJECT
public:
WorkSpaceGraphicsImageItemMimeData();
~WorkSpaceGraphicsImageItemMimeData();
virtual bool hasFormat ( const QString & mimeType ) const;
virtual QStringList formats () const;
WorkSpaceContent *getWorkSpaceContent() const;
void setWorkSpaceContent(WorkSpaceContent *value);
protected:
virtual QVariant retrieveData ( const QString & mimeType, QVariant::Type type ) const;
private:
QString mime_type_;
WorkSpaceContent *work_space_content;
};保存 WorkSpaceContent 指针,上面 dropEvent 函数通过 getWorkSpaceContent() 获取该指针。
实现
源对象
mousePressEvent:改变鼠标指针
void WorkSpaceGraphicsImageItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
if( FormularAction == interaction_action_type_)
{
setCursor(Qt::ClosedHandCursor);
}
else
{
QGraphicsPixmapItem::mousePressEvent(event);
}
}mouseMoveEvent:生成 QDrag 对象,装入 WorkSpaceGraphicsImageItemMimeData
void WorkSpaceGraphicsImageItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
if( FormularAction == interaction_action_type_)
{
if (QLineF(event->screenPos(), event->buttonDownScreenPos(Qt::LeftButton))
.length() < QApplication::startDragDistance()) {
return;
}
QDrag *drag = new QDrag(event->widget());
WorkSpaceGraphicsImageItemMimeData *mime = new WorkSpaceGraphicsImageItemMimeData();
mime->setWorkSpaceContent(work_space_content_);
drag->setMimeData(mime);
setCursor(Qt::ClosedHandCursor);
drag->exec();
}
else
{
QGraphicsPixmapItem::mouseMoveEvent(event);
}
}mouseReleaseEvent:恢复鼠标指针
void WorkSpaceGraphicsImageItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
if( FormularAction == interaction_action_type_)
{
setCursor(Qt::OpenHandCursor);
}
else
{
QGraphicsPixmapItem::mouseReleaseEvent(event);
}
}目标对象
需要在构造函数中设置接受拖拽:
setAcceptDrops(true);
dragEnterEvent:判断是否是特定的拖拽操作,只接受上述拖拽。
void WorkSpaceGraphicsImageItem::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
{
const WorkSpaceGraphicsImageItemMimeData *mime_data =
qobject_cast<const WorkSpaceGraphicsImageItemMimeData *>(event->mimeData());
if (mime_data) {
event->setAccepted(true);
}
else
{
QGraphicsPixmapItem::dragEnterEvent(event);
}
}dropEvent:接受拖拽事件,执行拖拽后的操作
void WorkSpaceGraphicsImageItem::dropEvent(QGraphicsSceneDragDropEvent *event)
{
const WorkSpaceGraphicsImageItemMimeData *mime_data =
qobject_cast<const WorkSpaceGraphicsImageItemMimeData *>(event->mimeData());
if (mime_data) {
WorkSpaceContent *source_work_space_content = mime_data->getWorkSpaceContent();
WorkSpaceContent *target_work_space_content = work_space_content_;
if(source_work_space_content == target_work_space_content)
{
qWarning()<<"[WorkSpaceGraphicsImageItem] dropEvent: image is same.";
return;
}
QMessageBox::information(0, "Image Viewer", "WorkSpaceGraphicsImageItem::dropEvent");
}
else
{
QGraphicsPixmapItem::dropEvent(event);
}
}