4
votes

I have an application with a mainwindow in which I create a QGraphicsScene like this:

DiagramWindow::DiagramWindow()
{
    scene = new QGraphicsScene(0, 0, 600, 500);

Then on the same program, I'm calling another window with another (different)QGraphicsScene. Both of these scenes have their respective QGraphicsViews and I use the same custom class to draw QGraphicsItem in each scene/window.

Now I'm trying to implement drag and drop between the two scenes/windows using this and I'm getting an effect that I think is similar/the same as in this SO question . Basically, when I drag a QGraphicsItem from the second window/scene to the main window, it does not trigger the event on the scene, BUT it does trigger in in the main window's toolbar/ borders.

My event handling functions are:

void DiagramWindow::dragEnterEvent(QDragEnterEvent *event)
{
    qDebug() << "I'm on the main window!";

    event->acceptProposedAction();
}

and

void DiagramWindow::dropEvent(QDropEvent *event)
{
    event->acceptProposedAction();
    qDebug() << "got a drop!";
}

According to the answers there, I would have to setAcceptDrops() in a QGraphicsScene (which is not possible), so the trick seems to be to overload the QGraphicsScene::dragMoveEvent(). Since I don't have a specific class for my QGraphicsScene (just for it's parent DiagramWindow), I don't know how I can write a function to target the scene's specific dragMoveEvent().

QUESTION 1 I was hoping I could do something like:

DiagramWindow->scene::dragMoveEvent()
{
    ...
}

But of course this is not possible. I'm really new to C++/Qt and the general workflow/syntax dynamics still ellude me. How can I target the QGraphicsScene inside my MainWindow to write the event handling function?

QUESTION 2 Also, I noticed that by rewriting these event handling functions, I (obviously) lost most of the funcionality I had in the main window - selecting and moving around the QGraphicsItems no longer works. Is there anyway I can make these events trigger only if the events are being originated in the second window? I have looked at QDrag->source() but I'm not getting how it works either - something like, if the events originate in the second window, do this, else, keep doing what you were doing before - which I don't actually know what is... :)

1

1 Answers

5
votes

Question1

If the event is received by the diagramWindow, and want it receive by the scene which is currently displayed by a view, then what you should do is pass the event to the view, that will convert it to a QGraphicsSceneDragDropEvent and redirect it to the scene:

void DiagramWindow::dragMoveEvent(event)
{
    view->dragMoveEvent(event);
}

Question 2

Don't know much about Drag event so can't help, but to get the previous behaviour depending on a if statement, you should do:

void MyDerivedClass::myEvent(event)
{
    if(...)
        // do specific behaviour
    else
        QBaseClass::myEvent(event); // default behaviour
}

assuming your class MyDerivedClass(in your case, DiagramWindow) inherits from the Qt class QBaseClass (in your case, QMainWindow?), and the event method you want to override is myEvent() (in your case, dragMoveEvent).

Working example:

I don't know exacly what your class DiagramWindow is, but here is a working example that should give you all the necessary ideas to make it work in your case. I suggest you start from this working example, and modify it to get what you need.

main.cpp:

#include <QApplication>

#include "Scene.h"
#include "View.h"

int main(int argc, char * argv[])
{
    QApplication app(argc,argv);

    Scene * scene1 = new Scene(1);
    View * view1 = new View(scene1, 1);

    Scene * scene2 = new Scene(2);
    View * view2 = new View(scene2,2);

    view1->show();
    view2->show();
    return app.exec();
}

Scene.h:

#ifndef SCENE_H
#define SCENE_H

#include <QGraphicsScene>
#include <QGraphicsEllipseItem>

class Item;

class Item: public QGraphicsEllipseItem
{
public:
    Item(int x,int y);

protected:
    void mousePressEvent(QGraphicsSceneMouseEvent *event);
};


class Scene: public QGraphicsScene
{
public:
    Scene(int i);

protected:
    virtual void dragEnterEvent ( QGraphicsSceneDragDropEvent * event );
    virtual void dragLeaveEvent ( QGraphicsSceneDragDropEvent * event );
    virtual void dragMoveEvent ( QGraphicsSceneDragDropEvent * event );
    virtual void dropEvent ( QGraphicsSceneDragDropEvent * event );

    int i;
};

#endif

Scene.cpp:

#include "Scene.h"

#include <QtDebug>
#include <QGraphicsSceneMouseEvent>
#include <QDrag>
#include <QMimeData>

Item::Item(int x, int y) : QGraphicsEllipseItem(x,y,50,50) {}

void Item::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    qDebug() << "item mouse press";

    // Create the mime  data that will be transfered  from one scene
    // to another
    QMimeData * mimeData = new QMimeData;

    // In our case, the data will be the address of the item.
    //
    // Note: This is  UNSAFE, and just for the  sake of example. The
    // good way to do it is to create your own mime type, containing
    // all the information necessary to recreate an identical Item.
    // 
    // This  is because  drag  and  drop is  meant  to work  between
    // applications, and the address  of your item is not accessible
    // by  other  applications   (deferencing  it  would  produce  a
    // segfault). It  works fine  in this case  since you  perform a
    // drag  and   drop  between  different  windows   of  the  same
    // application.
    Item * item = this;
    QByteArray byteArray(reinterpret_cast<char*>(&item),sizeof(Item*));
    mimeData->setData("Item",byteArray);

    // start the event
    QDrag * drag = new QDrag(event->widget());
    drag->setMimeData(mimeData);
    drag->start();
}

Scene::Scene(int i) : i(i)
{
    Item * item = new Item(100+100*i,100);
    addItem(item);
}           

void Scene::dragEnterEvent ( QGraphicsSceneDragDropEvent * event )
{
    qDebug() << "scene" << i << "drag enter"; 
}

void Scene::dragLeaveEvent ( QGraphicsSceneDragDropEvent * event )
{
    qDebug() << "scene" << i << "drag leave"; 
}

void Scene::dragMoveEvent ( QGraphicsSceneDragDropEvent * event )
{
    qDebug() << "scene" << i << "drag move";
}


void Scene::dropEvent ( QGraphicsSceneDragDropEvent * event )
{
    qDebug() << "scene" << i << "drop";

    // retrieve the address of the item from the mime data
    QByteArray byteArray = event->mimeData()->data("Item");
    Item * item = *reinterpret_cast<Item**>(byteArray.data());

    // add the item  to the scene (automatically remove  it from the
    // other scene)
    addItem(item);
}

View.h:

#ifndef VIEW_H
#define VIEW_H

#include <QGraphicsView>
#include "Scene.h"

class View: public QGraphicsView
{
public:
    View(Scene * scene, int i);

protected:
    virtual void dragEnterEvent ( QDragEnterEvent * event );
    virtual void dragLeaveEvent ( QDragLeaveEvent * event );
    virtual void dragMoveEvent ( QDragMoveEvent * event );
    virtual void dropEvent ( QDropEvent * event );

private:
    Scene * scene_;
    int i;
};

#endif

View.cpp:

#include "View.h"
#include <QtDebug>

View::View(Scene * scene, int i) :
    QGraphicsView(scene),
    scene_(scene),
    i(i)
{
}

void View::dragEnterEvent ( QDragEnterEvent * event )
{
    qDebug() << "view" << i << "drag enter";
    QGraphicsView::dragEnterEvent(event);
}

void View::dragLeaveEvent ( QDragLeaveEvent * event )
{
    qDebug() << "view" << i <<"drag leave";
    QGraphicsView::dragLeaveEvent(event);
}

void View::dragMoveEvent ( QDragMoveEvent * event )
{
    qDebug() << "view" << i << "drag move";
    QGraphicsView::dragMoveEvent(event);
}

void View::dropEvent ( QDropEvent * event )
{
    qDebug() << "view" << i << "drop";
    QGraphicsView::dropEvent(event);
}

In your case, if you need to explicitly call view->someDragDropEvent(event) from your DiagramWindow, then you just have to change the protected: to public:. But I don't think it is necessary, just try without reimplementing the drag and drop event in DiagramWindow, and it should automatically call the one of your view.