QGraphicsPixmapItem позиционируется неправильно
Мне нужно переместить
QGraphicsPixmapItem
через кружок в верхнем левом углу изображения. То есть, когда я захватываю круг с помощью мыши, мне нужно, чтобы верхний левый угол изображения следовал за кругом. Я сделал подкласс
QGraphicsEllipseItem
и повторно реализовал
itemChange
, но когда я устанавливаю положение изображения на это значение, изображение позиционируется неправильно. Что мне следует изменить в моем коде?
import sys
from PyQt5.QtWidgets import QMainWindow, QApplication, QGraphicsView
from PyQt5 import QtGui, QtWidgets
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.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)
def set_pixmap(self):
pixmap = QtGui.QPixmap("image.jpg")
self.setPixmap(pixmap)
self.pixmap_controller = PixmapController(self)
self.pixmap_controller.set_pixmap_controller()
self.pixmap_controller.setPos(self.boundingRect().topLeft())
self.pixmap_controller.setFlag(QtWidgets.QGraphicsItem.ItemSendsScenePositionChanges, True)
def change_image_position(self, position):
self.setPos(position)
class PixmapController(QtWidgets.QGraphicsEllipseItem):
def __init__(self, pixmap):
super(PixmapController, self).__init__(parent=pixmap)
self.pixmap = pixmap
self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
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 itemChange(self, change, value):
if change == QtWidgets.QGraphicsItem.ItemPositionChange:
self.pixmap.change_image_position(value)
return super(PixmapController, self).itemChange(change, value)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
1 ответ
Когда у графического элемента есть родительский элемент, его система координат основана на этом родителе, а не на сцене.
Проблема в том, что при попытке переместить
PixmapController
, движение осуществляется в родительских координатах (элемент растрового изображения). Когда вы проверяете
ItemPositionChange
вы изменяете родительскую позицию, но позиция элемента все равно изменяется на основе родительской системы координат.
Хотя вы можете просто вернуть пустой QPoint (который не изменит положение элемента), это не будет хорошим выбором: как только вы отпустите кнопку мыши и снова начнете перемещать ее, растровое изображение сбросит свое положение.
Решение состоит не в установке флага подвижного элемента, а в фильтрации движений мыши, вычислении дельты на основе начальной позиции щелчка и использовании этой дельты для перемещения родительского элемента на основе его текущей позиции.
class PixmapController(QtWidgets.QGraphicsEllipseItem):
def __init__(self, pixmap):
super(PixmapController, self).__init__(parent=pixmap)
self.pixmap = pixmap
# the item should *NOT* move
# self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
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:
delta = event.pos() - self.startPos
self.parentItem().setPos(self.parentItem().pos() + delta)
Если вы хотите использовать свой
change_image_position
функции, вам необходимо соответствующим образом изменить эти функции; код ниже делает то же самое, что и последняя строка в примере выше:
class Image(QtWidgets.QGraphicsPixmapItem):
# ...
def change_image_position(self, delta):
self.setPos(self.pos() + delta)
class PixmapController(QtWidgets.QGraphicsEllipseItem):
# ...
def mouseMoveEvent(self, event):
if event.buttons() == QtCore.Qt.LeftButton:
delta = event.pos() - self.startPos
self.pixmap.change_image_position(delta)
Совет: не добавляйте дочерний виджет к QMainWindow таким образом, поскольку он не будет правильно изменять размер при изменении размера окна. Использовать
self.setCentralWidget(self.view)
вместо; если вы хотите добавить поля, используйте контейнер QWidget, установите этот виджет как центральный виджет, добавьте простой QHBoxLayout (или QVBoxLayout), добавьте представление в этот макет, а затем установите поля с помощью
layout.setContentsMargins(left, top, right, bottom)