Search code examples
pythonpyqtpyqt4nodespyside

PySide/PyQt4 draw nodes and connections


Trying to create nodes and connect them to each other. I'm stacked on setElementPositionAt. The error is: AttributeError: 'PySide.QtGui.QPainterPath' object has no attribute 'updateElement' I have found the working code here: How can I draw nodes and edges in PyQT? but can't adapt it to my widget. What I'm doing wrong? Here is the code:

from PySide.QtCore import *
from PySide.QtGui import *

rad = 5


class WindowClass(QMainWindow):
    def __init__(self):
        super(WindowClass, self).__init__()
        self.view = ViewClass()
        self.setCentralWidget(self.view)


class ViewClass(QGraphicsView):
    def __init__(self):
        super(ViewClass, self).__init__()

        self.setDragMode(QGraphicsView.RubberBandDrag)
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        self.s = SceneClass()
        self.setScene(self.s)
        self.setRenderHint(QPainter.Antialiasing)


class SceneClass(QGraphicsScene):
    def __init__(self, id=None):
        super(SceneClass, self).__init__()
        self.setSceneRect(-1000, -1000, 2000, 2000)
        self.grid = 30

        self.p = QPainterPath()
        self.it = None
        self.node = None

    def drawBackground(self, painter, rect):
        if False:
            painter = QPainter()
            rect = QRect()

        painter.fillRect(rect, QColor(30, 30, 30))
        left = int(rect.left()) - int((rect.left()) % self.grid)
        top = int(rect.top()) - int((rect.top()) % self.grid)
        right = int(rect.right())
        bottom = int(rect.bottom())
        lines = []
        for x in range(left, right, self.grid):
            lines.append(QLine(x, top, x, bottom))
        for y in range(top, bottom, self.grid):
            lines.append(QLine(left, y, right, y))
        painter.setPen(QPen(QColor(50, 50, 50)))
        painter.drawLines(lines)

    def mousePressEvent(self, event):
        if event.button() == Qt.RightButton:
            self.p.moveTo(0, 0)
            self.p.lineTo(200, 100)
            self.it = Path(self.p,None)
            self.addItem(self.it)
            for i in xrange(2):
                self.node = Node(self, i, QPointF(self.p.elementAt(i)))
                self.node.setPos(QPointF(self.p.elementAt(i)))
                self.addItem(self.node)

        super(SceneClass, self).mousePressEvent(event)


class Path(QGraphicsPathItem):
    def __init__(self, path, scene):
        super(Path, self).__init__(path)
        self.pth = path

        self.scn = SceneClass(self.setPen(QPen(Qt.red, 1.75)))

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


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

        self.pos = pos
        self.rad = rad
        self.path = QPainterPath()
        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)
        return QGraphicsEllipseItem.itemChange(self, change, value)

if __name__ == '__main__':
    app = QApplication([])
    wd = WindowClass()
    wd.show()
    app.exec_()

Solution

  • You have made changes to the base classes that you have taken as reference so it is normal for you to have errors if you do not understand the code.

    When you call the updateElement method you must do it from an instance of the Path class, not an instance of the class QPaintePath, I have placed the same code of the author that you took as a reference initially and I have added to your graphic part.

    class WindowClass(QMainWindow):
        def __init__(self):
            super(WindowClass, self).__init__()
            self.view = ViewClass()
            self.setCentralWidget(self.view)
    
    
    class ViewClass(QGraphicsView):
        def __init__(self):
            super(ViewClass, self).__init__()
    
            self.setDragMode(QGraphicsView.RubberBandDrag)
            self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
            self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
    
            self.s = SceneClass()
            self.setScene(self.s)
            self.setRenderHint(QPainter.Antialiasing)
    
    
    class SceneClass(QGraphicsScene):
        def __init__(self, id=None):
            super(SceneClass, self).__init__()
            self.setSceneRect(-1000, -1000, 2000, 2000)
            self.grid = 30
            self.it = None
            self.node = None
    
        def drawBackground(self, painter, rect):
            if False:
                painter = QPainter()
                rect = QRect()
    
            painter.fillRect(rect, QColor(30, 30, 30))
            left = int(rect.left()) - int((rect.left()) % self.grid)
            top = int(rect.top()) - int((rect.top()) % self.grid)
            right = int(rect.right())
            bottom = int(rect.bottom())
            lines = []
            for x in range(left, right, self.grid):
                lines.append(QLine(x, top, x, bottom))
            for y in range(top, bottom, self.grid):
                lines.append(QLine(left, y, right, y))
            painter.setPen(QPen(QColor(50, 50, 50)))
            painter.drawLines(lines)
    
        def mousePressEvent(self, event):
            if event.button() == Qt.RightButton:
                path = QPainterPath()
                path.moveTo(0, 0)
                path.lineTo(200, 100)
                self.addItem(Path(path, self))
            super(SceneClass, self).mousePressEvent(event)
    
    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)
            return QGraphicsEllipseItem.itemChange(self, change, value)
    
    
    class Path(QGraphicsPathItem):
        def __init__(self, path, scene):
            super(Path, self).__init__(path)
            for i in range(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 = self.path()
            path.setElementPositionAt(index, pos.x(), pos.y())
            self.setPath(path)
    

    enter image description here