如何使用 PyQtGraph 绘制图像上两点之间的水平距离

How to plot the horizontal distance between 2 points on an image with PyQtGraph

我想用 PyQtGraph 绘制图像上两点之间的水平距离,但是我画不出来。
我认为这样做的一种方法是使用 3 个 LineSegmentROI 实例并使它们看起来像一个正确的弧线一样连接,因为它们已经具有许多非常适合这个想法的功能。
就像可拖动一样,这对于通过简单地拖动边来测量不同的距离非常有用。
问题是手柄,无法移除,甚至无法隐藏。
有人做过这样的事吗?

# import the necessary packages
from pyqtgraph.graphicsItems.ImageItem import ImageItem
from pyqtgraph.graphicsItems.LinearRegionItem import LinearRegionItem
import requests
import numpy as np
import cv2
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui


image = cv2.imread('example.png') # Change if you save the image with a different name
image = cv2.rotate(image, cv2.ROTATE_90_CLOCKWISE)

app = QtGui.QApplication([])

## Create window with GraphicsView widget
w = pg.GraphicsView()
w.show()
w.resize(image.shape[0], image.shape[1]) # Depending on the picture you may need to resize
w.setWindowTitle('Test')

view = pg.ViewBox()
view.setLimits(xMin=-image.shape[0]*0.05, xMax=image.shape[0]*1.05,
               minXRange=100, maxXRange=2000,
               yMin=-image.shape[1]*0.05, yMax=image.shape[1]*1.05,
               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
line = pg.QtGui.QGraphicsLineItem(200, -100, 400, -100, view)
line.setPen(pg.mkPen(color=(255, 0, 0), width=10))
view.addItem(line)

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

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_()

我最终从 借用了 RectItem 并将其代码用于 LineItem
我用三个 LineItem 画了两点之间的测距仪。
我仍然需要添加一些信号和插槽来处理调整大小,但我正在努力。
然而解决方案的核心就在这里,我会跟进我的改进

# import the necessary packages
from PySide2.QtCore import QLineF, Qt, Signal, Slot, QObject, QPointF, QRectF, QSizeF
from PySide2.QtGui import QRegion
from PySide2.QtWidgets import QGraphicsItem, QLabel, QWidget
from pyqtgraph.graphicsItems.ImageItem import ImageItem
from pyqtgraph.graphicsItems.LinearRegionItem import LinearRegionItem
import numpy as np
import cv2
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui, QtWidgets
from pyqtgraph.graphicsItems.ViewBox.ViewBox import ViewBox


image = cv2.imread('image.jpg')
image = cv2.rotate(image, cv2.ROTATE_90_CLOCKWISE)

app = QtGui.QApplication([])

## Create window with GraphicsView widget
w = pg.GraphicsView()
w.show()
w.setWindowTitle('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)


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._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)) )
        # if (self.horizontal):
        #     self.extend = 0
        # self._line.setP1( QPointF(self._line.x1(), self._line.y1() - abs(self.extend)) )

    def _generate_picture(self):
        painter = QtGui.QPainter(self.picture)
        painter.setPen(pg.mkPen(color="y", width=2))
        painter.drawLine(self.line)
        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)


class Distance(QObject):
    def __init__(self, A: QPointF, B: QPointF, view: ViewBox, parent: QWidget=None):
        super().__init__(parent)
        self.A = A
        self.B = B
        if A.x() > B.x():
            self.A, self.B = B, A
        self.distance = abs(B.x() - A.x())

        print(self.A)
        print(self.B)

        extend = 50
        top = max(self.A.y(), self.B.y()) + 200
        self.left = LineItem(QtCore.QLineF(self.A.x(), self.A.y(), self.A.x(), top), extend)
        self.right = LineItem(QtCore.QLineF(self.B.x(), self.B.y(), self.B.x(), top), extend)
        self.top = LineItem(QtCore.QLineF(self.A.x(), top, self.B.x(), top), horizontal=True)

        self.top.setPos(0, 0)

        self.left.moved.connect(self.onLeftSegmentMoved)
        self.right.moved.connect(self.onRightSegmentMoved)
        self.top.moved.connect(self.onTopSegmentMoved)

        self.label = pg.TextItem(str(round(self.distance, 2)), color=(0xFF, 0xFF, 0x00), anchor=(1, 1))
        # self.label.setParentItem(self.top)
        self.label.setPos(self.A.x()+self.distance/2, top + 5)

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

    @Slot(QPointF)
    def onLeftSegmentMoved(self, delta: QPointF):
        topLeft = self.top.initialPos.p1()
        newX = topLeft.x() + delta.x()
        newTopLeft = QPointF(newX, topLeft.y())
        self.top.line.setP1(newTopLeft)
        self.top._generate_picture()

        pos = self.label.pos()
        self.distance = abs(self.top.line.x2() - self.top.line.x1())
        self.label.setPos(newX + (self.top.line.x2() - self.top.line.x1())/2, pos.y())
        self.label.setText(str(round(self.distance, 2)))

    @Slot(QPointF)
    def onTopSegmentMoved(self, delta: QPointF):
        leftTop = self.top.initialPos.p1()
        newY = leftTop.y() + delta.y()
        newLeftTop = QPointF(leftTop.x(), newY)
        self.left.line.setP2(newLeftTop)
        self.left._generate_picture()

        rightTop = self.top.initialPos.p2()
        newY = rightTop.y() + delta.y()
        newRightTop = QPointF(rightTop.x(), newY)
        self.right.line.setP2(newRightTop)
        self.right._generate_picture()

        pos = self.label.pos()
        self.label.setPos(pos.x(), newY)

    @Slot(QPointF)
    def onRightSegmentMoved(self, delta: QPointF):
        topRight = self.top.initialPos.p2()
        newX = topRight.x() + delta.x()
        newTopRight = QPointF(newX, topRight.y())
        self.top.line.setP2(newTopRight)
        self.top._generate_picture()

        pos = self.label.pos()
        self.distance = abs(self.top.line.x2() - self.top.line.x1())
        self.label.setPos(newX - (self.top.line.x2() - self.top.line.x1())/2, pos.y())
        self.label.setText(str(round(self.distance, 2)))


distance = Distance(QPointF(925, 425), QPointF(138, 500), view)