没有正确旋转图像

Not Rotating Image Correctly

我正在尝试旋转 QGraphicsView 对象的背景,但遇到了麻烦。红笔描述了我遇到的问题。

我想要的:

这是我的代码:

首先,我将显示箭头 class:


from PyQt5 import QtCore, QtGui, QtWidgets # importation of some libraries 

# Construnction of an arrow item, it'll be used in the QGraphicsView 
class ArrowItem(QtWidgets.QGraphicsPathItem): # it inherit QgraphicsPathItem, which allows to handle it 
# in the QgraphicsView
    def __init__(self, parent=None): # The init method
        super().__init__(parent)
        self._length = -1
        self._angle = 0
        self._points = QtCore.QPointF(), QtCore.QPointF(), QtCore.QPointF() # with three points we 
# construct a triangle. 
        self.length = 40.0 # the basic triangle length 
        self.rotate(180) # the triangle was built upside down, though I've just ran the function 'rotate'

    @property
    def angle(self):
        """
        angle of the arrow
        :return:
        """
        return self._angle

    @angle.setter
    def angle(self, angle):
        self._angle = angle

    @property
    def length(self):
        return self._length

    @length.setter
    def length(self, l):
        self._length = l

        pos_top = QtCore.QPointF(0, l * 4 / 5)
        pos_left = QtCore.QPointF(-l * 3 / 5, -l / 5)
        pos_right = QtCore.QPointF(
            l * 3 / 5,
            -l / 5,
        )

        path = QtGui.QPainterPath()
        path.moveTo(pos_top)
        path.lineTo(pos_right)
        path.lineTo(pos_left)

        self.setPath(path)

        self._points = pos_top, pos_left, pos_right

    def paint(self, painter, option, widget):
        pos_top, pos_left, pos_right = self._points

        left_color = QtGui.QColor("#cc0000")
        right_color = QtGui.QColor("#ff0000")
        bottom_color = QtGui.QColor("#661900")

        path_left = QtGui.QPainterPath()
        path_left.lineTo(pos_top)
        path_left.lineTo(pos_left)

        path_right = QtGui.QPainterPath()
        path_right.lineTo(pos_top)
        path_right.lineTo(pos_right)

        path_bottom = QtGui.QPainterPath()
        path_bottom.lineTo(pos_left)
        path_bottom.lineTo(pos_right)

        painter.setPen(QtGui.QColor("black"))
        painter.setBrush(left_color)
        painter.drawPath(path_left)
        painter.setBrush(right_color)
        painter.drawPath(path_right)
        painter.setBrush(bottom_color)
        painter.drawPath(path_bottom)

    def rotate(self, value):
        """

        :param value:
        """

        self._angle += value
        transform = QtGui.QTransform()
        transform.translate(20, 100)
        transform.rotate(self._angle)

        self.setTransform(transform)

    # Just a function to translate the arrow, it'll not be used now!
    def moveTo(self, next_position, duration=100):
        self._animation = QtCore.QVariantAnimation()
        self._animation.setStartValue(self.pos())
        self._animation.setEndValue(next_position)
        self._animation.setDuration(duration)
        self._animation.start(QtCore.QAbstractAnimation.DeleteWhenStopped)
        self._animation.valueChanged.connect(self.setPos)

下面是主要的 window 代码:

# Just setting up the main Window 
class WorkScreen(QtWidgets.QMainWindow):
   # Some constant variable to keep things easy to change in the future
    VIEW_HEIGHT = 600
    VIEW_WIDTH = 900
    FLAT_BUTTON = True
    """
    Attempt to paint the work screen.
    """

    def __init__(self, **kwargs):
        # initializing the parent class, using a 2.7 pythonic style
        super(WorkScreen, self).__init__(**kwargs)
        # customizing the main window.
        self.setStyleSheet("""
                    QMainWindow{
                    background: rgb(180, 180, 180)}
                    
                    
                    QPushButton{
                     background-color: rgb(0, 200, 0);
                      
                    }

                """)
        # the attribute 'rotation' must be zero.
        self.rotation = 0
        # defining some attributes
        self.pixmap = QtGui.QPixmap()
        self.scene = QtWidgets.QGraphicsScene()

        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)
        self.verticalLayout_3 = QtWidgets.QVBoxLayout()
        self.pushButton_status = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_settings = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_analyse = QtWidgets.QPushButton(self.centralwidget)
        self.verticalLayout_2 = QtWidgets.QVBoxLayout()
        self.view = QtWidgets.QGraphicsView(self.centralwidget)
        self.verticalLayout = QtWidgets.QVBoxLayout()
        self.pushButton_field = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_guide = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_mapping = QtWidgets.QPushButton(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.arrowItem = ArrowItem()
        self.set_up()
        self.view.setScene(self.scene)
        self.view.scale(-1, 1)
        self.image = QtGui.QImage("ola.png")
        self.image = self.image.scaled(self.VIEW_WIDTH+300, self.VIEW_HEIGHT+300)
        self.pixmap.convertFromImage(self.image)
        self.scene.addPixmap(self.pixmap)
        self.add_arrow()

    def set_up(self):
        """
        build the main window.
        """
        self.pushButton_guide.setFlat(self.FLAT_BUTTON)
        self.pushButton_mapping.setFlat(self.FLAT_BUTTON)
        self.pushButton_field.setFlat(self.FLAT_BUTTON)
        self.pushButton_status.setFlat(self.FLAT_BUTTON)
        self.pushButton_settings.setFlat(self.FLAT_BUTTON)
        self.pushButton_analyse.setFlat(self.FLAT_BUTTON)
        self.setObjectName("MainWindow")
        self.setMaximumWidth(1024)
        self.setMaximumHeight(600)
        self.resize(1024, 600)
        self.centralwidget.setObjectName("centralwidget")
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.verticalLayout_3.setContentsMargins(-1, -1, 0, -1)
        self.verticalLayout_3.setObjectName("verticalLayout_3")
        self.pushButton_status.setObjectName("pushButton_status")
        self.verticalLayout_3.addWidget(self.pushButton_status)
        self.pushButton_settings.setObjectName("pushButton_settings")
        self.verticalLayout_3.addWidget(self.pushButton_settings)
        self.pushButton_analyse.setObjectName("pushButton_analyse")
        self.verticalLayout_3.addWidget(self.pushButton_analyse)
        self.horizontalLayout.addLayout(self.verticalLayout_3)
        self.verticalLayout_2.setObjectName("verticalLayout_2")
        self.view.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        self.view.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        self.view.setRenderHints(QtGui.QPainter.HighQualityAntialiasing | QtGui.QPainter.TextAntialiasing)
        self.view.setCacheMode(QtWidgets.QGraphicsView.CacheBackground)
        self.view.scale(-1, 1)
        self.view.setFixedHeight(self.VIEW_HEIGHT)
        self.view.setFixedWidth(self.VIEW_WIDTH)
        self.view.setObjectName("view")
        # self.view.setFixedWidth(self.VIEW_WIDTH)
        # self.view.setFixedHeight(self.VIEW_HEIGHT)
        self.verticalLayout_2.addWidget(self.view)
        self.horizontalLayout.addLayout(self.verticalLayout_2)
        self.verticalLayout.setContentsMargins(-1, -1, 0, -1)
        self.verticalLayout.setObjectName("verticalLayout")
        self.pushButton_field.setObjectName("pushButton_field")
        self.verticalLayout.addWidget(self.pushButton_field)
        self.pushButton_guide.setObjectName("pushButton_guide")
        self.verticalLayout.addWidget(self.pushButton_guide)
        self.pushButton_mapping.setObjectName("pushButton_mapping")
        self.verticalLayout.addWidget(self.pushButton_mapping)
        self.horizontalLayout.addLayout(self.verticalLayout)
        self.setCentralWidget(self.centralwidget)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 26))
        self.menubar.setObjectName("menubar")
        self.setMenuBar(self.menubar)
        self.statusbar.setObjectName("statusbar")
        self.setStatusBar(self.statusbar)

        self.retranslateUi()
        QtCore.QMetaObject.connectSlotsByName(self)
        # Just to test
        self.pushButton_status.clicked.connect(self.rotate_arrow)
        self.pushButton_guide.clicked.connect(self.rotate_pixmap)

    # Created just to emit a signal
    def rotate_arrow(self):
        """
        It'll not be used after.
        """
        self.arrowItem.rotate(value=30)

    def rotate_pixmap(self):
        pixmap = self.pixmap

        self.rotation += 15
        transform = QtGui.QTransform().rotate(self.rotation, axis=QtCore.Qt.ZAxis)
        pixmap = pixmap.transformed(transform, QtCore.Qt.SmoothTransformation)
        self.scene.addPixmap(pixmap)
        self.arrowItem.setPos(self.arrowItem.pos())
        self.arrowItem.rotate(self.rotation)
        self.arrowItem.setZValue(self.arrowItem.zValue()+1)





    def add_arrow(self):
        """

        :cvar

        """
        self.scene.addItem(self.arrowItem)
        self.arrowItem.setPos(QtCore.QPointF(self.VIEW_WIDTH / 2 - 50, self.VIEW_HEIGHT / 2 - 100))

    def retranslateUi(self):
        _translate = QtCore.QCoreApplication.translate
        self.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.pushButton_status.setText(_translate("MainWindow", "Situação"))
        self.pushButton_settings.setText(_translate("MainWindow", "Configurações"))
        self.pushButton_analyse.setText(_translate("MainWindow", "Analisar"))
        self.pushButton_field.setText(_translate("MainWindow", "Campo"))
        self.pushButton_guide.setText(_translate("MainWindow", "Guia"))
        self.pushButton_mapping.setText(_translate("MainWindow", "Mapeamento"))

# calling the application 
if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = WorkScreen()
    ui.show()
    sys.exit(app.exec_())

这是我用来在背景上显示的图像:

如果你想旋转一个QGraphicsItem,没有必要使用QTransform,只需要使用setRotation 方法。另一方面,必须对图像进行缩放,使可见区域与旋转后的区域相交的区域与可见区域相同,最小区域的计算方式为:sqrt(2) x max(width, height)。综合以上,解决办法是:

from PyQt5 import QtCore, QtGui, QtWidgets


class ArrowItem(QtWidgets.QGraphicsPathItem):
    def __init__(self, parent=None):
        super().__init__(parent)
        self._length = -1
        self._points = (
            QtCore.QPointF(),
            QtCore.QPointF(),
            QtCore.QPointF(),
        )
        self.length = 40.0

    @property
    def length(self):
        return self._length

    @length.setter
    def length(self, l):
        self._length = l

        pos_top = QtCore.QPointF(0, l * 4 / 5)
        pos_left = QtCore.QPointF(-l * 3 / 5, -l / 5)
        pos_right = QtCore.QPointF(
            l * 3 / 5,
            -l / 5,
        )

        path = QtGui.QPainterPath()
        path.moveTo(pos_top)
        path.lineTo(pos_right)
        path.lineTo(pos_left)

        self.setPath(path)

        self._points = pos_top, pos_left, pos_right

    def paint(self, painter, option, widget):
        pos_top, pos_left, pos_right = self._points

        left_color = QtGui.QColor("#cc0000")
        right_color = QtGui.QColor("#ff0000")
        bottom_color = QtGui.QColor("#661900")

        path_left = QtGui.QPainterPath()
        path_left.lineTo(pos_top)
        path_left.lineTo(pos_left)

        path_right = QtGui.QPainterPath()
        path_right.lineTo(pos_top)
        path_right.lineTo(pos_right)

        path_bottom = QtGui.QPainterPath()
        path_bottom.lineTo(pos_left)
        path_bottom.lineTo(pos_right)

        painter.setPen(QtGui.QColor("black"))
        painter.setBrush(left_color)
        painter.drawPath(path_left)
        painter.setBrush(right_color)
        painter.drawPath(path_right)
        painter.setBrush(bottom_color)
        painter.drawPath(path_bottom)

    def moveTo(self, next_position, duration=100):
        self._animation = QtCore.QVariantAnimation()
        self._animation.setStartValue(self.pos())
        self._animation.setEndValue(next_position)
        self._animation.setDuration(duration)
        self._animation.start(QtCore.QAbstractAnimation.DeleteWhenStopped)
        self._animation.valueChanged.connect(self.setPos)


class MainWindow(QtWidgets.QMainWindow):
    VIEW_HEIGHT = 600
    VIEW_WIDTH = 900

    def __init__(self, parent=None):
        super().__init__(parent)

        self.scene = QtWidgets.QGraphicsScene(self)
        self.view = QtWidgets.QGraphicsView(self.scene)
        self.status_btn = QtWidgets.QPushButton("Status")
        self.settings_btn = QtWidgets.QPushButton("Settings")
        self.analyze_btn = QtWidgets.QPushButton("Analyze")
        self.field_btn = QtWidgets.QPushButton("Field")
        self.guide_btn = QtWidgets.QPushButton("Guide")
        self.mapping_btn = QtWidgets.QPushButton("Mapping")

        central_widget = QtWidgets.QWidget()
        self.setCentralWidget(central_widget)

        vlayl = QtWidgets.QVBoxLayout()
        vlayl.addWidget(self.status_btn)
        vlayl.addWidget(self.settings_btn)
        vlayl.addWidget(self.analyze_btn)

        vlayr = QtWidgets.QVBoxLayout()
        vlayr.addWidget(self.field_btn)
        vlayr.addWidget(self.guide_btn)
        vlayr.addWidget(self.mapping_btn)

        hlay = QtWidgets.QHBoxLayout(central_widget)
        hlay.addLayout(vlayl)
        hlay.addWidget(self.view)
        hlay.addLayout(vlayr)

        self.status_btn.clicked.connect(self.rotate_arrow)
        self.guide_btn.clicked.connect(self.rotate_pixmap)

        self.view.setFixedSize(self.VIEW_WIDTH, self.VIEW_HEIGHT)
        r = self.view.mapToScene(self.view.viewport().rect()).boundingRect()
        self.view.setSceneRect(r)

        factor = 1.5 * max(self.VIEW_WIDTH, self.VIEW_HEIGHT)
        pixmap = QtGui.QPixmap("ola.png").scaled(factor, factor)
        self.pixmap_item = self.scene.addPixmap(pixmap)
        center = self.pixmap_item.boundingRect().center()
        self.pixmap_item.setPos(-center)
        self.pixmap_item.setTransformOriginPoint(center)

        self.arrow_item = ArrowItem()
        self.scene.addItem(self.arrow_item)

    def rotate_arrow(self):
        delta = 30.0
        self.arrow_item.setRotation(self.arrow_item.rotation() + delta)

    def rotate_pixmap(self):
        delta = 15.0

        self.pixmap_item.setRotation(self.pixmap_item.rotation() + delta)
        self.arrow_item.setRotation(self.arrow_item.rotation() + delta)


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = MainWindow()
    w.show()
    sys.exit(app.exec_())