【问题标题】:Drag and drop of QGraphicsItem between two pairs of QGraphicsScene/QGraphicsView在两对QGraphicsScene/QGraphicsView之间拖放QGraphicsItem
【发布时间】:2013-06-17 11:22:30
【问题描述】:

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

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

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

现在我正在尝试使用this 在两个场景/窗口之间进行implement drag and drop,我得到的效果与this 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!";
}

根据那里的答案,我必须在 QGraphicsScene 中使用 setAcceptDrops()(这是不可能的),所以诀窍似乎是重载 QGraphicsScene::dragMoveEvent()。因为我的 QGraphicsScene 没有特定的类(只是它的父 DiagramWindow),我不知道如何编写一个函数来定位 scene'具体dragMoveEvent()

问题 1 我希望我能做这样的事情:

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

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

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

【问题讨论】:

    标签: qt drag-and-drop qgraphicsview qgraphicsscene


    【解决方案1】:

    问题1

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

    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 是什么,但这里有一个工作示例,它应该为您提供所有必要的想法,使其适用于您的情况。我建议您从这个工作示例开始,并对其进行修改以获得您需要的内容。

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

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

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

    【讨论】:

    • 更多信息问题 2 含义:stackoverflow.com/questions/357307/…
    • 您对第一个问题的回复不起作用,因为 QGraphicsScene 的 dragMoveEvent 需要 QGraphicsSceneDragDropEvent 类型的事件,而 DiagramWindow 的 dragMoveEventQDragEnterEvent 类型。所以似乎需要某种 conversion 。有什么想法吗?
    • 好的,确实,我检查了文档。所以你要做的是调用view-&gt;dragMoveEvent(),这将进行转换(从鼠标坐标转换为场景坐标),并重定向到其附加的QGraphicsScene。
    • 好的,没错。只需将其公开 ;-) 如果您使用直接的 QGraphicsView,这意味着使用派生类代替,您可以在其中重新实现事件方法,但将其声明为公开而不是受保护。在重新实现中,您只需按照我的答案第二部分中的说明调用基本方法,而不使用“if”。或者,您可以保持方法受保护,但将 DiagramWindow 声明为继承 QGraphicsView 的类的朋友类。
    • @Joum:不,你绝对不需要自己重新实现,这并不难。我有一个工作示例,可以让您了解如何做。希望有帮助:)
    猜你喜欢
    • 2013-10-13
    • 2012-02-02
    • 2011-08-04
    • 2012-04-09
    • 2017-02-16
    • 2013-07-30
    • 2020-08-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多