Hello,
I'm trying to make a little project with PyQtGraph.
Briefly, my goal is to draw a sort of ruler that measures the distance 
between three points.
One point is the origin of the measure, and the other two are independent 
points.
Something like this:
[image: Screenshot (36).png]
I've tried everything and the best result I managed to obtain is this.
The problem I'm facing is that when I put one line on the common point and 
the other under it, like this:
[image: Immagine 2021-05-26 162818.png]
If I click one of the two lines, the design brokes, and I can't understand 
why this happens and how to prevent it.
[image: Immagine 2021-05-26 162846.png]
I'd like to understand more precisely how PyQtGraph handles the drawing of 
the objects because at the moment I can't comprehend how I could change my 
code to make it work as I want.

-- 
You received this message because you are subscribed to the Google Groups 
"pyqtgraph" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/pyqtgraph/43e59f9c-5faf-46d5-8684-8b7e53d3d6f1n%40googlegroups.com.
# import the necessary packages
from PySide2.QtCore import QLineF, Qt, Signal, Slot, QObject, QPointF, QRectF, QSizeF
from PySide2.QtGui import QRegion, QFont
from PySide2.QtWidgets import QGraphicsItem, QLabel, QWidget
from pyqtgraph.graphicsItems.ImageItem import ImageItem
from pyqtgraph.graphicsItems.LinearRegionItem import LinearRegionItem
# import requests
from functools import partial
import numpy as np
import cv2
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui, QtWidgets
from pyqtgraph.graphicsItems.ViewBox.ViewBox import ViewBox
import random

pg.setConfigOptions(useOpenGL=True)

image = np.zeros((1280, 960, 3))

app = QtGui.QApplication([])

## Create window with GraphicsView widget
w = pg.GraphicsView()
w.show()

w.setWindowTitle('Seam Test')

view = pg.ViewBox()

view.setLimits(xMin=0, xMax=image.shape[0],
               minXRange=100, maxXRange=2000,
               yMin=0, yMax=image.shape[1],
               minYRange=100, maxYRange=2000)
w.setCentralItem(view)

## lock the aspect ratio
view.setAspectLocked(True)

## Add image item
item = ImageItem(image)
view.addItem(item)

## Add line item

class PointItem(QObject):
    pos_changed = Signal(object)

    def __init__(self, x, y, parent=None):
        super().__init__(parent)
        self._point = QPointF(x, y)
        self._delta = QPointF(0, 0)
        
    @property
    def pos(self):
        return self._point
    
    @pos.setter
    def pos(self, new_pos: tuple[float, float]):
        self._point.setX(new_pos[0])
        self._point.setY(new_pos[1])

    @property
    def delta(self):
        return self._delta

    def setDelta(self, d: QPointF, caller):
        self._delta.setX(d.x())
        self._delta.setY(d.y())
        self.pos_changed.emit(caller)

    # def copy(self, parent=None):
    #     return PointItem(self._point.x(), self._point.y(), parent)

class RectItem(pg.UIGraphicsItem):
    def __init__(self, rect, parent=None):
        super().__init__(parent)
        self._rect = rect
        self.picture = QtGui.QPicture()
        self._generate_picture()

        self.setFlag(QGraphicsItem.ItemIsSelectable)
        self.setFlag(QGraphicsItem.ItemIsMovable)
        self.setFlag(QGraphicsItem.ItemSendsGeometryChanges)

    @property
    def rect(self):
        return self._rect

    def _generate_picture(self):
        painter = QtGui.QPainter(self.picture)
        painter.setPen(pg.mkPen("w"))
        painter.setBrush(pg.mkBrush("g"))
        painter.drawRect(self.rect)
        painter.end()

    def paint(self, painter, option, widget=None):
        painter.drawPicture(0, 0, self.picture)

    def boundingRect(self):
        return QRectF(self.picture.boundingRect())

    # Non usare perché dà troppe informazioni
    def mouseMoveEvent(self, event):
        print(event)
        super().mouseMoveEvent(event)


class LineItem(pg.UIGraphicsItem):

    moved = Signal(QPointF)

    def __init__(self, line, extend=0, horizontal=False, parent=None):
        super().__init__(parent)
        self.initialPos = QLineF(line)
        self._line = line
        self.extend = extend
        self.horizontal = horizontal
        self.delta = QPointF(0,0)

        self._extendLine()

        self.picture = QtGui.QPicture()
        self._generate_picture()

        self.setFlag(QGraphicsItem.ItemIsSelectable)
        self.setFlag(QGraphicsItem.ItemIsMovable)
        self.setFlag(QGraphicsItem.ItemSendsGeometryChanges)

    @property
    def line(self):
        return self._line

    def _extendLine(self):
        # if (self.extend != 0 and not self.horizontal):
        #     self._line.setP1( QPointF(self._line.x1(), self._line.y1() - abs(self.extend)) )
        # elif (self.horizontal):
        #      #self.extend = 0
        #     self._line.setP1( QPointF(self._line.x1(), self._line.y1() - abs(self.extend)) )
        # else:
        #     self._line.setP1(QPointF(self._line.x1(), self._line.y1()))
        pass

    def _generate_picture(self):
        painter = QtGui.QPainter(self.picture)
        painter.setPen(pg.mkPen(color="y", width=2))
        painter.drawLine(self.getP1(), self.getP2())
        painter.end()

    def paint(self, painter, option, widget=None):
        painter.drawPicture(0, 0, self.picture)

    def boundingRect(self):
        lineShape = self.picture.boundingRect()
        lineShape.adjust(-10, -10, 10, 10)
        return QtCore.QRectF(lineShape)

    def itemChange(self, change, value):
        if change == QtWidgets.QGraphicsItem.ItemPositionChange:
            # value is the new position.
            if self.horizontal:
                if value.x() != 0:
                    value = QPointF(0, value.y())

            self.moved.emit(value)
            print(f"value: {value}")
        return pg.UIGraphicsItem.itemChange(self, change, value)

    def getP1(self):
        return QPointF(self._line.x1(), self._line.y1())

    def getP2(self):
        return QPointF(self._line.x2(), self._line.y2())

    def setDelta(self, delta: QPointF):
        self.delta = delta

    def getDelta(self):
        return self.delta

class TerminatedLineItem(pg.UIGraphicsItem):

    moved = Signal(QPointF)

    def __init__(self, line, extend=0, horizontal=False, parent=None):
        super().__init__(parent)
        self.initialPos = QLineF(line)
        self._line = line
        self.extend = extend
        self.horizontal = horizontal
        self.delta = QPointF(0, 0)

        self._extendLine()

        self.picture = QtGui.QPicture()
        self._generate_picture()

        self.setFlag(QGraphicsItem.ItemIsSelectable)
        self.setFlag(QGraphicsItem.ItemIsMovable)
        self.setFlag(QGraphicsItem.ItemSendsGeometryChanges)

    @property
    def line(self):
        return self._line

    def _extendLine(self):
        if (self.extend != 0 and not self.horizontal):
            self._line.setP1(QPointF(self._line.x1(), self._line.y1() - abs(self.extend)))
        elif (self.horizontal):
            # self.extend = 0
            self._line.setP1(QPointF(self._line.x1(), self._line.y1() - abs(self.extend)))
        else:
            self._line.setP1(QPointF(self._line.x1(), self._line.y1()))

    def _generate_picture(self):
        painter = QtGui.QPainter(self.picture)
        painter.setPen(pg.mkPen(color="y", width=2))
        painter.drawLine(QPointF(self.line.x1(), self.line.y1()), QPointF(self.line.x2(), self.line.y2()))
        painter.drawRect(self.line.x1() - 4, self.line.y1() - 4, 8, 8)
        painter.end()

    def paint(self, painter, option, widget=None):
        painter.drawPicture(0, 0, self.picture)

    def boundingRect(self):
        lineShape = self.picture.boundingRect()
        lineShape.adjust(-10, -10, 10, 10)
        return QtCore.QRectF(lineShape)

    def itemChange(self, change, value):
        if change == QtWidgets.QGraphicsItem.ItemPositionChange:
            # value is the new position.
            if self.horizontal:
                if value.x() != 0:
                    value = QPointF(0, value.y())
            # else:
            #     if value.y() != 0:
            #         value = QPointF(value.x(), 0)
            self.moved.emit(value)
        return pg.UIGraphicsItem.itemChange(self, change, value)

    def getP1(self):
        return QPointF(self._line.x1(), self._line.y1())

    def setDelta(self, delta: QPointF):
        self.delta = delta

    def getDelta(self):
        return self.delta


class Distance(QObject):
    def __init__(self, A: PointItem, B: PointItem, view: ViewBox, inity: float, name, parent: QWidget=None):
        super().__init__(parent)

        self.name = name
        # self._id = str(id(self))

        if A.pos.x() > B.pos.x():
            self.A, self.B = B, A
        else:
            self.A, self.B = A, B

        # print(f"ID A: {id(self.A)}, ID B: {id(self.B)}, Distance {self.name}")

        self.distance = abs(B.pos.x() - A.pos.x())
        self.picture = QtGui.QPicture()
        self.Achanged = True
        self.Bchanged = True

        self.extend = 0
        top = inity
        self.left = TerminatedLineItem(QtCore.QLineF(0, 0, 0, 0), self.extend)
        self.right = TerminatedLineItem(QtCore.QLineF(0, 0, 0, 0), self.extend)
        self.top = LineItem(QtCore.QLineF(0, 0, 0, 0), horizontal=True)

        self.left.line.setP1(QPointF(self.A.pos.x(), self.A.pos.y()))
        self.left.line.setP2(QPointF(self.A.pos.x(), top))
        self.right.line.setP1(QPointF(self.B.pos.x(), self.B.pos.y()))
        self.right.line.setP2(QPointF(self.B.pos.x(), top))
        self.top.line.setP1(QPointF(self.A.pos.x(), top))
        self.top.line.setP2(QPointF(self.B.pos.x(), top))

        self.left._generate_picture()
        self.right._generate_picture()
        self.top._generate_picture()

        self.top.setPos(0, 0)

        self.left.moved.connect(self.onLeftSegmentMoved)
        self.right.moved.connect(self.onRightSegmentMoved)
        self.top.moved.connect(self.onTopSegmentMoved)
        self.A.pos_changed.connect(self._onAMoved)
        self.B.pos_changed.connect(self._onBMoved)

        font = QFont()
        font.setPixelSize(12)
        html = f'<div style="text-align: center; font-size: 16px; color: yellow;">{str(round(self.B.pos.x() - self.A.pos.x(), 2))}</div>'
        self.label = pg.TextItem(
            html=html,
            anchor=(0.5, 1)
            # border='w',
            # fill=(0, 0, 255, 100)
        )
        self.label.setParentItem(self.top)
        self.label.setPos(min(self.top.line.x1(), self.top.line.x2()) + ((self.top.boundingRect().width()) / 2) - (self.label.boundingRect().width() / 2), self.top.line.y1() + self.top.getDelta().y() + 5)

        view.addItem(self.label)
        view.addItem(self.left)
        view.addItem(self.right)
        view.addItem(self.top)

    def __del__(self):
        view.removeItem(self.label)
        view.removeItem(self.left)
        view.removeItem(self.top)
        view.removeItem(self.right)

    @Slot(QPointF)
    def onLeftSegmentMoved(self, delta: QPointF):
        # print(f"{self.name} in olsm")
        self.A.pos = (self.left.getP1().x(), self.left.getP1().y())
        # self.A.setDelta(delta, self._id)
        self.A.setDelta(delta, self)
        # self._drawlines()

    @Slot(QPointF)
    def onTopSegmentMoved(self, delta: QPointF):
        self.top.setDelta(delta)
        self._drawlines()

    @Slot(QPointF)
    def onRightSegmentMoved(self, delta: QPointF):
        # print(f"{self.name} in orsm")
        self.B.pos = (self.right.getP1().x(), self.right.getP1().y())
        # self.B.setDelta(delta, self._id)
        self.B.setDelta(delta, self)
        # self._drawlines()

    @Slot(object)
    def _onAMoved(self, caller):
        # #print(f"caller : {caller_id}, me : {self._id}")
        # print(f"caller_id {caller_id}, my_id{self._id}")
        self.Achanged = (self is caller)

        self._drawlines()

    @Slot(object)
    def _onBMoved(self, caller):
        self.Bchanged = (self is caller)

        self._drawlines()

    def _drawlines(self):
        # print(f"func: {self.name}, A: {self.A.pos}, B: {self.B.pos}")
        # print(f"ID A: {id(self.A)}, ID B: {id(self.B)}, Distance {self.name}")

        if self.Achanged:
            self.left.line.setP1(QPointF(self.A.pos.x(), self.A.pos.y()))
            self.left.line.setP2(QPointF(self.A.pos.x(), self.top.getP1().y() + self.top.getDelta().y() - self.A.delta.y()))
        else:
            self.left.line.setP1(QPointF(self.A.pos.x() + self.A.delta.x(), self.A.pos.y() + self.A.delta.y()))
            self.left.line.setP2(QPointF(self.A.pos.x() + self.A.delta.x(), self.top.getP1().y() + self.top.getDelta().y()))

        if self.Bchanged:
            self.right.line.setP1(QPointF(self.B.pos.x(), self.B.pos.y()))
            self.right.line.setP2(QPointF(self.B.pos.x(), self.top.getP1().y() + self.top.getDelta().y() - self.B.delta.y()))
        else:
            self.right.line.setP1(QPointF(self.B.pos.x() + self.B.delta.x(), self.B.pos.y() + self.B.delta.y(),))
            self.right.line.setP2(QPointF(self.B.pos.x() + self.B.delta.x(), self.top.getP1().y() + self.top.getDelta().y()))

        self.top.line.setP1(QPointF(self.A.pos.x() + self.A.delta.x(), self.top.getP1().y()))
        self.top.line.setP2(QPointF(self.B.pos.x() + self.B.delta.x(), self.top.getP1().y()))

        self.distance = (abs((self.A.pos.x() + self.A.delta.x()) - (self.B.pos.x() + self.B.delta.x())))

        self.label.setAnchor((0.5, 1))
        self.label.setPos(min(self.top.line.x1(), self.top.line.x2()) + ((self.top.boundingRect().width()) / 2) - (self.label.boundingRect().width() / 2), self.top.line.y1() + self.top.getDelta().y() + 5)
        self.label.setText(str(round(abs(self.distance), 2)))
        html = f'<div style="text-align: center; font-size: 16px; color: yellow;">{str(round(abs(self.distance), 2))}</div>'
        self.label.setHtml(html)
        self.left._generate_picture()
        self.top._generate_picture()
        self.right._generate_picture()

        painter = QtGui.QPainter(self.picture)
        painter.setPen(pg.mkPen(color="y", width=2))
        painter.drawLines([self.left.line, self.right.line, self.top.line])
        painter.end()

        print(f"AC : {self.Achanged}, BC : {self.Bchanged}, name: {self.name}")

def mouseClicked(evt):
    pos = evt[0]
    print(pos)


couples = 3
points: list[PointItem] = []
for i in range(0, couples*2):
    x = random.randint(100, 600)
    y = random.randint(100, 600)
    points.append(PointItem(x, y))

distance = []
distance.append(Distance(points[0], points[3], view, 500, "1"))
distance.append(Distance(points[0], points[2], view, 600, "2"))
# for i in range(0, couples):
#     distance.append(Distance(points[(2*i)], points[(2*i)+1], view, 600+(50*i)))
#     print(i)

proxyClicked = pg.SignalProxy(w.scene().sigMouseClicked, rateLimit=60, slot=mouseClicked)



## Start Qt event loop unless running in interactive mode.
if __name__ == '__main__':
    import sys

    if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
        QtGui.QApplication.instance().exec_()

Reply via email to