【问题标题】:How to retrieve QGraphicsRectItem coordinate with respect to a QGraphicsPixmapItem on a scene?如何检索关于场景上 QGraphicsPixmapItem 的 QGraphicsRectItem 坐标?
【发布时间】:2021-11-17 17:17:29
【问题描述】:

相对于场景中的 QGraphicsPixmapItem 检索 QGraphicsRectItem 坐标的最佳方法是什么?

这是我的主窗口类的代码。

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)

这是自定义 QGraphicsRectItem 的代码(感谢 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)

主窗口 GUI 如下所示。

【问题讨论】:

  • Graphics View 框架中的所有坐标都是基于浮点的。不幸的是,您没有提供minimal reproducible example,因此很难更好地理解您的问题。除此之外,如果你需要将变换的原点设置为矩形的中心,你可以使用rect.center()
  • 嗨@musicamante!谢谢你一直对我这么有耐心。我稍后会详细说明我的问题。
  • (供将来参考,无需告诉您要编辑问题,只需在更新后添加评论,因为 cmets 应始终保持在最低限度)现在,问题有点复杂。事实是 QGraphicsRectItem 的矩形并不总是与项目的位置匹配:您可以将 QGraphicsRectItem 定位在 10x10 处,使用 rect(20, 50, 50, 100),结果是矩形将被绘制30x60。添加旋转使其更加复杂,因为矩形的原点将在项目坐标中考虑,但如果您需要 ->
  • -> 要知道矩形的实际原点,您必须将其映射到场景(考虑任何变换,包括旋转和缩放)。因此,必须了解三件事:1.您对什么坐标感兴趣?现在你总是得到 item 坐标中的矩形; 2.那个矩形的最终目的是什么(我相信它正在裁剪,但我不确定)? 3. 这些坐标是如何使用的,或者你想向用户展示什么?
  • @musicamante 这些值与场景相关,而不是视口。您想要矩形相对于哪个坐标系:图像或视口或自身项目或场景?

标签: python pyqt pyside qgraphicsscene qgraphicsitem


【解决方案1】:

Qt 图形框架处理几个坐标系:

  • 关于场景。
  • 关于视口。
  • 关于任何项目。

并且 QGraphicsView、QGraphicsScene 和 QGraphicsItems 具有允许在各种类型的坐标系之间进行转换的方法。

一般来说,实现是将相对于 X 的任意位置转换为场景的坐标,然后再转换相对于 Y 的坐标。

还应该知道,相对于场景和项目的坐标是浮点数:QPointFQRectFQPolygonF,但可以使用 toPoint()、@987654325 将其转换为整数值@ 和 toPolygon() 方法。

所以在这种情况下,您可以将 QGraphicsRectItem 的 boundingRect() 相对于项目转换为场景的坐标,然后将它们相对于 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()

输出:

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)

【讨论】:

  • 我理解你的解释。还有一件事,当我设置 rect_item.setPos(QtCore.QPointF(0, 0)) 时,print(point) 的第一个输出是 (-2, -2)。不应该是 (0, 0) 吗?
  • @husniandre 您总是会在转换中看到错误,因此您可以应用更正
  • 很抱歉再次打扰您,如果我将项目的初始旋转设置为超过 45 度,比如说 90 度,然后尝试拖动和移动它,项目会随机移动。当我在 mouseMoveEvent 方法上打印 self.pos 时,x 和 y 位置变得如此巨大。你知道为什么会发生这种情况@eyllanesc 吗?
  • @husniandre 这个问题与我的回答无关,也与您在帖子中的要求无关,所以我不会回答。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-11-12
  • 1970-01-01
  • 2012-06-19
  • 2021-11-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多