I need to rotate a QGraphicsPixmapItem
through a circle. That is, the circle always needs to be at the top left corner of the image and when I drag the circle, the image has to rotate. I set the rotation angle using setRotation
and the rotation point with setTransformOriginPoint
. However, the rotation is not working fine. Could someone point me in the right direction please?
import sys
from PyQt5.QtWidgets import QMainWindow, QApplication, QGraphicsView
from PyQt5 import QtGui, QtWidgets, QtCore
import math
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.scene = Scene()
self.view = QGraphicsView(self)
self.setGeometry(10, 30, 850, 600)
self.view.setGeometry(20, 22, 800, 550)
self.setFixedSize(850, 600)
self.view.setScene(self.scene)
class Scene(QtWidgets.QGraphicsScene):
def __init__(self, parent=None):
super(Scene, self).__init__(parent)
# other stuff here
self.set_image()
def set_image(self):
image = Image()
self.addItem(image)
image.set_pixmap()
class Image(QtWidgets.QGraphicsPixmapItem):
def __init__(self, parent=None):
super(Image, self).__init__(parent)
self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
self.setTransformOriginPoint(self.boundingRect().center())
def set_pixmap(self):
pixmap = QtGui.QPixmap("image.jpg")
self.setPixmap(pixmap)
self.pixmap_controller = PixmapController(self)
self.pixmap_controller.set_pixmap_controller()
class PixmapController(QtWidgets.QGraphicsEllipseItem):
def __init__(self, pixmap):
super(PixmapController, self).__init__(parent=pixmap)
self.pixmap = pixmap
color = QtGui.QColor(0, 0, 0)
brush = QtGui.QBrush(color)
self.setBrush(brush)
def set_pixmap_controller(self):
self.setRect(-5, -5, 10, 10)
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.LeftButton:
self.startPos = event.pos()
def mouseMoveEvent(self, event):
if event.buttons() == QtCore.Qt.LeftButton:
self.finalPos = event.pos()
delta = self.finalPos - self.startPos
item_position = self.pixmap.transformOriginPoint()
angle = math.atan2(item_position.y() - self.finalPos.y(), item_position.x() - self.finalPos.x()) / math.pi * 180 - 45
self.parentItem().setRotation(angle)
self.parentItem().setPos(self.parentItem().pos() + delta)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
As with your previous question, you have to consider that the item coordinate system is based on its parent (or the scene, if there's none).
There are also two problems with your code:
QPointF()
, as in the top left corner of the item) as the bounding rect is null at that time; this will make the transformation inconsistent, as the origin will always be on the top left of the pixmap item, not its center;If you still want to be able to do both transformations individually using mouse movements, you can use the event modifiers to choose which transformation actually apply. In the following example, the normal mouse movement causes translation, while keeping pressed the Ctrl key when clicking will apply a rotation.
class Image(QtWidgets.QGraphicsPixmapItem):
# ...
def set_pixmap(self):
pixmap = QtGui.QPixmap("image.jpg")
self.setPixmap(pixmap)
self.pixmap_controller = PixmapController(self)
self.pixmap_controller.set_pixmap_controller()
class PixmapController(QtWidgets.QGraphicsEllipseItem):
# ...
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.LeftButton:
self.startPos = event.pos()
self.origin = self.parentItem().transformOriginPoint()
self.startAngle = math.atan2(
self.origin.y(),
self.origin.x()
) / math.pi * 180 - 45
self.isRotating = event.modifiers() == QtCore.Qt.ControlModifier
def mouseMoveEvent(self, event):
if event.buttons() == QtCore.Qt.LeftButton:
self.finalPos = event.pos()
delta = self.finalPos - self.startPos
angle = math.atan2(
self.origin.y() - delta.y(),
self.origin.x() - delta.x()
) / math.pi * 180 - 45
if self.isRotating:
self.parentItem().setRotation(
self.parentItem().rotation() + (angle - self.startAngle))
else:
self.parentItem().setPos(self.parentItem().pos() + delta)
A small suggestion: functions should always be created for their reusability and/or readability of the code; since you're always creating the same rectangle for the ellipse item (and that rectangle is always based on the parent's coordinate system), there's no use for a dedicate function like set_pixmap_controller
and its related call, just use setRect()
in the __init__
.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.