1

I'm trying to implement Drag and Drop behavior, across my whole application, I would like to execute some action when a drop occurs, no mater where in the MainWindow. The Problem that I'm facing is that in my MainWindow, I have a Widget which contains a QGraphicsView, which again contains a QGraphicsScene, I can detect the drop anywhere in the application via EventFilter, except in the in the View and Scene. Is it possible to achieve some kind of global drag and drop behavior which I can detect from the MainWindow? I'm trying to avoid spreading the Logic across all my visible Widgets.

This is the drag and drop logic in my MainWindow, works, as said, for all drag and drops that happen not over the View/Scene:

void MainWindow::dropEvent(QDropEvent *event)
{
    auto pixmap = QPixmap(event->mimeData()->urls().first().toString().remove("file://"));
    if(!pixmap.isNull()) {
        load(pixmap);
    }

    event->acceptProposedAction();
}

void MainWindow::dragEnterEvent(QDragEnterEvent *event)
{
    if (event->mimeData()->hasUrls()) {
        event->acceptProposedAction();
    }
}

For the drag and drops over the View/Scene I've tried to install following event filter:

bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
    if(event->type() == QEvent::DragEnter) {
        qDebug("Enter");
    } else if(event->type() == QEvent::Drop) {
        qDebug("Drop");
    } else if(event->type() == QEvent::GraphicsSceneDrop)   {
        qDebug("Scene drop");
    } else if (event->type() >= 159 && event->type() <= 168) {
        qDebug("Any Scene Event");
    }
    return QObject::eventFilter(obj, event);
}

And apply it in the MainWindow ctor, the only thing that I detect is an enter when it enters the view.

...
setAcceptDrops(true);
mChildWidget->installEventFilter(this);
mChildWidget->setAcceptDrops(true);
...
Damir Porobic
  • 681
  • 1
  • 8
  • 21
  • Without a [mcve] I can only speculate but... you might want to try installing the event filter on the viewport of the `QGraphicsView`: `graphics_view->viewport()->installEventFilter(filter)`. – G.M. Apr 04 '20 at 10:03
  • @G.M. Doesn't that mean that I would need to install the filter in two places, once under the view and once in the MainWindow? Is there a way to delegate the filter from the parent to the child? – Damir Porobic Apr 04 '20 at 11:42
  • You need to install the filter on all `QObject`s whose events you want to filter. Generally speaking events propagate from the child to the parent -- not the other way round. – G.M. Apr 04 '20 at 14:34
  • Ok, does that mean that an event, which is not consumed by the child, should propagate to the parent? Should that work up to the first widget, in my case the MainWindow? Is for that required to set the parent QWidget in the ctor when creating child widgets? – Damir Porobic Apr 04 '20 at 17:54

1 Answers1

1

It looks like I was facing two issues here. The first one is explained in this question here Accepting drops on a QGraphicsScene. The QGraphicsScene ignores DragAndDrops that are not happening over items. Basically you can enable drag and drop but only for evey item individually, which still leaves the arae that is not covered by an item. The solution to this issue seems to be to overwrite dragMoveEvent

void MyQGprahicsScene::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
{
    Q_UNUSED(event)

    // Overriding default dragMoveEvent in order to accept drops without items under them
}

The second issue was that i was facing is that I was trying to apply the eventFilter to Child widget, but it looks like I had to apply it to qApp, after which everything seemed to work, probably because qApp is the top QObject where all events will land when not handled.

#include "DragAndDropHandler.h"

bool DragAndDropHandler::eventFilter(QObject *obj, QEvent *event)
{
    if(event->type() == QEvent::DragEnter) {
        handleDragEnter(dynamic_cast<QDragEnterEvent *>(event));
    } else if(event->type() == QEvent::Drop) {
        handleDrop(dynamic_cast<QDropEvent *>(event));
    } else if(event->type() == QEvent::GraphicsSceneDrop) {
        handleDrop(dynamic_cast<QGraphicsSceneDragDropEvent *>(event));
    } else if (event->type() ==  QEvent::GraphicsSceneDragEnter) {
        handleDragEnter(dynamic_cast<QGraphicsSceneDragDropEvent *>(event));
    }
    return QObject::eventFilter(obj, event);
}

void DragAndDropHandler::handleDragEnter(QDragEnterEvent *event)
{
    if (event->mimeData()->hasUrls()) {
        event->acceptProposedAction();
    }
}

void DragAndDropHandler::handleDragEnter(QGraphicsSceneDragDropEvent *event)
{
    if (event->mimeData()->hasUrls()) {
        event->acceptProposedAction();
    }
}

void DragAndDropHandler::handleDrop(QDropEvent *event)
{
    auto path = getUrlFromMimeData(event->mimeData());
    event->acceptProposedAction();
    emit imageDropped(path);
}

void DragAndDropHandler::handleDrop(QGraphicsSceneDragDropEvent *event)
{
    auto path = getUrlFromMimeData(event->mimeData());
    event->acceptProposedAction();
    emit imageDropped(path);
}

QString DragAndDropHandler::getUrlFromMimeData(const QMimeData *mimeData) const
{
    return mimeData->urls().first().toString().remove("file://");
}

And, in the constructor of my MainWindow:

setAcceptDrops(true);
qApp->installEventFilter(mDragAndDropHandler);
connect(mDragAndDropHandler, &DragAndDropHandler::imageDropped, this, &MainWindow::loadImageFromFile);
Damir Porobic
  • 681
  • 1
  • 8
  • 21