4

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

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

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

现在我正在尝试使用在两个场景/窗口之间实现拖放,并且我得到了我认为与此 SO question 中相似/相同的效果。基本上,当我将 QGraphicsItem 从第二个窗口/场景拖到主窗口时,它不会触发场景中的事件,它会在主窗口的工具栏/边框中触发。

我的事件处理功能是:

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

    event->acceptProposedAction();
}

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

根据那里的答案,我必须setAcceptDrops()在 QGraphicsScene 中(这是不可能的),所以诀窍似乎是重载QGraphicsScene::dragMoveEvent(). 由于我的 QGraphicsScene 没有特定的类(只是它的父DiagramWindow),我不知道如何编写一个函数来定位场景的特定dragMoveEvent().

问题 1我希望我能做类似的事情:

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

但这当然是不可能的。我对 C++/Qt 真的很陌生,一般的工作流程/语法动态仍然使我难以理解。如何以 MainWindow 中的 QGraphicsScene 为目标来编写事件处理函数?

问题 2另外,我注意到通过重写这些事件处理函数,我(显然)失去了我在主窗口中的大部分功能 - 选择和移动 QGraphicsItems 不再起作用。无论如何,只有当事件起源于第二个窗口时,我才能使这些事件触发?我看过,QDrag->source()但我也不知道它是如何工作的——比如,如果事件起源于第二个窗口,就这样做,否则,继续做你以前做的事情——我实际上不知道是什么。 .. :)

4

1 回答 1

5

问题1

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

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

问题2

对 Drag 事件了解不多,因此无能为力,但是要根据 if 语句获得先前的行为,您应该这样做:

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

假设您的类MyDerivedClass(在您的情况下,DiagramWindow)继承自 Qt 类QBaseClass(在您的情况下,QMainWindow?),并且您要覆盖的事件方法是myEvent()(在您的情况下,dragMoveEvent)。

工作示例:

我不确切地知道您的类 DiagramWindow 是什么,但这是一个工作示例,它应该为您提供所有必要的想法,以使其适用于您的情况。我建议您从这个工作示例开始,并对其进行修改以获得您需要的内容。

主.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();
}

场景.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

场景.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);
}

视图.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

查看.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);
}

在您的情况下,如果您需要view->someDragDropEvent(event)从您的 显式调用DiagramWindow,那么您只需将 更改protected:public:。但我认为没有必要,只需尝试重新实现中的拖放事件DiagramWindow,它应该会自动调用您的视图之一。

于 2013-06-21T11:13:07.900 回答