Qt - How to show image/icon/data while dragging an item? - c++

I have an application where I can drag an item to a QGraphicsScene and create a new object depending on the item text, but how can I change the data being displayed while moving around the item?
for example, instead of a text, I want to show an icon:
I have a list with some itens:
OptionList::OptionList(QWidget *parent) : QListWidget(parent)
{
this->setDragEnabled(true);
this->setDropIndicatorShown(true);
this->setSelectionMode(QAbstractItemView::SingleSelection);
this->setDefaultDropAction(Qt::CopyAction);
this->setViewMode(QListView::ListMode);
for(const QString &color : {"Blue", "Red", "Green", "Yellow"})
{
OptionItem *item = new OptionItem;
item->setText(color);
item->setFlags(Qt::ItemIsEnabled| Qt::ItemIsSelectable| Qt::ItemIsDragEnabled);
addItem(item);
}
}
I drop the itens into the scene to create a new object:
MyScene::MyScene()
{
setBackgroundBrush(Qt::lightGray);
}
void MyScene::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
{
if(event->mimeData()->hasFormat("application/x-qabstractitemmodeldatalist"))
event->setAccepted(true);
}
void MyScene::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
{
if(event->mimeData()->hasFormat("application/x-qabstractitemmodeldatalist"))
event->setAccepted(true);
}
void MyScene::dropEvent(QGraphicsSceneDragDropEvent *event)
{
QByteArray encoded = event->mimeData()->data("application/x-qabstractitemmodeldatalist");
QDataStream stream(&encoded, QIODevice::ReadOnly);
QStringList colors;
while (!stream.atEnd())
{
int row, col;
QMap<int, QVariant> roleDataMap;
stream >> row >> col >> roleDataMap;
colors << roleDataMap[Qt::DisplayRole].toString();
}
QPointF posView = event->scenePos() ;
for(const QString & color: colors)
{
Block *newBlock = new Block(color);
newBlock->setPos(posView);
addItem(newBlock);
}
}
Then, I created OptionItem class, derived from QListWidgetItem, and reimplemented the mousePressEvent, mouseMoveEvent and mouseReleaseEvent
OptionItem::OptionItem()
{
}
void OptionItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
event->setAccepted(true);
}
void OptionItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
QDrag *drag = new QDrag(event->widget());
QMimeData *mime = new QMimeData;
QImage image(":/images/MyIcon_icon.png");
mime->setImageData(image);
drag->setMimeData(mime);
drag->setPixmap(QPixmap::fromImage(image));
drag->setHotSpot(QPoint(15, 30));
drag->exec();
event->setAccepted(true);
}
void OptionItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
event->setAccepted(true);
}
I tried to follow the drag drop robot example in the Qt Creator but it isn't the same thing
It seems that the image appears very quickly when I start dragging an item
Is there a way to show the icon while dragging the item through the whole operation?

The classes that inherit from QAbstractItemView support the default drag so they already have implemented methods, instead the example you point out shows how to implement this functionality for some class that does not have it, the task in your case is simple, you must overwrite the method startDrag of QListWidget.
optionilist.h
#ifndef OPTIONLIST_H
#define OPTIONLIST_H
#include <QListWidget>
class OptionList: public QListWidget{
public:
OptionList(QWidget* parent=nullptr);
protected:
void startDrag(Qt::DropActions supportedActions);
};
#endif // OPTIONLIST_H
optionlist.cpp
#include "optionlist.h"
#include <QDrag>
OptionList::OptionList(QWidget *parent): QListWidget(parent){
setDragEnabled(true);
setDropIndicatorShown(true);
setSelectionMode(QAbstractItemView::SingleSelection);
setDefaultDropAction(Qt::CopyAction);
setViewMode(QListView::ListMode);
for(const QString &color : {"Blue", "Red", "Green", "Yellow"}){
QListWidgetItem *blue = new QListWidgetItem;
blue->setText(color);
blue->setFlags(Qt::ItemIsEnabled| Qt::ItemIsSelectable| Qt::ItemIsDragEnabled);
addItem(blue);
}
}
void OptionList::startDrag(Qt::DropActions supportedActions){
if(supportedActions & Qt::CopyAction){
QList<QListWidgetItem *> m_items = selectedItems();
if(m_items.isEmpty())
return;
QMimeData *data = mimeData(m_items);
QDrag *drag = new QDrag(this);
QPixmap pixmap(":/images/MyIcon_icon.png");
drag->setPixmap(pixmap);
drag->setMimeData(data);
drag->setHotSpot(pixmap.rect().center());
drag->exec(Qt::CopyAction);
}
else
QListWidget::startDrag(supportedActions);
}
The complete code can be found at the following link.

The above is correct!
Also should note that drag->setMimeData(data); needs to be called after drag->setPixmap(pixmap);.
Otherwise, during the drag moving, it will show the original mimedata type instead of showing an image/icon.

Related

How to drag and drop a specific QPixmap into a QGraphicsView?

On a subclassed QListWidget I have several items. Every QListWidget item (e.g. "ROS Init", "Images" etc) that is shown below is associated with a specific icon.
The problem I have is that I am trying to drag and drop the specific icon corresponding to that QListWidget item, but nothing happens.
Below the function responsible for the dragging:
void ListView::startDrag(Qt::DropActions supportedActions)
{
QMap<int, QString> icons;
icons.insert(IT_RosInit, "ROS Init");
icons.insert(IT_Images, "Images");
icons.insert(IT_Path, "Path");
icons.insert(IT_RosShutDown, "ROS Shutdown");
if (supportedActions & Qt::CopyAction)
{
const QList<QListWidgetItem *> &m_items(selectedItems());
if (m_items.isEmpty())
return;
QPixmap pixmapLaser("/home/images/laserscan.png");
QPixmap pixmapPCloud2("/home/images/pcloud2.png");
// etc ...
QStringList iconImages;
for(int i = 0; i < icons.count(); ++i)
{
for (const QString &tableType : iconImages) {
if (tableType == "ROS Init")
{
auto *data = mimeData(m_items);
auto *drag = new QDrag(this);
drag->setPixmap(pixmapLaser);
drag->setMimeData(data);
drag->setHotSpot(pixmapLaser.rect().center());
drag->exec(Qt::CopyAction);
}
else if(tableType == "Images")
{
auto *data2 = mimeData(m_items);
auto *drag2 = new QDrag(this);
drag2->setPixmap(pixmapPCloud2);
drag2->setMimeData(data2);
drag2->setHotSpot(pixmapPCloud2.rect().center());
drag2->exec(Qt::CopyAction);
}
}
}
}
else
{
QListWidget::startDrag(supportedActions);
}
}
After subclassing the QListWidget I just reimplemented the usual drag and drop function. All other function are working properly but the startDrag and in fact as I try to drag the proper QPixmap, I actually see nothing being dragged.
I consulted this source, useful, and also this other source which was useful but it didn't reimplement the startDrag but instead dropEvent which for me is not a problem because that is working well.
I also consulted this source and this other source but that also didn't help fixing the problem.
Thanks for shedding light on this matter for solving the problem
Solution
I would approach this problem in the following way:
Set the ItemIsDragEnabled flag of QListWidgetItem to enable the item for dragging:
item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled);
Set the desired data for each item:
item->setData(Qt::UserRole, type);
where type is one of the enumerated values IT_RosInit, IT_Images, etc.
Enable the drag functionality of the ListWidget:
setDragEnabled(true);
setDragDropMode(QAbstractItemView::DragOnly);
Use the item settings to setup the QDrag object, created in startDrag.
Example
Here is an example I have prepared for you to demonstrate how the proposed solution could be implemented:
MainWindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QWidget(parent)
{
auto *l = new QVBoxLayout(this);
auto *list = new ListView(this);
list->addItem(createItem(":/pix/images/laserscan.png", tr("RosInit"), IT_RosInit));
list->addItem(createItem(":/pix/images/icons/pcloud2.png", tr("Images"), IT_Images));
list->addItem(createItem(":/pix/images/icons/some_icon.png", tr("Path"), IT_Path));
list->addItem(createItem(":/pix/images/icons/another_icon.png", tr("RosShutDown"), IT_RosShutDown));
l->addWidget(list);
resize(300, 400);
setWindowTitle("IconDrag");
}
QListWidgetItem *MainWindow::createItem(const QString &pm, const QString &text, int type)
{
auto *item = new QListWidgetItem(QIcon(QPixmap(pm)), text);
item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled);
item->setData(Qt::UserRole, type);
return item;
}
ListView.cpp
ListView::ListView(QWidget *parent) :
QListWidget(parent)
{
setDragEnabled(true);
setDragDropMode(QAbstractItemView::DragOnly);
setSelectionBehavior(QAbstractItemView::SelectRows);
setSelectionMode(QAbstractItemView::SingleSelection);
}
void ListView::startDrag(Qt::DropActions supportedActions)
{
if (supportedActions & Qt::CopyAction) {
const QList<QListWidgetItem *> &items(selectedItems());
if (items.isEmpty())
return;
const QPixmap &pm(items.first()->icon().pixmap(64));
auto *item = items.first();
auto *mimeData = new QMimeData();
auto *drag = new QDrag(this);
mimeData->setData("text/plain", item->data(Qt::UserRole).toByteArray());
drag->setPixmap(pm);
drag->setMimeData(mimeData);
drag->setHotSpot(pm.rect().center());
drag->exec(Qt::CopyAction);
}
}

Accept Drag & Drop to QGraphicsScene

Trying to drag & drop label from QWidget to QGraphicsScene. I have made custom implementation from QLabel. Also QGraphicsView is accepting drops. The problem is that QGraphicsScene doens't get any events when dropping element to it.
QLabel implementation .cpp
void ItemLabel::mousePressEvent(QMouseEvent *ev)
{
if(ev->button() == Qt::LeftButton){
QDrag *drag = new QDrag(this);
QMimeData *mimeData = new QMimeData;
mimeData->setText("Test");
drag->setMimeData(mimeData);
drag->setPixmap(*this->pixmap());
drag->exec();
Qt::DropAction dropAction = drag->exec(Qt::CopyAction | Qt::MoveAction);
}
}
Scene .cpp
void Scene::dropEvent(QGraphicsSceneDragDropEvent *event)
{
event->acceptProposedAction();
qDebug() << "drop";
}
void Scene::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
{
if(event->mimeData()->hasFormat("text/plain"))
event->acceptProposedAction();
}
What I need to do to get scene receive drops?
You should extend a QGraphicsView, set up a QGraphicsScene and then override the QGraphicsView drag/drop slots.
Take a look at this working sample:
MyGraphicsView::MyGraphicsView(QObject *p_parent):
QGraphicsView(),
m_scene(nullptr)
{
...
m_scene = new QGraphicsScene(rect(), p_parent);
setScene(m_scene);
setAcceptDrops(true);
}
void MyGraphicsView::dragEnterEvent(QDragEnterEvent *p_event)
{
p_event->acceptProposedAction();
}
void MyGraphicsView::dragMoveEvent(QDragMoveEvent *p_event)
{
p_event->acceptProposedAction();
}
void MyGraphicsView::dropEvent(QDropEvent *p_event)
{
p_event->acceptProposedAction();
}
If you do not acceptProposedAction() in dragEnter and dragMove, you will never get dropEvent triggered.

Qt - How to get the text from an item when dragging and dropping in a QGraphicScene?

I'am new to Qt Creator and coding in general and I'am trying to create an application where I have a list of color options and an area where I can drag these options and create a graphic item with the color I chose, here's the code:
I created a QListWidget and added two QListWidgetItem itens for now:
OptionList::OptionList(QWidget *parent) : QListWidget(parent)
{
this->setDragEnabled(true);
this->setDropIndicatorShown(true);
this->setSelectionMode(QAbstractItemView::SingleSelection);
this->setDefaultDropAction(Qt::CopyAction);
this->setViewMode(QListView::ListMode);
QListWidgetItem *blue = new QListWidgetItem;
blue->setText("Blue");
blue->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable |
Qt::ItemIsDragEnabled);
addItem(blue);
QListWidgetItem *red = new QListWidgetItem;
red->setText("Red");
red->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable |
Qt::ItemIsDragEnabled);
addItem(red);
}
I Created a QgraphicsPathItem that receives a string, depending on the string, it changes color
Block::Block(QString color, QGraphicsItem *parent) : QGraphicsPathItem(parent)
{
QPainterPath p;
p.addRoundedRect(0, 0, 150, 50, 2, 2);
setPath(p);
setPen(QPen(Qt::black));
if (color == "Blue")
{
setBrush(Qt::blue);
}
else if (color == "Red")
{
setBrush(Qt::red);
}
setFlag(QGraphicsItem::ItemIsMovable);
setFlag(QGraphicsItem::ItemIsSelectable);
}
then, I created MyScene class derived from QGraphicsScene, and reimplemented the dragEnterEvent, dragMoveEvent and dropEvent
#include "myscene.h"
MyScene::MyScene()
{
setBackgroundBrush(Qt::lightGray);
}
void MyScene::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
{
event->setAccepted(true);
}
void MyScene::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
{
event->setAccepted(true);
}
void MyScene::dropEvent(QGraphicsSceneDragDropEvent *event)
{
QString color;
color = event->mimeData()->text();
Block *newBlock = new Block(color);
QPointF posView = event->scenePos();
newBlock->setPos(posView);
addItem(newBlock);
}
I tried using QString color; color = event->mimeData()->text(); but it isn't working
I know it has something to do with the QMimeData class but I don't know what to do
How can I get the text from the item in the list and pass it to the Block class to change its color?
You have to decode the data since QListWidget, which is a QAbstractItemView, supports multi-selection.
void MyScene::dragEnterEvent(QGraphicsSceneDragDropEvent *event){
if(event->mimeData()->hasFormat("application/x-qabstractitemmodeldatalist"))
event->setAccepted(true);
}
void MyScene::dragMoveEvent(QGraphicsSceneDragDropEvent *event){
if(event->mimeData()->hasFormat("application/x-qabstractitemmodeldatalist"))
event->setAccepted(true);
}
void MyScene::dropEvent(QGraphicsSceneDragDropEvent *event){
QByteArray encoded = event->mimeData()->data("application/x-qabstractitemmodeldatalist");
QDataStream stream(&encoded, QIODevice::ReadOnly);
QStringList colors;
while (!stream.atEnd())
{
int row, col;
QMap<int, QVariant> roleDataMap;
stream >> row >> col >> roleDataMap;
colors << roleDataMap[Qt::DisplayRole].toString();
}
QPointF posView = event->scenePos();
for(const QString & color: colors){
Block *newBlock = new Block(color);
newBlock->setPos(posView);
addItem(newBlock);
}
}

Drag and drop listwidget items with a combobox inside

I have two listwidgets in icon mode list1 is a list of 100 widget items (each Widget item is an icon packed with a combobox with 3-4 items inside) .
Those combobox items are data of the icon this will never change ,they are packed with the Widget item ,listwidget2 is empty and I just want to be able to drag the widget item(icon and combobox with items) from 1 and make a favorite list with some widgets of list1, nothing will change to combobox items or the widget items of list1 they will have always the same data,
the problem is that each time I drag the widget only the icon is copied to the other listwidget.
QDir dir ("icons");
QFileInfoList list = dir.entryInfoList(QDir::AllEntries |
QDir::Dirs|QDir::NoDotAndDotDot);
for(int i=0 ; i < list.length() ; i++){
dir_names.push_back(list.at(i).baseName());
/*Setting the icon*/
QIcon icon;
icon.addFile(list.at(i).absoluteFilePath(), QSize(), QIcon::Normal,
QIcon::Off);
QListWidgetItem *iconItem = new QListWidgetItem(ui->listWidget);
iconItem->setIcon(icon);
QComboBox *box = new QComboBox;
QListWidgetItem *textItem = ui->listWidget->item(i);
ui->listWidget->setItemWidget( textItem,box);
box->setFixedHeight(18);
box->addItem(list.at(i).baseName());
}
If you want the combobox to be moved you must overwrite the dropEvent method so you must create a class that inherits from QListWidget, get the widget and copy the necessary data. If you want to use it in Qt Designer you must promote it.
listwidget.h
#ifndef LISTWIDGET_H
#define LISTWIDGET_H
#include <QListWidget>
class ListWidget : public QListWidget
{
Q_OBJECT
public:
ListWidget(QWidget * parent = 0);
void dropEvent(QDropEvent * event);
protected:
void mouseMoveEvent(QMouseEvent * event);
};
#endif // LISTWIDGET_H
listwidget.cpp
#include "listwidget.h"
#include <QDropEvent>
#include <QComboBox>
ListWidget::ListWidget(QWidget *parent):QListWidget(parent)
{
setDragEnabled(true);
setAcceptDrops(true);
setDropIndicatorShown(true);
setDefaultDropAction(Qt::MoveAction);
}
void ListWidget::dropEvent(QDropEvent *event)
{
if(event->dropAction() == Qt::MoveAction && event->source()){
ListWidget *listWidget = qobject_cast<ListWidget *>(event->source());
if(!listWidget)
return;
QList<QPersistentModelIndex> pIndexes;
for(QModelIndex index: listWidget->selectedIndexes()){
pIndexes << QPersistentModelIndex(index);
}
std::sort(pIndexes.begin(), pIndexes.end());
QListWidgetItem *item = itemAt(event->pos());
int rowStart = item? row(item) : count();
for(QPersistentModelIndex pindex: pIndexes){
int r = QModelIndex(pindex).row();
QComboBox *input = qobject_cast<QComboBox *>(listWidget->itemWidget(listWidget->item(r)));
QComboBox *output;
if(input){
// move data to QComboBox
output = new QComboBox;
for(int i=0; i<input->count(); i++){
output->addItem(input->itemText(i));
output->setCurrentText(input->currentText());
}
}
QListWidgetItem *it = listWidget->takeItem(r);
insertItem(rowStart, it);
if(input)
setItemWidget(it, output);
}
setState(QAbstractItemView::NoState);
}
}
void ListWidget::mouseMoveEvent(QMouseEvent *event)
{
setState(QAbstractItemView::DraggingState);
QListWidget::mouseMoveEvent(event);
}
In the following link there is an example.

Moving a QGraphicsRectItem with mouse

I'm trying to move a QGraphicsRectItem after I add it to the scene. It moves, but appears with a certain offset from the mouse pointer. I think it is simply adding the mouse pointer position to its original position. I am at a loss as to how to work around this.
Here is my code:
class ucFilter : public QGraphicsItem {
std::shared_ptr<QGraphicsRectItem> m_rect;
std::shared_ptr<QGraphicsTextItem> m_text;
std::shared_ptr<QString> m_name;
std::shared_ptr<QPointF> m_pos;
QGraphicsItem* selectedItem;
bool m_mouseGrabbed;
public:
static const int default_x = 80, default_y=40;
ucFilter::ucFilter(QString &name, QPointF &pos){
m_name = shared_ptr<QString>(new QString(name));
m_pos = shared_ptr<QPointF>(new QPointF(pos));
m_rect = shared_ptr<QGraphicsRectItem>( new QGraphicsRectItem(pos.x()-default_x, pos.y()-default_y, 2*default_x, 2*default_y ));
m_text = shared_ptr<QGraphicsTextItem>( new QGraphicsTextItem(name));
m_text->setPos(pos.x() - m_text->boundingRect().width()/2, pos.y()- 30);
selectedItem = NULL;
m_mouseGrabbed = false;
}
QGraphicsRectItem* getRect() { return m_rect.get(); }
QGraphicsTextItem* getText() { return m_text.get(); }
QString* getName() { return m_name.get(); }
QPointF* getPos() { return m_pos.get(); }
void setPos(QPointF newPos) { m_pos->setX(newPos.x()); m_pos->setY(newPos.y()); }
QRectF ucFilter::boundingRect() const
{
return m_rect->boundingRect();
}
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
Q_UNUSED(widget);
//QBrush brush(
if (!m_mouseGrabbed){ grabMouse(); m_mouseGrabbed = true; }
}
void mousePressEvent(QGraphicsSceneMouseEvent *event){
selectedItem = this;
QGraphicsItem::mousePressEvent(event);
}
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event){
selectedItem = NULL;
QGraphicsItem::mouseReleaseEvent(event);
}
void mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
if(NULL != selectedItem){
m_text->setPos(event->pos());
m_rect->setPos(event->pos());
}
QGraphicsItem::mouseMoveEvent(event);
}
};
the ucFilter object is created in the scene dropEvent:
void cGraphicsScene::dropEvent(QGraphicsSceneDragDropEvent * event){
QTreeView* source = static_cast<QTreeView*>(event->source());
string name = event->mimeData()->text().toUtf8().constData();
if(0 == name.length()){
event->acceptProposedAction();
return ; // nothing to do anymore
}
QPointF pos = event->scenePos ();
shared_ptr<ucFilter> newFilter = shared_ptr<ucFilter>(new ucFilter(event->mimeData()->text(),event->scenePos ()));
m_filters.push_back(newFilter);
this->addItem(newFilter->getRect());
this->addItem(newFilter->getText());
this->addItem(newFilter.get()); // also add the item to grab mouse events
event->acceptProposedAction();
}
Where could the problem be ?
Here a screenshot of what I'm actually seeing :
I would like the rectangle to be drawn where the mouse is..
You have several issues:
The use of shared pointers to hold everything is completely unwarranted. The scene acts as a container for items - just like QObject is a container for objects.
ucFilter doesn't have children. It holds pointers to other items, but that is unnecessary. The base item can be a rectangle itself, and it can have the text as a child. That way you don't need to handle positioning in a special fashion.
ucFilter can be movable. Don't reimplement that functionality yourself.
When you pass things by reference, pass them as const references unless you are intending to pass the modified value out. If you wish to change the value inside of the body of the function, you can pass it by value instead.
The mouse is already grabbed when you are dragging the item.
Let's start with the ucFilter item. It is really simple and does everything you need. Note that m_text is held by value, and is made a child of the rectangle parent.
// https://github.com/KubaO/stackoverflown/tree/master/questions/graphics-item-drop-32574576
#include <QtWidgets>
class ucFilter : public QGraphicsRectItem {
QGraphicsTextItem m_text;
public:
ucFilter(const QString &name, const QPointF &pos, QGraphicsItem * parent = 0) :
QGraphicsRectItem(parent),
m_text(this)
{
static const QRect defaultRect(0, 0, 160, 80);
setPos(pos);
setRect(QRect(-defaultRect.topLeft()/2, defaultRect.size()));
setFlags(QGraphicsItem::ItemIsMovable);
setName(name);
}
void setName(const QString & text) {
m_text.setPlainText(text);
m_text.setPos(-m_text.boundingRect().width()/2, -30);
}
QString name() const {
return m_text.toPlainText();
}
};
Since we're dropping from a convenience widget (a QListWidget), we need to decode the text from a application/x-qabstractitemmodeldatalist mime type:
const char * kMimeType = "application/x-qabstractitemmodeldatalist";
QVariant decode(const QMimeData* data, Qt::ItemDataRole role = Qt::DisplayRole) {
auto buf = data->data(kMimeType);
QDataStream stream(&buf, QIODevice::ReadOnly);
while (!stream.atEnd()) {
int row, col;
QMap<int, QVariant> map;
stream >> row >> col >> map;
if (map.contains(role)) return map[role];
}
return QVariant();
}
The scene creates an item as soon as a drag enters it, and moves the item during the drag.
Ownership of the items remains with the scene: we don't need to delete any items unless we want to explicitly remove them from the scene. The m_dragItem is used to refer to the currently dragged item simply to move it and add it to m_filters upon completion of the drop. It the drag leaves the scene (or is aborted), the item is simply deleted from the scene.
class cGraphicsScene : public QGraphicsScene {
QList<ucFilter*> m_filters;
ucFilter* m_dragItem;
public:
cGraphicsScene(QObject * parent = 0) : QGraphicsScene(parent), m_dragItem(nullptr) {}
void dragEnterEvent(QGraphicsSceneDragDropEvent * event) Q_DECL_OVERRIDE {
if (!event->mimeData()->hasFormat(kMimeType)) return;
auto name = decode(event->mimeData()).toString();
if (name.isEmpty()) return;
QScopedPointer<ucFilter> filter(new ucFilter(name, event->scenePos()));
addItem(m_dragItem = filter.take());
event->acceptProposedAction();
}
void dragMoveEvent(QGraphicsSceneDragDropEvent * event) Q_DECL_OVERRIDE {
if (!m_dragItem) return;
m_dragItem->setPos(event->scenePos());
event->acceptProposedAction();
}
void dropEvent(QGraphicsSceneDragDropEvent * event) Q_DECL_OVERRIDE {
if (!m_dragItem) return;
m_dragItem->setPos(event->scenePos());
m_filters << m_dragItem;
event->acceptProposedAction();
}
void dragLeaveEvent(QGraphicsSceneDragDropEvent * event) Q_DECL_OVERRIDE {
delete m_dragItem;
m_dragItem = nullptr;
event->acceptProposedAction();
}
};
The test harness is very simple: our scene, a view displaying it, and a list with two items that you can drag onto the scene.
int main(int argc, char ** argv) {
QApplication app{argc, argv};
QWidget w;
cGraphicsScene scene;
QGraphicsView view(&scene);
QListWidget list;
QHBoxLayout l(&w);
l.addWidget(&view);
l.addWidget(&list);
list.setFixedWidth(120);
list.addItem("Item1");
list.addItem("Item2");
list.setDragDropMode(QAbstractItemView::DragOnly);
view.setAcceptDrops(true);
w.resize(500, 300);
w.show();
return app.exec();
}
You can drag items from the list on the right to the scene on the left. You can also move the items within the scene, to relocate them.