paceholder / nodeeditor

Qt Node Editor. Dataflow programming framework
BSD 3-Clause "New" or "Revised" License
3.07k stars 824 forks source link

Help with read access violation when moving model to a widget #394

Closed reubengann closed 1 year ago

reubengann commented 1 year ago

Hope someone can help me, I'm sure I'm doing something wrong. Would appreciate any help someone could lend.

I'm trying to adapt the SimpleGraphModel example. If I run all the code in main, I have no issues. However, when I try to move the setup of the graph into a MainWindow widget, I always get Exception thrown: read access violation in AbstractGraphModel's nodeData method.

This code works:

#include <QtNodes/GraphicsView>
#include <QtNodes/BasicGraphicsScene>
#include <QtGui/QScreen>
#include <QtWidgets/QApplication>
#include <QtWidgets/QCheckBox>
#include <QtWidgets/QGroupBox>
#include <QtWidgets/QVBoxLayout>
#include "SimpleGraphModel.h"

int main(int argc, char **argv)
{
    QApplication app(argc, argv);
    SimpleGraphModel graphModel;
    {
        NodeId id1 = graphModel.addNode();
        graphModel.setNodeData(id1, NodeRole::Position, QPointF(0, 0));

        NodeId id2 = graphModel.addNode();
        graphModel.setNodeData(id2, NodeRole::Position, QPointF(300, 300));

        graphModel.addConnection(ConnectionId{ id1, 0, id2, 0 });
    }
    auto scene = new QtNodes::BasicGraphicsScene(graphModel);
    QWidget window;
    QVBoxLayout* l = new QVBoxLayout(&window);
    QtNodes::GraphicsView view(scene);
    l->addWidget(&view);
    QGroupBox* groupBox = new QGroupBox("Options");
    QCheckBox* cb1 = new QCheckBox("Checkbox 1");
    QCheckBox* cb2 = new QCheckBox("Checkbox 2");
    cb2->setChecked(true);
    QVBoxLayout* vbl = new QVBoxLayout;
    vbl->addWidget(cb1);
    vbl->addWidget(cb2);
    vbl->addStretch();
    groupBox->setLayout(vbl);
    view.setContextMenuPolicy(Qt::ActionsContextMenu);
    QAction createNodeAction(QStringLiteral("Create Node"), &view);
    QObject::connect(&createNodeAction, &QAction::triggered, [&]() {
        QPointF posView = view.mapToScene(view.mapFromGlobal(QCursor::pos()));
        NodeId const newId = graphModel.addNode();
        graphModel.setNodeData(newId, NodeRole::Position, posView);
        });
    view.insertAction(view.actions().front(), &createNodeAction);
    l->addWidget(groupBox);
    window.resize(800, 600);
    window.move(QApplication::primaryScreen()->availableGeometry().center() - view.rect().center());
    window.showNormal();
    return app.exec();
}

but this does not

#include <QtNodes/GraphicsView>
#include <QtNodes/BasicGraphicsScene>
#include <QtGui/QScreen>
#include <QtWidgets/QApplication>
#include <QtWidgets/QCheckBox>
#include <QtWidgets/QGroupBox>
#include <QtWidgets/QVBoxLayout>
#include "SimpleGraphModel.h"

class MainWindow : public QWidget {
public:
    MainWindow() {
        SimpleGraphModel graphModel;
        {
            NodeId id1 = graphModel.addNode();
            graphModel.setNodeData(id1, NodeRole::Position, QPointF(0, 0));

            NodeId id2 = graphModel.addNode();
            graphModel.setNodeData(id2, NodeRole::Position, QPointF(300, 300));

            graphModel.addConnection(ConnectionId{ id1, 0, id2, 0 });
        }
        auto scene = new QtNodes::BasicGraphicsScene(graphModel);
        QWidget window;
        QVBoxLayout* l = new QVBoxLayout(&window);
        QtNodes::GraphicsView view(scene);
        l->addWidget(&view);
        QGroupBox* groupBox = new QGroupBox("Options");
        QCheckBox* cb1 = new QCheckBox("Checkbox 1");
        QCheckBox* cb2 = new QCheckBox("Checkbox 2");
        cb2->setChecked(true);
        QVBoxLayout* vbl = new QVBoxLayout;
        vbl->addWidget(cb1);
        vbl->addWidget(cb2);
        vbl->addStretch();
        groupBox->setLayout(vbl);
        view.setContextMenuPolicy(Qt::ActionsContextMenu);
        QAction createNodeAction(QStringLiteral("Create Node"), &view);
        QObject::connect(&createNodeAction, &QAction::triggered, [&]() {
            QPointF posView = view.mapToScene(view.mapFromGlobal(QCursor::pos()));
            NodeId const newId = graphModel.addNode();
            graphModel.setNodeData(newId, NodeRole::Position, posView);
            });
        view.insertAction(view.actions().front(), &createNodeAction);
        l->addWidget(groupBox);
    }

    ~MainWindow() {};
};

int main(int argc, char **argv)
{
    QApplication app(argc, argv);
    MainWindow *window = new MainWindow;
    window->resize(800, 600);
    window->showNormal();
    return app.exec();
}
reubengann commented 1 year ago

Closing this since I solved it myself. In case anybody in the future has the same question:

#include <QtNodes/GraphicsView>
#include <QtNodes/BasicGraphicsScene>
#include <QtGui/QScreen>
#include <QtWidgets/QApplication>
#include <QtWidgets/QCheckBox>
#include <QtWidgets/QGroupBox>
#include <QtWidgets/QVBoxLayout>
#include "SimpleGraphModel.h"

class NodeGraph {
public:
    SimpleGraphModel graphModel;
    QtNodes::GraphicsView* view;
    QAction* createNodeAction;
    NodeGraph()
    {
        {
            NodeId id1 = graphModel.addNode();
            graphModel.setNodeData(id1, NodeRole::Position, QPointF(0, 0));

            NodeId id2 = graphModel.addNode();
            graphModel.setNodeData(id2, NodeRole::Position, QPointF(300, 300));

            graphModel.addConnection(ConnectionId{ id1, 0, id2, 0 });
        }
        auto scene = new QtNodes::BasicGraphicsScene(graphModel);
        view = new QtNodes::GraphicsView(scene);
        view->setContextMenuPolicy(Qt::ActionsContextMenu);
        createNodeAction = new QAction(QStringLiteral("Create Node"), view);
        QObject::connect(createNodeAction, &QAction::triggered, [&]() {
            QPointF posView = view->mapToScene(view->mapFromGlobal(QCursor::pos()));
            NodeId const newId = graphModel.addNode();
            graphModel.setNodeData(newId, NodeRole::Position, posView);
            });
        view->insertAction(view->actions().front(), createNodeAction);
    }
};

class MainWindow : public QWidget
{
public:
    QVBoxLayout* layout;
    NodeGraph* graph;
    QGroupBox* groupBox;
    QCheckBox* cb1;
    QCheckBox* cb2;
    MainWindow()
    {
        layout = new QVBoxLayout(this);
        graph = new NodeGraph();
        layout->addWidget(graph->view);
        groupBox = new QGroupBox("Options");
        cb1 = new QCheckBox("Nodes are locked");
        cb2 = new QCheckBox("Connections detachable");
        cb2->setChecked(true);
        QVBoxLayout* vbl = new QVBoxLayout;
        vbl->addWidget(cb1);
        vbl->addWidget(cb2);
        vbl->addStretch();
        groupBox->setLayout(vbl);
        layout->addWidget(groupBox);
    }
};

int main(int argc, char **argv)
{
    QApplication app(argc, argv);
    MainWindow window;

    window.resize(800, 600);
    window.showNormal();
    return app.exec();
}