PyQt5 画家组对象

PyQt5 Painter Group Object

我想知道是否有一种方法可以将一堆形状绘制为自己的对象,以便当用户想要缩放、更改颜色或旋转时,所有形状都会相应地调整?

然后我希望将那组形状放置在 QGraphicsView 上预定的 x,y 坐标上,并进行指定的旋转。目前,我收到一些错误消息,显示 QPainter::scale: Painter not activeNotImplementedError: QGraphicsItem.paint() is abstract and must be overridden.

我对 PyQt 中的图形和绘画还很陌生,不确定这是否是实现我想要的目标的正确方法。非常感谢任何帮助

代码

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys
from math import sqrt

class LogObject(QObject):
    hovered = pyqtSignal()
    notHovered = pyqtSignal()

class ExamplePaintObject(QPainter):
    def __init__(self):
        super(ExamplePaintObject,self).__init__()
        self.color = QColor(0,0,0)

    def paintEvent(self,event):
        self.drawEllipse(0,0,0.0268,0.0268)
        self.drawEllipse(0,0,0.0357,0.0357)
        self.drawLine(0.0357,0.0045,0.0357+0.0821,0.0045)
        self.drawLine(0.0357,-0.0045,0.0357+0.0821,-0.0045)
        self.drawRect(0.0357+0.0821,-0.0045,0.1450,0.1450)


class Point(QGraphicsItem):

    def __init__(self, x, y,r, name):

        super(Point,self).__init__()
        self.setFlag(QGraphicsItem.ItemIsSelectable, True)
        self.name = name

        self.ex= ExamplePaintObject()
        self.ex.scale(40,40)

        self.x = x
        self.y = y
        self.rotate = r
        self.setRotation(self.rotate)
        self.setAcceptHoverEvents(True)
        self.log = LogObject()
        self.setPos(x, y)
        self.isSelected = False


    def itemChange(self, change, value):
        if change == self.ItemSelectedChange:
            self.setBrush(QBrush(Qt.green) if value else QBrush(Qt.black))
        return QGraphicsItem.itemChange(self, change, value)

    def hoverEnterEvent(self, event):
        self.setBrush(QColor("red"))
        self.log.hovered.emit()
        QGraphicsItem.hoverMoveEvent(self, event)

    def hoverLeaveEvent(self, event):
        if self.isSelected == True:
            self.setBrush(QBrush(Qt.green))
        else:
            self.setBrush(QColor("black"))
        self.log.notHovered.emit()
        QGraphicsItem.hoverMoveEvent(self, event)

    def mousePressEvent(self, event):
        print(self.name)
        QGraphicsItem.mousePressEvent(self, event)


class Viewer(QGraphicsView):
    photoClicked = pyqtSignal(QPoint)
    rectChanged = pyqtSignal(QRect)

    def __init__(self, parent):
        super(Viewer, self).__init__(parent)
        self.rubberBand = QRubberBand(QRubberBand.Rectangle, self)
        self.setMouseTracking(True)
        self.origin = QPoint()
        self.changeRubberBand = False

        self._zoom = 0
        self._empty = True
        self.setScene(QGraphicsScene(self))

        self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)
        self.setResizeAnchor(QGraphicsView.AnchorUnderMouse)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setFrameShape(QFrame.NoFrame)
        self.area = float()
        self.setPoints()
        self.viewport().setCursor(Qt.ArrowCursor)
        QTimer.singleShot(0, self.reset_fit)
        self.selectedItems = []

    def setItems(self):
        self.data = {
            "x": [
                -2414943.8686,
                -2417160.6592,
                -2417160.6592,
                -2417856.1783,
                -2417054.7618,
                -2416009.9966,
                -2416012.5232,
                -2418160.8952,
                -2418160.8952,
                -2416012.5232,
                -2417094.7694,
                -2417094.7694,
            ],
            "y": [
                10454269.7008,
                10454147.2672,
                10454147.2672,
                10453285.2456,
                10452556.8132,
                10453240.2808,
                10455255.8752,
                10455183.1912,
                10455183.1912,
                10455255.8752,
                10456212.5959,
                10456212.5959,
            ],
            "rotation":[
            313.9962,
            43.9962,
            223.9962,
            227.7070,
            227.7070,
            313.9962,
            43.9962,
            43.9962,
            223.9962,
            223.9962,
            43.9962,
            223.9962,
            ]
        }
        maxX = max(self.data["x"])
        minX = min(self.data["x"])
        maxY = max(self.data["y"])
        minY = min(self.data["y"])
        distance = sqrt((maxX - minX) ** 2 + (maxY - minY) ** 2)

        self.area = QRectF(minX, minY, distance, distance)
        for i, (x, y,r) in enumerate(zip(self.data["x"], self.data["y"],self.data["rotation"])):
            p = Point(x, y,r, "Point__" + str(i))
            p.log.hovered.connect(self.hoverChange)
            p.log.notHovered.connect(self.notHoverChange)
            self.scene().addItem(p)

    def setPoints(self):
        self.setItems()
        self.setDragMode(self.ScrollHandDrag)

    def wheelEvent(self, event):
        if event.angleDelta().y() > 0:
            factor = 1.25
            self._zoom += 1
        else:
            factor = 0.8
            self._zoom -= 1
        if self._zoom > 0:
            self.scale(factor, factor)
        elif self._zoom == 0:
            self.reset_fit()
        else:
            self._zoom = 0

    def hoverChange(self):
        self.viewport().setCursor(Qt.PointingHandCursor)

    def notHoverChange(self):
        self.viewport().setCursor(Qt.ArrowCursor)

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            singleItem = self.itemAt(event.pos().x(), event.pos().y())
            if singleItem != None:
                if QApplication.keyboardModifiers() == Qt.ShiftModifier: # This will determine if the shift key is depressed
                    if singleItem.isSelected == True:
                        singleItem.setSelected(False)
                        singleItem.isSelected = False
                        self.selectedItems.remove(singleItem)
                elif singleItem.isSelected == False:
                    singleItem.setSelected(True)
                    singleItem.isSelected = True
                    self.selectedItems.append(singleItem)
            else:
                self.origin = event.pos()
                self.rubberBand.setGeometry(QRect(self.origin, QSize()))
                self.rectChanged.emit(self.rubberBand.geometry())
                self.rubberBand.show()
                self.changeRubberBand = True
            return

        elif event.button() == Qt.MidButton:
            self.viewport().setCursor(Qt.ClosedHandCursor)
            self.original_event = event
            handmade_event = QMouseEvent(
                QEvent.MouseButtonPress,
                QPointF(event.pos()),
                Qt.LeftButton,
                event.buttons(),
                Qt.KeyboardModifiers(),
            )
            QGraphicsView.mousePressEvent(self, handmade_event)

        super(Viewer, self).mousePressEvent(event)

    def mouseReleaseEvent(self, event):
        point = event.pos()
        # print(self.mapToScene(point))
        if event.button() == Qt.LeftButton:
            self.changeRubberBand = False
            if self.rubberBand.isVisible():
                self.rubberBand.hide()
                rect = self.rubberBand.geometry()
                rect_scene = self.mapToScene(rect).boundingRect()
                selected = self.scene().items(rect_scene)
                if selected:

                    for selectedPoints in selected:
                        if QApplication.keyboardModifiers() == Qt.ShiftModifier: # This will determine if the shift key is depressed
                            if selectedPoints.isSelected == True:
                                selectedPoints.setSelected(False)
                                selectedPoints.isSelected = False
                                self.selectedItems.remove(selectedPoints)
                        elif selectedPoints.isSelected == False: # if the shif key is not depressed and its not selected, then select it
                            selectedPoints.setSelected(True)
                            selectedPoints.isSelected = True
                            self.selectedItems.append(selectedPoints)
                    print( "".join("Item: %s\n" % child.name for child in self.selectedItems))
                else:
                    print(" Nothing\n")
                    for selected in self.selectedItems:
                        selected.setSelected(False)
                        selected.isSelected = False
                    self.selectedItems.clear()
                    QGraphicsView.mouseReleaseEvent(self, event)

        elif event.button() == Qt.MidButton:
            self.viewport().setCursor(Qt.ArrowCursor)
            handmade_event = QMouseEvent(
                QEvent.MouseButtonRelease,
                QPointF(event.pos()),
                Qt.LeftButton,
                event.buttons(),
                Qt.KeyboardModifiers(),
            )
            QGraphicsView.mouseReleaseEvent(self, handmade_event)
        # super(Viewer, self).mouseReleaseEvent(event)

    def mouseMoveEvent(self, event):
        if self.changeRubberBand:
            self.rubberBand.setGeometry(
                QRect(self.origin, event.pos()).normalized()
            )
            self.rectChanged.emit(self.rubberBand.geometry())
            QGraphicsView.mouseMoveEvent(self, event)
        super(Viewer, self).mouseMoveEvent(event)

    def reset_fit(self):
        r = self.scene().itemsBoundingRect()
        self.resetTransform()
        self.setSceneRect(r)
        self.fitInView(r, Qt.KeepAspectRatio)
        self._zoom = 0
        self.scale(1, -1)


class Window(QWidget):
    def __init__(self):
        super(Window, self).__init__()
        self.viewer = Viewer(self)
        self.btnLoad = QToolButton(self)
        self.btnLoad.setText("Fit Into View")
        self.btnLoad.clicked.connect(self.fitPoints)

        VBlayout = QVBoxLayout(self)
        VBlayout.addWidget(self.viewer)
        HBlayout = QHBoxLayout()
        HBlayout.setAlignment(Qt.AlignLeft)
        HBlayout.addWidget(self.btnLoad)

        VBlayout.addLayout(HBlayout)

    def fitPoints(self):
        self.viewer.reset_fit()

if __name__ == '__main__':
    import sys
    app = QApplication(sys.argv)
    window = Window()
    window.setGeometry(500, 300, 800, 600)
    window.show()
    sys.exit(app.exec_())

一个可能的解决方案是使用 QPainterPath:

def create_path():
    path = QPainterPath()
    path.addEllipse(0, 0, 0.0268, 0.0268)
    path.addEllipse(0, 0, 0.0357, 0.0357)
    path.moveTo(0.0357, 0.0045)
    path.lineTo(0.0357 + 0.0821, 0.0045)
    path.moveTo(0.0357, -0.0045)
    path.lineTo(0.0357 + 0.0821, -0.0045)
    path.addRect(0.0357 + 0.0821, -0.0045, 0.1450, 0.1450)
    tr = QTransform()
    tr.scale(100, 100)
    path = tr.map(path)
    return path


class Point(QGraphicsPathItem):
    def __init__(self, x, y, r, name):
        super(Point, self).__init__()
        self.setFlag(QGraphicsItem.ItemIsSelectable, True)
        self.name = name
        self.setPath(create_path())
        self.setScale(10)
        # ...

为了更好地观察,您必须对 QGraphicView 使用 setRenderHints (QPainter.Antialiasing):

class Viewer(QGraphicsView):
    photoClicked = pyqtSignal(QPoint)
    rectChanged = pyqtSignal(QRect)

    def __init__(self, parent):
        super(Viewer, self).__init__(parent)
        self.rubberBand = QRubberBand(QRubberBand.Rectangle, self)
        self.setMouseTracking(True)
        self.origin = QPoint()
        self.changeRubberBand = False
        self.setRenderHints(QPainter.Antialiasing) # <---
        # ....