Qt:Drag-Drop操作在QGraphicsView及Model/View框架下的实现

本文详细介绍了如何在Qt应用中实现Model/View和QGraphicsView之间的DragDrop功能,包括Model/View部分的DragDrop模式设置、数据封装与传递,以及QGraphicsView部分的Drag模式配置、Item接受拖动和事件处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近使用到Qt的Drag Drop功能,结合自己的例子写下来给大家分享一下。实现从QTreeView拖动Node到QGraphicsView上,以及QGraphicsView上item之间的拖动。

先来说Model/View中的实现

1.Model/View要实现Drag Drop操作,首先需要为View设置DragDropMode属性。

1
2
enum DragDropMode
{ NoDragDrop, DragOnly, DropOnly, DragDrop, InternalMove }

以上是View支持的所有Mode Type,View默认是NoDragDrop,即默认不支持拖拽。

m_treeView->setDragDropMode(QAbstractItemView::DragOnly);

我只需要TreeView的Drag操作,所以设置成DragOnly,可以依据自己需要实现的功能来设置。

2.Drag and Drop是两个操作,独立分开的。

Drag 实现需要的步骤:第一步,在对应的Model的flags函数中,允许drag的item返回Qt::ItemIsDragEnabled。

1
2
3
4
5
6
Qt::ItemFlags NetlistModel::flags(const QModelIndex &index) const
{
    Qt::ItemFlags flags = QAbstractItemModel::flags(index);
    flags = flags | Qt::ItemIsDragEnabled;
    return flags;
}

第二步,Model中需要实现mimeData函数,该函数是封装Drag数据,数据由Drop方接收处理。该步是处理传递的数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
QMimeData *NetlistModel::mimeData(const QModelIndexList &indexes) const
{
    if (indexes.count() <= 0)
    {//If the list of indexes is empty, or there are no supported MIME types,
         //0 is returned rather than a serialized empty list.
        return 0;
    }
 
    //QMap<int, QStringList> nodeData;
    //QStringList listName;
//  foreach(QModelIndex index, indexes)
//  {
//      TreeNode* node = nodeFromIndex(index);
//  }
 
    QByteArray itemData;
    QDataStream dataStream(&itemData, QIODevice::WriteOnly);
 
    TreeNode* node = nodeFromIndex(indexes.at(0));
    NlsData::ConItem* conItem = static_cast<NlsData::ConItem*>(node->data());
    if (conItem)
    {
        dataStream << node->type() << conItem->name();
    }
 
    QMimeData *data = new QMimeData;
    data->setData("nlsdata/items", itemData);//key值需要与Drop中一致
    return data;
}                   

QTreeView我只需要单个拖拽,所以数据处理是单个index,如果多个拖拽,可以将数据封装成List or Map,注意QDataStream只可以放Qt的基本数据类型,自定义的struct其中也只能是基本数据类型,不能放指针。

3.对于Drop操作,步骤也相似,第一步flags函数中返回Qt::ItemIsDropEnabled;第二步需要实现mimeTypes函数,这个函数要返回当前数据模型允许接收的数据类型列表,它会在Drag操作过程中被调用,如果Drag操作所包含的对象(mimeData方法返回的数据对象)没有相关类型的数据,就不允许执行Drop操作;第三步实现dropMimeData方法。这个方法主要在drop操作后解析数据并添加到当前模型的合适位置。因为我在QTreeView不允许Drop操作,所以就不贴代码了。

 

接下来是QGraphicsView中的实现。

1.设置QGraphicsView的DragMode。关于DragMode,This property holds the behavior for dragging the mouse over the scene while the left mouse button is pressed.

在View的构造函数中添加

1
setDragMode(ScrollHandDrag);

不过该函数似乎只设置了Drag时的鼠标样式,因为我的Drag Drop都是在QGraphicsItem中实现的。

2.在QGraphicsItem的构造函数中设置setAcceptDrops(true),允许接收Drop。

1
2
3
4
5
6
if (!border)
{
  setFlags(ItemIsSelectable);
  setAcceptsHoverEvents(true);
  setAcceptDrops(true);
}

当然,为需要的Item设置需要的。

3.实现Item的一下事件

1
2
3
4
void dragEnterEvent(QGraphicsSceneDragDropEvent *event);
void dragLeaveEvent(QGraphicsSceneDragDropEvent *event);
void dropEvent(QGraphicsSceneDragDropEvent *event);//处理drop
void mouseMoveEvent(QGraphicsSceneMouseEvent *event);//添加QDrag

dragEnterEvent中,如果有需要的数据,event->setAccepted(true),接收drag事件

1
2
3
4
5
6
7
8
9
10
11
12
13
void GPkgCellItem::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
{
    if (event->mimeData()->hasFormat("nlsdata/items"))
    {
        event->setAccepted(true);
        m_bOnDrag = true;//painter函数中使用,绘制不同的样式
        update();
    }
    else
    {
        event->setAccepted(false);
    }
}

dragLeaveEvent

1
2
3
4
5
6
void GPkgCellItem::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
{
    Q_UNUSED(event);
    m_bOnDrag = false;
    update();
}

dropEvent中需要处理mimeData,获得drag传过来的数据。

1
2
3
4
5
6
7
8
9
10
11
12
void GPkgCellItem::dropEvent(QGraphicsSceneDragDropEvent *event)
{
    m_bOnDrag = false;
    if (event->mimeData()->hasFormat("nlsdata/items"))
    {
        QByteArray itemData = event->mimeData()->data("nlsdata/items");
        QDataStream dataStream(&itemData, QIODevice::ReadOnly);
        dataStream >> m_dragType >> m_dragName;
        setAcceptDrops(false);
    }
    update();
}

实现QGraphicsItem之间的Drag,需要在mouseMoveEvent中添加QDrag动作,该Drag也在dropEvent中接收。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
void GPkgCellItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    if (QLineF(event->screenPos(), event->buttonDownScreenPos(Qt::LeftButton))
        .length() < QApplication::startDragDistance())
    {//触发需满足move事件最小距离(The default value is 4 pixels.)
        return;
    }
 
    if (m_dragName.isEmpty())
    {
        return;
    }
 
    QMimeData *mimeData = new QMimeData;
    QByteArray exData;
    QDataStream dataStream (&exData, QIODevice::WriteOnly);
    dataStream << m_dragType << m_dragName;
    mimeData->setData("nlsdata/items", exData);
    QDrag *drag = new QDrag (event->widget());
    drag->setMimeData(mimeData);
    if (drag->exec(Qt::MoveAction) == Qt::MoveAction)
    {//drag动作完成、Drop处理之后的动作,在dropEvent调用之后被调用,相当于回调函数,可以把数据清理之类的放在此处处理。
        m_dragName = "";
        m_dragType = 0;
        setAcceptDrops(true);//随时更新状态,是否允许drop
        update();
    }
}

Drag-Drop的机制就是在开始拖动鼠标时启动QDrag并封装数据,然后drag到某个位置松开鼠标触发Drop,然后获取数据并处理。

添加一张程序的截图

 

 

记录,成长过程的每一步。。。





Qt实现QTreeView到GraphicsView的拖放功能,可以通过以下几个步骤来完成: 1. **设置QTreeView的拖放功能**: - 启用QTreeView的拖放功能。 - 实现自定义的模型(如QStandardItemModel),以便在拖动时提供正确的数据。 2. **设置GraphicsView的接受拖放功能**: - 启用QGraphicsView的接受拖放功能。 - 实现自定义的QGraphicsItem,以便在接收数据后进行相应的处理和显示。 3. **实现拖放逻辑**: - 在拖动操作开始时,提供要传输的数据。 - 在拖动操作结束时,接收并处理传输的数据。 以下是一个简单的示例代码,展示了如何在Qt实现QTreeView到GraphicsView的拖放功能: ```cpp #include <QApplication> #include <QTreeView> #include <QGraphicsView> #include <QStandardItemModel> #include <QGraphicsScene> #include <QGraphicsRectItem> #include <QMouseEvent> // 自定义的QGraphicsRectItem,用于接收数据 class DraggableRectItem : public QGraphicsRectItem { public: DraggableRectItem(const QString &data) { this->data = data; setRect(0, 0, 100, 100); setBrush(Qt::blue); } protected: void mousePressEvent(QMouseEvent *event) override { // 设置拖动数据 QDrag *drag = new QDrag(event->widget()); QMimeData *mimeData = new QMimeData; mimeData->setText(data); drag->setMimeData(mimeData); drag->exec(); } private: QString data; }; // 自定义QGraphicsView,用于接收拖放数据 class MyGraphicsView : public QGraphicsView { public: MyGraphicsView(QWidget *parent = nullptr) : QGraphicsView(parent) { setScene(new QGraphicsScene(this)); } protected: void dragEnterEvent(QDragEnterEvent *event) override { if (event->mimeData()->hasText()) { event->acceptProposedAction(); } } void dropEvent(QDropEvent *event) override { if (event->mimeData()->hasText()) { QString data = event->mimeData()->text(); DraggableRectItem *item = new DraggableRectItem(data); scene()->addItem(item); item->setPos(mapToScene(event->pos())); event->acceptProposedAction(); } } }; int main(int argc, char *argv[]) { QApplication app(argc, argv); QTreeView treeView; QStandardItemModel model(3, 1, &treeView); model.setHeaderData(0, Qt::Horizontal, QObject::tr("Nodes")); for (int i = 0; i < 3; ++i) { QStandardItem *item = new QStandardItem(QString("Node %0").arg(i)); model.setItem(i, 0, item); } treeView.setModel(&model); treeView.setDragEnabled(true); treeView.setAcceptDrops(false); MyGraphicsView graphicsView; graphicsView.setWindowTitle("Graphics View"); treeView.show(); graphicsView.show(); return app.exec(); } ``` 在这个示例中,我们创建了一个QTreeView和一个MyGraphicsViewQTreeView使用QStandardItemModel作为模型,并启用了拖动功能。MyGraphicsView继承自QGraphicsView,并实现了拖放事件的处理逻辑。当我们在QTreeView中拖动一个节点并将其拖到MyGraphicsView中时,会在GraphicsView中创建一个新的QGraphicsRectItem,并显示拖动的数据。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值