Issue
What is the best way to retrieve the QGraphicsRectItem coordinates with respect to the QGraphicsPixmapItem on the scene?
Here is the code for my main window class.
import sys
from PySide2 import QtCore, QtGui
from PySide2.QtWidgets import *
from PySide2.QtGui import QBrush, QPen
class main_window(QWidget):
def __init__(self):
super().__init__()
self.setGeometry(100, 100, 500, 500)
self.rect = ResizableRect(100, 100, 100, 100)
self.rect.setZValue(1)
self.rect.setRotation(10)
self.view = QGraphicsView(self)
self.scene = QGraphicsScene(self.view)
self.scene.addItem(self.rect)
pixmap = QtGui.QPixmap("images/sadcat.jpg")
pixmap_item = self.scene.addPixmap(pixmap)
pixmap_item.setPixmap(pixmap)
self.view.setSceneRect(0, 0, 500,500)
self.view.setScene(self.scene)
self.slider = QSlider(QtCore.Qt.Horizontal)
self.slider.setMinimum(0)
self.slider.setMaximum(90)
vbox = QVBoxLayout(self)
vbox.addWidget(self.view)
vbox.addWidget(self.slider)
self.setLayout(vbox)
self.slider.valueChanged.connect(self.rotate)
def rotate(self, value):
self.angle = int(value)
self.rect.setRotation(self.angle)
Here is the code for the custom QGraphicsRectItem (credits to Resize a QGraphicsItem with the mouse)
class ResizableRect(QGraphicsRectItem):
def __init__(self, *args):
super().__init__(*args)
self.setFlag(QGraphicsItem.ItemIsMovable, True)
self.setFlag(QGraphicsItem.ItemIsSelectable, True)
self.setFlag(QGraphicsItem.ItemSendsScenePositionChanges, True)
self.setPen(QPen(QBrush(QtGui.QColor('red')), 5))
self.selected_edge = None
self.first_pos = self.click_rect = None
def mousePressEvent(self, event):
""" The mouse is pressed, start tracking movement. """
self.first_pos = event.pos()
self.rect_shape = self.rect()
if abs(self.rect_shape.left() - self.first_pos.x()) < 5:
self.selected_edge = 'left'
elif abs(self.rect_shape.right() - self.first_pos.x()) < 5:
self.selected_edge = 'right'
elif abs(self.rect_shape.top() - self.first_pos.y()) < 5:
self.selected_edge = 'top'
elif abs(self.rect_shape.bottom() - self.first_pos.y()) < 5:
self.selected_edge = 'bottom'
else:
self.selected_edge = None
self.first_pos = event.pos()
self.click_rect = self.rect_shape
super().mousePressEvent(event)
def mouseMoveEvent(self, event):
""" Continue tracking movement while the mouse is pressed. """
# Calculate how much the mouse has moved since the click.
self.pos = event.pos()
x_diff = self.pos.x() - self.first_pos.x()
y_diff = self.pos.y() - self.first_pos.y()
# Start with the rectangle as it was when clicked.
self.rect_shape = QtCore.QRectF(self.click_rect)
# Then adjust by the distance the mouse moved.
if self.selected_edge is None:
self.rect_shape.translate(x_diff, y_diff)
elif self.selected_edge == 'top':
self.rect_shape.adjust(0, y_diff, 0, 0)
elif self.selected_edge == 'left':
self.rect_shape.adjust(x_diff, 0, 0, 0)
elif self.selected_edge == 'bottom':
self.rect_shape.adjust(0, 0, 0, y_diff)
elif self.selected_edge == 'right':
self.rect_shape.adjust(0, 0, x_diff, 0)
self.setRect(self.rect_shape)
coor = self.rect_shape.getRect()
self.setTransformOriginPoint(coor[0] + coor[2]/2, coor[1] + coor[3]/2)
print(coor)
The main window GUI can be seen below.
Solution
Qt Graphics Framework handles several coordinate systems:
- With respect to the scene.
- With respect to the viewport.
- With respect to any item.
And QGraphicsView, QGraphicsScene, and QGraphicsItems have methods that allow conversion between the various types of coordinate systems.
In general the implementation is to convert any position with respect to X to coordinates of the scene and then convert the coordinates with respect to Y.
It should also be known that the coordinates with respect to the scene and the items are in floating point: QPointF
, QRectF
and QPolygonF
but can be converted to integer values using the toPoint()
, toRect()
and toPolygon()
methods, respectively.
So in this case you can convert the boundingRect()
of the QGraphicsRectItem that are with respect to the item to coordinates of the scene, and then convert them with respect to the QGraphicsPixmapItem:
import random
from PySide2 import QtCore, QtGui, QtWidgets
def build_pixmap():
pixmap = QtGui.QPixmap(400, 400)
pixmap.fill(QtCore.Qt.transparent)
painter = QtGui.QPainter(pixmap)
painter.setRenderHints(
QtGui.QPainter.Antialiasing | QtGui.QPainter.SmoothPixmapTransform
)
painter.setPen(QtCore.Qt.NoPen)
for _ in range(100):
x, y = random.sample(range(-100, 400), 2)
color = QtGui.QColor(*random.sample(range(255), 3))
painter.setBrush(color)
painter.drawEllipse(QtCore.QRect(0, 0, 100, 100).translated(x, y))
painter.end()
return pixmap
def main():
app = QtWidgets.QApplication()
scene = QtWidgets.QGraphicsScene()
view = QtWidgets.QGraphicsView(
scene,
renderHints=QtGui.QPainter.Antialiasing | QtGui.QPainter.SmoothPixmapTransform,
)
rect_item = QtWidgets.QGraphicsRectItem()
rect_item.setPen(QtGui.QPen(QtGui.QColor("red"), 5))
rect_item.setBrush(QtGui.QColor("gray"))
rect_item.setRect(QtCore.QRectF(-30, -40, 100, 200))
rect_item.setPos(QtCore.QPointF(170, 150))
rect_item.setTransformOriginPoint(30, 20)
rect_item.setRotation(30)
pixmap_item = QtWidgets.QGraphicsPixmapItem()
pixmap_item.setPixmap(build_pixmap())
scene.addItem(pixmap_item)
scene.addItem(rect_item)
scene_coordinate = rect_item.mapToScene(rect_item.boundingRect())
# view_coordinate = view.mapFromScene(scene_coordinate)
pixmap_coordinate = pixmap_item.mapFromScene(scene_coordinate)
for point in pixmap_coordinate.toPolygon():
print(point)
view.resize(640, 480)
view.show()
app.exec_()
if __name__ == "__main__":
main()
Output:
PySide2.QtCore.QPoint(177, 85)
PySide2.QtCore.QPoint(268, 137)
PySide2.QtCore.QPoint(166, 315)
PySide2.QtCore.QPoint(75, 262)
PySide2.QtCore.QPoint(177, 85)
Answered By - eyllanesc
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.