Drag and Drop Implementation
-
Hello everyone, I'm having a bit of an issue with trying to understand Drag and Drop within Qt. I'm a beginner with both coding and Qt, but loving Qt so far! Ok, here's my situation: I've looked up the documentation for Drag and Drop and checked out the examples, specifically the Draggable Icons example (with the boat, house, and car icons). I'm trying to modify it to fit my specific situation, but I'm sure I'm missing several things, so here it goes.
Here's a screenshot of the basic setup I have made in Qt Creator:
There is a group box on the right with some icons in it, I want to make those icons draggable. The large gray box on the left is a QLabel in which a user can select an image to load into it. I want that large QLabel to be where the user can drop any of the icons from the right group box and arrange them as they desire (and drop as many of them as they would like as well). The draggable icons example was perfect for what I wanted, but I cannot get it to work. I'm to understand that I make the QLabels with the icons draggable by subclassing them (is that the same as "Promoting" them?), so I made a class called draggable and then I made another class called DropArea. However, when I "Promote" the icons to Draggable and the large QLabel to Drop Area, nothing happens when I build and run. Again, I know I'm missing several things. What am I missing and/or doing wrong? Here's my code:
Draggable.h
#ifndef DRAGGABLE_H #define DRAGGABLE_H #include <QLabel> class Draggable : public QLabel { public: explicit Draggable(QWidget *parent = nullptr); protected: void dragMoveEvent(QDragMoveEvent *event) override; void mousePressEvent(QMouseEvent *event) override; }; #endif // DRAGGABLE_H
DropArea.h
#ifndef DROPAREA_H #define DROPAREA_H #include <QLabel> class DropArea : public QLabel { public: explicit DragWidget(QWidget *parent = nullptr); protected: void dragEnterEvent(QDragEnterEvent *event) override; void dragMoveEvent(QDragMoveEvent *event) override; void dropEvent(QDropEvent *event) override; void mousePressEvent(QMouseEvent *event) override; }; #endif // DROPAREA_H
And here are my source files:
Draggable.cpp
#include <QtWidgets> #include "draggable.h" Draggable::Draggable(QWidget *parent) : QLabel(parent) { } void Draggable::dragMoveEvent(QDragMoveEvent *event) { if (event->mimeData()->hasFormat("application/icondata")) { if (event->source() == this) { event->setDropAction(Qt::MoveAction); event->accept(); } else { event->acceptProposedAction(); } } else { event->ignore(); } } void Draggable::mousePressEvent(QMouseEvent *event) { QLabel *child = static_cast<QLabel*>(childAt(event->pos())); if (!child) return; QPixmap pixmap = child->pixmap(Qt::ReturnByValue); QByteArray itemData; QDataStream dataStream(&itemData, QIODevice::WriteOnly); dataStream << pixmap << QPoint(event->pos() - child->pos()); QMimeData *mimeData = new QMimeData; mimeData->setData("application/icondata", itemData); QDrag *drag = new QDrag(this); drag->setMimeData(mimeData); drag->setPixmap(pixmap); drag->setHotSpot(event->pos() - child->pos()); QPixmap tempPixmap = pixmap; QPainter painter; painter.begin(&tempPixmap); painter.fillRect(pixmap.rect(), QColor(127, 127, 127, 127)); painter.end(); child->setPixmap(tempPixmap); if (drag->exec(Qt::CopyAction | Qt::MoveAction, Qt::CopyAction) == Qt::MoveAction) { child->close(); } else { child->show(); child->setPixmap(pixmap); } }
DropArea.cpp
#include <QtWidgets> #include "draggable.h" Draggable::Draggable(QWidget *parent) : QLabel(parent) { } void Draggable::dragEnterEvent(QDragEnterEvent *event) { if (event->mimeData()->hasFormat("application/icondata")) { if (event->source() == this) { event->setDropAction(Qt::MoveAction); event->accept(); } else { event->acceptProposedAction(); } } else { event->ignore(); } } void Draggable::dragMoveEvent(QDragMoveEvent *event) { if (event->mimeData()->hasFormat("application/icondata")) { if (event->source() == this) { event->setDropAction(Qt::MoveAction); event->accept(); } else { event->acceptProposedAction(); } } else { event->ignore(); } } void Draggable::dropEvent(QDropEvent *event) { if (event->mimeData()->hasFormat("application/icondata")) { QByteArray itemData = event->mimeData()->data("application/icondata"); QDataStream dataStream(&itemData, QIODevice::ReadOnly); QPixmap pixmap; QPoint offset; dataStream >> pixmap >> offset; QLabel *newIcon = new QLabel(this); newIcon->setPixmap(pixmap); newIcon->move(event->pos() - offset); newIcon->show(); newIcon->setAttribute(Qt::WA_DeleteOnClose); if (event->source() == this) { event->setDropAction(Qt::MoveAction); event->accept(); } else { event->acceptProposedAction(); } } else { event->ignore(); } } void Draggable::mousePressEvent(QMouseEvent *event) { QLabel *child = static_cast<QLabel*>(childAt(event->pos())); if (!child) return; QPixmap pixmap = child->pixmap(Qt::ReturnByValue); QByteArray itemData; QDataStream dataStream(&itemData, QIODevice::WriteOnly); dataStream << pixmap << QPoint(event->pos() - child->pos()); QMimeData *mimeData = new QMimeData; mimeData->setData("application/icondata", itemData); QDrag *drag = new QDrag(this); drag->setMimeData(mimeData); drag->setPixmap(pixmap); drag->setHotSpot(event->pos() - child->pos()); QPixmap tempPixmap = pixmap; QPainter painter; painter.begin(&tempPixmap); painter.fillRect(pixmap.rect(), QColor(127, 127, 127, 127)); painter.end(); child->setPixmap(tempPixmap); if (drag->exec(Qt::CopyAction | Qt::MoveAction, Qt::CopyAction) == Qt::MoveAction) { child->close(); } else { child->show(); child->setPixmap(pixmap); } }
As you can see, I've taken much of the code from the draggable icons example and tried to implement it into my program. Again, please forgive me if I am doing something dumb or what an experienced coder would consider stupid, I'm still trying to learn. I left my constructor blank for the draggable class because I'm thinking that the QLabels with pixmap icons are already created in Qt Creator and I just want the drag and drop functionality from the other functions of the class. Is that wrong? If so, what should I have in my constructor? And what else do I need to correct/change in my code?
Thank you for taking a look at my issue, I really appreciate your time and help with this.
-
Hi and welcome to devnet,
From the looks of it, you split the functionality of the original widget the wrong way. I might be wrong but I think the Drag And Drop Puzzle example might be closer to what you want to do (i.e. one drag source and a different drop target).
As for the Promotion feature of designer it's not the same as inheritance. You have to subclass one of the base widget in order to use it though.
-
Hi SGaist,
Thank you for your welcome and reply.Ok, I will look more closely at the Puzzle example. Thank you for pointing that out. In terms of subclassing, what does that mean? How would I subclass a base widget? I think I've been misunderstanding the term when I see other coders use it.
Thanks again, I really appreciate your help :)
-
Subclassing is the action of creating a new class based on an existing one. For example, QLabel inherits from QWidget.
-
@Wiki_Wut said in Drag and Drop Implementation:
this would mean that MyClass is inheriting from QLabel?
yes
-
Thank you for your help. Unfortunately, I still cannot figure it out. I can't get that darn label to move, let alone be dragged and dropped. There's much about the examples I do not understand. Does anyone know if there is a good tutorial/article/source that thoroughly explains drag and drop functionality. When I say thoroughly, I mean going through the code and explaining line by line how it works since I'm a beginner with both Qt and coding? If not, that's ok, I still very much appreciate your time and help.
-
You should check the VoidRealms YouTube channel. It has a very extensive set of Qt related videos which may interest you.