QNodesEditor – Qt nodes/ports-based data processing flow editor

In case you ever wanted to use nodes-based editor in Qt, I think I’ve got just the right thing for you. I tried to make sure that no similar solution already existed. Now, briefly about the software I’ve stumbled upon and what is wrong with it in my opinion:

1) http://openassembler.wordpress.com/ – requires Python and PyQt which excludes pure C++ development – this doesn’t suit my needs
2) https://github.com/nical/kiwi – originally written in D (look above); C++ version is pretty much work in progress, and it’s a lot more complicated than QNodesEditor – among other things it imposes conventions intended for nodes-based processing with regards to data containers, ready states, etc. etc. – complicates things unnecessarily IMHO. UPDATE: After taking another look at kiwi I discovered it actually contains a very simple standalone node editor called Qiwi – it’s quite all-right, but doesn’t support port names and saving/loading.
UPDATE: 3) http://nukengine.com/qt-node-editor/ – this one was actually the closest to meet my expectations although I found the necessity to create GUI elements for nodes/ports myself and then attach logical nodes/ports classes to them to be bothersome. It’s a cool project though and I really like it, especially smart connections alignment.

QNodesEditor’s hallmark is being as simple as it gets. Blocks are just blocks, I don’t decide what they should be used for, maybe you just want them to be there and look pretty – it’s your choice. If you want to implement some processing logic – it’s up to you. New types of blocks can even be created completely dynamically in runtime, you don’t have to subclass anything. QNodesEditor is primarily a GUI tool.

Blocks consist of named input and output ports. Ports can have additional flags, currently just NamePort and TypePort (ports that cannot be used for connections, they’re just there to display some extra text with special formatting on the block – yes, you can think of it as an ugly hack). QNodesEditor supports saving and loading of node diagrams. Again – all data are saved as-is (names, connections, positions) without any introspection. It’s up to you to interpret the graphical schematic, attach extra data to it, etc. Blocks and connections are saved in the same order as they’re placed in the QGraphicsScene, so it seems natural to reference them by index when saving your application-specific data.

A short example:

    QGraphicsScene *s = new QGraphicsScene();
    ui->graphicsView->setScene(s);
    ui->graphicsView->setRenderHint(QPainter::Antialiasing);

    QNEBlock *b = new QNEBlock(0, s);
    b->addPort("test", 0, QNEPort::NamePort);
    b->addPort("TestBlock", 0, QNEPort::TypePort);
    b->addInputPort("in1");
    b->addInputPort("in2");
    b->addInputPort("in3");
    b->addOutputPort("out1");
    b->addOutputPort("out2");
    b->addOutputPort("out3");

    b = b->clone();
    b->setPos(150, 0);

    b = b->clone();
    b->setPos(150, 150);

    nodesEditor = new QNodesEditor(this);
    nodesEditor->install(s);

As you can see, it can hardly be any easier to set up a nodes-based editor. After creating a block “template” and cloning it several times to different positions in the scene, you just install QNodesEditor onto QGraphicsScene and voila, your QGraphicsScene is now working as a fully-fledged node editor. Left click and drag on nodes to move them around. Do the same with ports to create connections. Right click nodes and connections to delete them. You can do multiple selection by left-clicking with Ctrl pressed.

If you want to save your diagram, it cannot be any simpler:

QFile f("test.nodes");
f.open(QFile::WriteOnly);
QDataStream ds(&f);
nodesEditor->save(ds);

Want to load them back? No sweat:

QFile f("test.nodes");
f.open(QFile::ReadOnly);
QDataStream ds(&f);
nodesEditor->load(ds);

The code is BSD-licensed. You can use it as-is and develop application-specific stuff completely separately or just modify the code to suit your needs, since it’s really lightweight and easy to understand. Anyway – good luck and enjoy! Don’t forget to drop some feedback in case you find it useful (or not 😉 )!

Cheers!

DEMO: qnodeseditor_demo.zip
SOURCE CODE: qnodeseditor_src.zip
Qt5-compatible SOURCE CODE (by Carter): qnodeseditor-qt5.zip

27 Comments

  • hi!

    fantastic stuff. this is _exactly_ what i’m looking for since months. do you know if this is going to work in Qt 4.5.x or Qt 4.8 wihtout heavy modifications? need to support esp. those versions, and sadly it seems not to compile with both right away (need to say, that i’m no experienced Qt coder, so the answer might be obvious to the pro 😉

    anyway – thanks for sharing this!

  • additional note: it is working in Qt 4.8.x – my fault. for 4.5.x it seems to be only necessary to replace the ItemScenePositionHasChanged() – which is not available there – against something more basic; what in fact shouldn’t be very complicated.

    thanks again for this great piece of code!

  • in QT 5.0 I get the following error:

    1>—— Build started: Project: test, Configuration: Debug Win32 ——
    1>Build started 04/03/2013 15:04:33.
    1>InitializeBuildStatus:
    1> Touching “Debugtest.unsuccessfulbuild”.
    1>CustomBuild:
    1> All outputs are up-to-date.
    1>ClCompile:
    1> qneblock.cpp
    1>……….Desktopqnodeseditor_srcqnodeseditorqneblock.cpp(36): error C2664: ‘QGraphicsPathItem::QGraphicsPathItem(const QPainterPath &,QGraphicsItem *)’ : cannot convert parameter 1 from ‘QGraphicsItem *’ to ‘const QPainterPath &’
    1> Reason: cannot convert from ‘QGraphicsItem *’ to ‘const QPainterPath’
    1> No constructor could take the source type, or constructor overload resolution was ambiguous
    1>……….Desktopqnodeseditor_srcqnodeseditorqneblock.cpp(72): error C3861: ‘children’: identifier not found
    1>……….Desktopqnodeseditor_srcqnodeseditorqneblock.cpp(72): error C2227: left of ‘->condition’ must point to class/struct/union/generic type
    1>……….Desktopqnodeseditor_srcqnodeseditorqneblock.cpp(72): error C3861: ‘children’: identifier not found
    1>……….Desktopqnodeseditor_srcqnodeseditorqneblock.cpp(72): error C2227: left of ‘->i’ must point to class/struct/union/generic type
    1>……….Desktopqnodeseditor_srcqnodeseditorqneblock.cpp(72): error C3861: ‘children’: identifier not found
    1>……….Desktopqnodeseditor_srcqnodeseditorqneblock.cpp(72): error C2227: left of ‘->i’ must point to class/struct/union/generic type
    1>……….Desktopqnodeseditor_srcqnodeseditorqneblock.cpp(72): error C3861: ‘children’: identifier not found
    1>……….Desktopqnodeseditor_srcqnodeseditorqneblock.cpp(72): error C2227: left of ‘->brk’ must point to class/struct/union/generic type
    1>……….Desktopqnodeseditor_srcqnodeseditorqneblock.cpp(72): error C3861: ‘children’: identifier not found
    1>……….Desktopqnodeseditor_srcqnodeseditorqneblock.cpp(72): error C2227: left of ‘->brk’ must point to class/struct/union/generic type
    1>……….Desktopqnodeseditor_srcqnodeseditorqneblock.cpp(72): error C3861: ‘children’: identifier not found
    1>……….Desktopqnodeseditor_srcqnodeseditorqneblock.cpp(115): error C3861: ‘children’: identifier not found
    1>……….Desktopqnodeseditor_srcqnodeseditorqneblock.cpp(115): error C2227: left of ‘->condition’ must point to class/struct/union/generic type
    1>……….Desktopqnodeseditor_srcqnodeseditorqneblock.cpp(115): error C3861: ‘children’: identifier not found
    1>……….Desktopqnodeseditor_srcqnodeseditorqneblock.cpp(115): error C2227: left of ‘->i’ must point to class/struct/union/generic type
    1>……….Desktopqnodeseditor_srcqnodeseditorqneblock.cpp(115): error C3861: ‘children’: identifier not found
    1>……….Desktopqnodeseditor_srcqnodeseditorqneblock.cpp(115): error C2227: left of ‘->i’ must point to class/struct/union/generic type
    1>……….Desktopqnodeseditor_srcqnodeseditorqneblock.cpp(115): error C3861: ‘children’: identifier not found
    1>……….Desktopqnodeseditor_srcqnodeseditorqneblock.cpp(115): error C2227: left of ‘->brk’ must point to class/struct/union/generic type
    1>……….Desktopqnodeseditor_srcqnodeseditorqneblock.cpp(115): error C3861: ‘children’: identifier not found
    1>……….Desktopqnodeseditor_srcqnodeseditorqneblock.cpp(115): error C2227: left of ‘->brk’ must point to class/struct/union/generic type
    1>……….Desktopqnodeseditor_srcqnodeseditorqneblock.cpp(115): error C3861: ‘children’: identifier not found
    1>……….Desktopqnodeseditor_srcqnodeseditorqneblock.cpp(125): error C3861: ‘children’: identifier not found
    1>……….Desktopqnodeseditor_srcqnodeseditorqneblock.cpp(125): error C2227: left of ‘->condition’ must point to class/struct/union/generic type
    1>……….Desktopqnodeseditor_srcqnodeseditorqneblock.cpp(125): error C3861: ‘children’: identifier not found
    1>……….Desktopqnodeseditor_srcqnodeseditorqneblock.cpp(125): error C2227: left of ‘->i’ must point to class/struct/union/generic type
    1>……….Desktopqnodeseditor_srcqnodeseditorqneblock.cpp(125): error C3861: ‘children’: identifier not found
    1>……….Desktopqnodeseditor_srcqnodeseditorqneblock.cpp(125): error C2227: left of ‘->i’ must point to class/struct/union/generic type
    1>……….Desktopqnodeseditor_srcqnodeseditorqneblock.cpp(125): error C3861: ‘children’: identifier not found
    1>……….Desktopqnodeseditor_srcqnodeseditorqneblock.cpp(125): error C2227: left of ‘->brk’ must point to class/struct/union/generic type
    1>……….Desktopqnodeseditor_srcqnodeseditorqneblock.cpp(125): error C3861: ‘children’: identifier not found
    1>……….Desktopqnodeseditor_srcqnodeseditorqneblock.cpp(125): error C2227: left of ‘->brk’ must point to class/struct/union/generic type
    1>……….Desktopqnodeseditor_srcqnodeseditorqneblock.cpp(125): error C3861: ‘children’: identifier not found
    1> qneconnection.cpp
    1>……….Desktopqnodeseditor_srcqnodeseditorqneconnection.cpp(35): error C2664: ‘QGraphicsPathItem::QGraphicsPathItem(const QPainterPath &,QGraphicsItem *)’ : cannot convert parameter 1 from ‘QGraphicsItem *’ to ‘const QPainterPath &’
    1> Reason: cannot convert from ‘QGraphicsItem *’ to ‘const QPainterPath’
    1> No constructor could take the source type, or constructor overload resolution was ambiguous
    1> qnemainwindow.cpp
    1>c:usersbderoomsdesktopqnodeseditor_srcqnodeseditorui_qnemainwindow.h(14): fatal error C1083: Cannot open include file: ‘QtGui/QAction’: No such file or directory
    1> qneport.cpp
    1>……….Desktopqnodeseditor_srcqnodeseditorqneport.cpp(37): error C2664: ‘QGraphicsPathItem::QGraphicsPathItem(const QPainterPath &,QGraphicsItem *)’ : cannot convert parameter 1 from ‘QGraphicsItem *’ to ‘const QPainterPath &’
    1> Reason: cannot convert from ‘QGraphicsItem *’ to ‘const QPainterPath’
    1> No constructor could take the source type, or constructor overload resolution was ambiguous
    1> Generating Code…
    1>
    1>Build FAILED.
    1>
    1>Time Elapsed 00:00:02.92
    ========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

  • Just what i needed, but i use QT 5 and get the same error. cannot convert from ‘QGraphicsItem *’ to ‘const QPainterPath’

  • Hello, i’m started to using this wonderful but my question is how to track the flowcharts blocks and where is connected and translate them into script code (i.e Lua)?. Thanks.

  • Hi! Cool library;) But I think, Q_FOREACH instead of foreach should be used in library code – some people (e.g. me) compile project with flag QT_NO_KEYWORDS. Q_FOREACH is universal solution.

  • Hay thanks man this is a really good base to start creating qt node based software. Thanks for your library and your research on the subject it has saved me a lot of time . Just for the info. on getting it to run on qt5 (seeing some of the comments above). I have been able to smoothly compile it on a desktop version of QT5. Once again thanks a ton

  • Thanks a lot for sharing this excellent node-based editor project.
    One question: I tried replacing QGraphicsItem with QGraphicsProxyWidget, and I cannot figure out why the latter can’t be moved around the scene, while QGraphicsItem work. Any idea? Thanks

  • Thanks for the answer. Would it be possible for you to modify the code so that instead of using QPaint items, it actually allows to embed QWidgets, thus allowing to create a more complex interface node?
    I’d need this for a project I’m working on, so if you want to contact me at my email, we could discuss the terms. Thanks a lot.
    Alex

    • I’m convinced QGraphicsProxyWidget is the way to go, just the issues need to be ironed out. QGraphicsProxyWidget _is_ a QGraphicsItem so there’s no need to “replace” anything. Which widget did you put inside of the proxy? Maybe you should consider putting the proxy as a child of another QGraphicsItem, to provide a “border” to be used for dragging.

      • Thanks for the reply and clarifications.
        So I tried to add a child QGraphicsProxyWidget this way, directly in the block constructor (basically I’d like to replace the QPaint calls with widgets):

        QNEBlock::QNEBlock(QGraphicsItem *parent, QGraphicsScene *scene, QString nodeType ) : QGraphicsPathItem(parent, scene)
        {
        QLineEdit* pLineEdit = new QLineEdit(“Some Text”);
        QGraphicsProxyWidget *w = new QGraphicsProxyWidget(parent);
        w->setWidget(pLineEdit);
        …..
        }

        but unfortunately the control doesn’t show. Any idea what might be wrong?
        Thanks

        • Ok, I managed to solve it creating subclassing a new QGraphicsItem, and adding there proxy, ports etc., as you kindly suggested. Thanks again.

  • now i am working on the same thing using meta information about signals and slots. going to do automatic connection )

  • Sorry for this beginner question, but how tyo install this widget in Qt Creator ?

    If you find my question to much “stupid”, let me know !!! lol


Leave a Reply

Your email address will not be published. Required fields are marked *