在两对QGraphicsScene/QGraphicsView之间拖放QGraphicsItem。

时间:2023-02-03 08:00:06

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

我有一个主窗口的应用程序,在其中我创建了一个QGraphicsScene:

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.

然后在同一个程序中,我调用另一个窗口与另一个(不同的)QGraphicsScene。这两个场景都有各自的qgraphicsview,我使用同一个自定义类在每个场景/窗口中绘制QGraphicsItem。

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.

现在,我尝试在两个场景/窗口之间实现拖放操作,我得到了一个我认为与这个问题相似的效果。基本上,当我将一个QGraphicsItem从第二个窗口/场景拖动到主窗口时,它不会触发场景中的事件,但是它会触发主窗口的工具栏/边框。

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().

根据这里的答案,我必须在一个QGraphicsScene(不可能)中设置acceptdrop(),所以这个技巧似乎是重载QGraphicsScene::dragMoveEvent()。由于我没有为我的QGraphicsScene设置一个特定的类(仅为它的父图窗口),我不知道如何编写一个函数来针对场景的特定dragMoveEvent()。

QUESTION 1 I was hoping I could do something like:

问题1我希望我能做点什么:

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?

当然,这是不可能的。我对c++ /Qt真的很陌生,但我仍然无法理解一般的工作流/语法动态。如何在主窗口内锁定QGraphicsScene,以写入事件处理函数?

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... :)

问题2,我注意到,通过重写这些事件处理函数,我(很明显)失去了在主窗口中所拥有的大多数功能——选择和在QGraphicsItems中移动不再有效。如果事件发生在第二个窗口中,我是否可以让这些事件触发?我看了QDrag->源代码(),但我也不知道它是如何工作的——比如,如果事件发生在第二个窗口,那么就这样做,否则,继续做你以前做过的事情——我实际上不知道什么是……:)

1 个解决方案

#1


5  

Question1

问题1

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:

如果该事件是由diagramWindow接收的,并希望它接收当前视图显示的场景,那么您应该做的是将事件传递给视图,该视图将它转换为QGraphicsSceneDragDropEvent并将其重定向到场景:

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

Question 2

问题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:

对于拖拽事件不太了解,因此无法帮助,但是要根据if语句获得之前的行为,您应该这样做:

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).

假设您的类MyDerivedClass(在您的情况下,DiagramWindow)继承了Qt类QBaseClass(在您的案例中是QMainWindow?),您想要重写的事件方法是myEvent()(在您的例子中,是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:

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:

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:

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:

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:

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.

在您的案例中,如果您需要显式地调用视图—从您的图表窗口中显示的某个dragdropevent(事件),那么您只需更改受保护的:到public:。但我不认为有必要,只需尝试在DiagramWindow中重新实现拖放事件,它就会自动调用您的视图。

#1


5  

Question1

问题1

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:

如果该事件是由diagramWindow接收的,并希望它接收当前视图显示的场景,那么您应该做的是将事件传递给视图,该视图将它转换为QGraphicsSceneDragDropEvent并将其重定向到场景:

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

Question 2

问题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:

对于拖拽事件不太了解,因此无法帮助,但是要根据if语句获得之前的行为,您应该这样做:

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).

假设您的类MyDerivedClass(在您的情况下,DiagramWindow)继承了Qt类QBaseClass(在您的案例中是QMainWindow?),您想要重写的事件方法是myEvent()(在您的例子中,是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:

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:

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:

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:

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:

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.

在您的案例中,如果您需要显式地调用视图—从您的图表窗口中显示的某个dragdropevent(事件),那么您只需更改受保护的:到public:。但我不认为有必要,只需尝试在DiagramWindow中重新实现拖放事件,它就会自动调用您的视图。