PyQt5 - 如何计算中心点旋转后 QGraphicsRectItem 的角点?

PyQt5 - How to calculate corner points of a QGraphicsRectItem after rotation by its center point?

我的问题是,在围绕中心点旋转 theta 角度后,我无法找到 HighwayItem(这是一个 QGraphicsRectItem)的每个角点的像素值

我使用了解释 here and I also looked this 解释的旋转矩阵。但是,我找不到真正的价值。

任何帮助都会很棒。谢谢

这是 MapViewer() class。在此视图中创建了一个 HighwayItem。

from PyQt5 import QtCore, QtGui, QtWidgets

from PyQt5.QtGui import QPixmap
from PyQt5.QtCore import Qt, QPoint, QPointF, QRectF
from PyQt5.QtWidgets import QGraphicsScene, \
                            QGraphicsView, QGraphicsPixmapItem, \
from class_graphical_items import HighwayItem

class MapViewer(QGraphicsView):
    def __init__(self, parent, ui):
        super(MapViewer, self).__init__(parent)
        self.ui = ui

        # Attributes for highway
        self.add_highway_control = False
        self.current_highway = None
        self.start = QPointF()
        self.hw_counter = 0

        self._scene = QGraphicsScene(self)
        self._map = QGraphicsPixmapItem()
        self._scene.addItem(self._map)
        self.setScene(self._scene)

        self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)
        self.setResizeAnchor(QGraphicsView.AnchorUnderMouse)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setFrameShape(QtWidgets.QFrame.NoFrame)


     def mousePressEvent(self, event):
        if self._map.isUnderMouse():
            if self.add_highway_control:
                # Create a yellow highway
                self.current_highway = HighwayItem(self._scene, self.ui)
                self.hw_counter += 1
                self.start = self.mapToScene(event.pos()).toPoint()
                r = QRectF(self.start, self.start)
                self.current_highway.setRect(r)
                self._scene.addItem(self.current_highway)

                # When adding HW, set drag mode NoDrag
                self.setDragMode(QGraphicsView.NoDrag)
        super(MapViewer, self).mousePressEvent(event)
     

    def mouseMoveEvent(self, event):
        if self.add_highway_control and self.current_highway is not None:
            # When adding HW, set drag mode NoDrag
            self.setDragMode(QGraphicsView.NoDrag)

            r = QRectF(self.start, self.mapToScene(event.pos()).toPoint()).normalized()
            self.current_highway.setRect(r)
        super(MapViewer, self).mouseReleaseEvent(event)


    def mouseReleaseEvent(self, event):
        if self.add_highway_control:
            if self.current_highway is not None:
                # When finish the adding HW, set drag mode ScrollHandDrag
                self.setDragMode(QGraphicsView.ScrollHandDrag)

                self.update_item_dict(self.current_highway)
                self.update_item_table(self.current_highway)

            self.current_highway = None
            self.add_highway_control = False
        super(MapViewer, self).mouseReleaseEvent(event)

这是 HighwayItem class。它有一些规格,如颜色、不透明度等。

通过双击创建的 HighwayItem,我激活了主 window (ui) 中 QTreeWidget 中的旋转框。 通过更改旋转框值,用户可以旋转项目。

class HighwayItem(QGraphicsRectItem):
    def __init__(self, scene, ui):
        QGraphicsRectItem.__init__(self)
        self.scene = scene
        self.ui = ui

        self.setBrush(QtCore.Qt.yellow)
        self.setOpacity(0.5)
        self.setZValue(4.0)
        self.setFlag(QGraphicsItem.ItemIsMovable, True)
        self.setFlag(QGraphicsItem.ItemIsSelectable, True)
        self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True)
        self.setFlag(QGraphicsItem.ItemIsFocusable, True)
        self.setAcceptHoverEvents(True)

    # Here, I'm activating the spinbox by double clicking
    # on HighwayItem. In spinbox, I'm entering the rotation angle
    # of HighwayItem.
    def mouseDoubleClickEvent(self, event):
        
        selected_item = self.scene.selectedItems()

        if selected_item:
            for i in range(self.ui.treeWidget_objects.topLevelItemCount()):
                toplevel_item = self.ui.treeWidget_objects.topLevelItem(i)
                heading_item = toplevel_item.child(2)
                spinbox = self.ui.treeWidget_objects.itemWidget(heading_item, 2)

                if str(toplevel_item.text(2)) == str(selected_item[0]):
                    if 'HighwayItem' in str(selected_item[0]):
                        spinbox.setEnabled(True)
                    else:
                        spinbox.setEnabled(False)

这是设置 HWItem 旋转角度的 HWHeadingSpinBox() class。我的问题从这里开始。在 rotate_hw() 方法中,我将创建的 HighwayItem 通过其中心点进行转换,并通过其中心点对其进行旋转。

但是,当我尝试在 calc_rotated_coords() 方法中计算硬件的新角时,我搞砸了。

class HWHeadingSpinBox(QSpinBox):
    def __init__(self, viewer, selected_hw):
        QSpinBox.__init__(self)
        self.selected_hw = selected_hw
        self.viewer = viewer

        # First coords of HW
        tl = self.selected_hw.rect().topLeft()
        tr = self.selected_hw.rect().topRight()
        br = self.selected_hw.rect().bottomRight()
        bl = self.selected_hw.rect().bottomLeft()
        self.temp_list = [tl, tr, br, bl]

        self.setRange(-180, 180)
        self.setSuffix('°')
        self.setEnabled(False)
        self.valueChanged.connect(self.rotate_hw)

    def heading_val(self):
        return self.value()

    def rotate_hw(self):
        angle = self.heading_val()
        self.selected_hw.prepareGeometryChange()
        offset = self.selected_hw.boundingRect().center()   
        self.selected_hw.sceneBoundingRect().center()
        transform = QTransform()
        transform.translate(offset.x(), offset.y())
        transform.rotate(-angle)
        transform.translate(-offset.x(), -offset.y())
        self.selected_hw.setTransform(transform)


        # br_rect = self.selected_hw.sceneBoundingRect()
        # sbr_rect = self.selected_hw.sceneBoundingRect()
        # r_rect = self.selected_hw.sceneBoundingRect()
# 
        # rectitem = QtWidgets.QGraphicsRectItem(br_rect)
        # rectitem.setBrush(Qt.red)
        # self.viewer._scene.addItem(rectitem)
# 
        # rectitem = QtWidgets.QGraphicsRectItem(sbr_rect)
        # rectitem.setBrush(Qt.green)
        # self.viewer._scene.addItem(rectitem)
# 
        # rectitem = QtWidgets.QGraphicsRectItem(r_rect)
        # rectitem.setBrush(Qt.blue)
        # self.viewer._scene.addItem(rectitem)


     def calc_rotated_coords(self):
        # center point
        cx = self.selected_hw.rect().center().x()
        cy = self.selected_hw.rect().center().y()

        # rotation angle
        theta = math.radians(angle)

        rotated_corners = []
        for item in self.temp_list:
            x = item.x()
            y = item.y()

            temp_x = x - cx
            temp_y = y - cy

            rot_x = temp_x * math.cos(theta) + temp_y * math.sin(theta)
            rot_y = -temp_x * math.sin(theta) + temp_y * math.cos(theta)

            rotated_corners.append([rot_x, rot_y])
        
        self.temp_list = rotated_corners

        print("\nPIXEL VALUES OF HW: \n{}".format(self.temp_list))

解决方法如下:

我在 HighwayItem 中添加了 itemChange(self, change, value) 事件,如果 change 是 ItemPositionHasChanged,我计算了所有项目的角落:

def itemChange(self, change, value):
        if change == QGraphicsItem.ItemPositionHasChanged:
            top_left = self.mapToScene(self.rect().topLeft())
            top_right = self.mapToScene(self.rect().topRight())
            bottom_left = self.mapToScene(self.rect().bottomLeft())
            bottom_right = self.mapToScene(self.rect().bottomRight())

            changed_pos = [top_left, top_right, bottom_right, bottom_left]
        return super(HighwayItem, self).itemChange(change, value)