How can I draw nodes and edges in PyQT?

graph, pyqt, python

Solution

It has been a pain to find a good explanation for this (as of by the end of 2014 already), and since this question asks exactely what I was looking for, I'll post a transcription (from C++ to Python) of what I found in this post.

The code is below, and here is the rationale:

- `QGrahpicsItem`, `QPainterPath` and `QPainterPath.Element` are the classes you are looking for. Specifically, QPainterPath implements the kind of vector functionality you expect in applications such as CorelDraw, Adobe Illustrator, or Inkscape.

- The example below benefits from the pre-existing `QGraphicsEllipseItem` (for rendering nodes) and `QGraphicsPathItem` (for rendering the path itself), which inherit from `QGraphicsItem`.

- The `Path` constructor iterates over the `QPainterPath` elements, creating `Node` items for each one; Each of them, in turn, send updates to the parent Path object, which updates its `path` property accordingly.

- I found much, much easier to study the C++ Qt4 Docs than the rather less structured PyQt docs found elsewhere. Once you get used to mentally translate between C++ and Python, the docs themselves are a powerful way to learn how to use each class.

#!/usr/bin/env python
# coding: utf-8

from PyQt4.QtGui import *
from PyQt4.QtCore import *

rad = 5

class Node(QGraphicsEllipseItem):
    def __init__(self, path, index):
        super(Node, self).__init__(-rad, -rad, 2*rad, 2*rad)

        self.rad = rad
        self.path = path
        self.index = index

        self.setZValue(1)
        self.setFlag(QGraphicsItem.ItemIsMovable)
        self.setFlag(QGraphicsItem.ItemSendsGeometryChanges)
        self.setBrush(Qt.green)

    def itemChange(self, change, value):
        if change == QGraphicsItem.ItemPositionChange:
            self.path.updateElement(self.index, value.toPointF())
        return QGraphicsEllipseItem.itemChange(self, change, value)


class Path(QGraphicsPathItem):
    def __init__(self, path, scene):
        super(Path, self).__init__(path)
        for i in xrange(path.elementCount()):
            node = Node(self, i)
            node.setPos(QPointF(path.elementAt(i)))
            scene.addItem(node)
        self.setPen(QPen(Qt.red, 1.75))        

    def updateElement(self, index, pos):
        path.setElementPositionAt(index, pos.x(), pos.y())
        self.setPath(path)


if __name__ == "__main__":

    app = QApplication([])

    path = QPainterPath()
    path.moveTo(0,0)
    path.cubicTo(-30, 70, 35, 115, 100, 100);
    path.lineTo(200, 100);
    path.cubicTo(200, 30, 150, -35, 60, -30);

    scene = QGraphicsScene()
    scene.addItem(Path(path, scene))

    view = QGraphicsView(scene)
    view.setRenderHint(QPainter.Antialiasing)
    view.resize(600, 400)
    view.show()
    app.exec_()

Problem

In PyQT, how can I plot small "Nodes" at given points and connect them with edges? All of the PyQT tutorials I find are "plot a button! plot a checkbox!"

Original source