1

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_()
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
Cyberikia
  • 91
  • 6

1 Answers1

2

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

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • Now it's clear where my error was. Just one question. How variable `change` works? I mean when execute `self.path.updateElement(self.index, value)`. If I print type of that variable I get a list of different objects. How `updateElement()` gets from that list `QPointF()`, because when it executes `setElementPositionAt` it needs index and `pos.x() and pos.y()` – IKA Aug 13 '17 at 11:37
  • And one more question : How to get selected items from `SceneClass`? `self.selectedItems()` does not provide any information about selected elements. – IKA Aug 13 '17 at 12:26
  • If you review the docs: `setElementPositionAt()` you will notice that it receives the index of the item, coordinates it in x and the coordinate in y, QPointF does not agree directly, but we can get it through the functions x() and y() – eyllanesc Aug 13 '17 at 12:59
  • At what moment do you use `selectedItems()`? – eyllanesc Aug 13 '17 at 13:01
  • At mouseMoveEvent and mousPressEvent. – IKA Aug 13 '17 at 14:26
  • Here it is https://github.com/cyberiRex/irex/blob/master/Elements. In the documentations it says: _Returns a list of all currently selected items. The items are returned in no particular order_. – IKA Aug 13 '17 at 18:50
  • You have to enable the ItemIsSelectable flag: https://gist.github.com/eyllanesc/168aced67ffc2df0afb85e1e58b0eaff – eyllanesc Aug 13 '17 at 19:28
  • You helped me so much! – IKA Aug 13 '17 at 20:03