python - 无法在场景中正确定位 QGraphicsRectItem

标签 python c++ qt pyqt qgraphicsitem

我一生都无法弄清楚这一点,但我已将其归结为一个独立的问题。

我想做的是围绕在QGraphicsScene中选择的项目绘制一个QGraphicsRectItem。绘制矩形后,可以通过将所有项目移动到一起的方式来移动它。我已经研究过 QGraphicsItemGroup 并认为它在我的实际用例中不可行。

问题:我已经能够完成上述所有事情,除了我无法正确定位矩形项目,即它的大小正确通过移动它,所有项目都会被移动,但它不会与所选项目的 union 边界矩形对齐。我尝试将所有内容保留在场景坐标中,所以我不确定为什么会有偏移。

为什么会出现偏移?如何缓解这种情况?

这是可运行的代码,可以通过按住 Ctrl 键单击或橡皮筋选择来测试(我知道这是大量代码,但相关部分已注释)。

#####The line where the position of the rect item is set is marked like this#####

from PyQt4.QtGui import *
from PyQt4.QtCore import *
import sys


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

        self.selBox = None
        self.selectionChanged.connect(self.onSelectionChange)

    @pyqtSlot()
    def onSelectionChange(self):
        count = 0
        items = self.selectedItems()

        # Get bounding rect of all selected Items
        for item in self.selectedItems():
            if count == 0:
                rect = item.mapRectToScene(item.boundingRect())
            else:
                rect = rect.unite(item.mapRectToScene(item.boundingRect()))
            count += 1

        if count > 0:
            if self.selBox:
                # Update selBox if items are selected and already exists
                self.selBox.setRect(rect)
                self.selBox.items = items
            else:
                # Instantiate selBox if items are selected and does not already exist
                self.selBox = DiagramSelBox(rect, items)
                ##### Set position of selBox to topLeft corner of united rect #####
                self.selBox.setPos(rect.topLeft())
                self.addItem(self.selBox)
        elif self.selBox:
            # Remove selBox from scene if no items are selected and box is drawn
            self.removeItem(self.selBox)
            del self.selBox
            self.selBox = None


class DiagramSelBox(QGraphicsRectItem):
    def __init__(self, bounds, items, parent=None, scene=None):
        super().__init__(bounds, parent, scene)

        self.setFlag(QGraphicsItem.ItemIsSelectable, True)
        self.pressPos = None
        self.items = items

    def paint(self, painter, option, widget=None):
        pen = QPen(Qt.DashLine)
        painter.setPen(pen)
        painter.drawRect(self.rect())

    def mousePressEvent(self, e):
        # Get original position of selBox when clicked
        self.pressPos = self.pos()
        # mouseEvent is not passed on to scene so item selection
        # does not change

    def mouseMoveEvent(self, e):
        super().mouseMoveEvent(e)
        if self.pressPos:
            # Move selBox is original position is set
            newPos = self.mapToScene(e.pos()) - self.rect().center()
            self.setPos(newPos)

    def mouseReleaseEvent(self, e):
        # Update position of all selected items
        change = self.scenePos() - self.pressPos
        for item in self.items:
            item.moveBy(change.x(), change.y())

        super().mouseReleaseEvent(e)


if __name__ == "__main__":

    app = QApplication(sys.argv)

    view = QGraphicsView()
    view.setDragMode(QGraphicsView.RubberBandDrag)

    scene = DiagramScene()

    scene.setSceneRect(0, 0, 500, 500)

    rect1 = scene.addRect(20, 20, 100, 50)
    rect2 = scene.addRect(80, 80, 100, 50)
    rect3 = scene.addRect(140, 140, 100, 50)

    rect1.setFlag(QGraphicsItem.ItemIsSelectable, True)
    rect2.setFlag(QGraphicsItem.ItemIsSelectable, True)
    rect3.setFlag(QGraphicsItem.ItemIsSelectable, True)

    view.setScene(scene)
    view.show()

    sys.exit(app.exec_())

最佳答案

我没有安装 PyQt,但我在使用常规 QT 和 QGraphicsRectItem 时遇到了类似的问题。

我认为您混淆了有关坐标系的一些内容。每个 QGraphicsItem 的边界矩形都位于本地坐标中。本地坐标中的点 (0,0) 出现在场景中由 QGraphicsItem::pos() (scene-coordiantes) 给出的坐标上。

QGraphicsRectItem 有点特殊,因为我们通常根本不接触 pos (因此我们将其保留在 0,0)并在场景中传递一个矩形 -坐标到setRectQGraphicsRectItem::setRect 基本上将边界矩形设置为传递的值。因此,如果您根本不调用 setPos(在 onSelectionChange 中),而仅将场景坐标传递给 setRect,那么应该没问题。

DiagramSelBox 中的 mouseEvents 也需要调整。我的方法如下:

  1. mousePress:将e.pos(映射到场景)和self.rect.topLeft()之间的差异存储在self.diffPos中并将 self.rect.topLeft 复制到 self.startPos
  2. mouseMove:通过移动 self,确保 e.pos(映射到场景)和 self.rect.topLeft() 之间的差异保持不变.rect 周围(使用 self.diffPos 进行计算)
  3. mouseRelease:按照 self.rect.topLeft()self.startPos 之间的差异移动项目。

希望这有助于您入门。

关于python - 无法在场景中正确定位 QGraphicsRectItem,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38401086/

相关文章:

c++ - QXmlStreamReader获取标签值

python - 为新数据添加具有唯一标识符的列,但在 python 中维护先前数据的唯一标识符

python - 使用python opencv清除ocr图像

python - pymongo 和 flask 的认证问题

.net - C++ 托管和非托管静态库

python - PyQ5t : load Qt Designer into Python script (loadUiType): how to check error cause?

c++ - 无法为处于不同线程 (Qt) 中的父对象创建子对象

python - "esc"键盘键本身的 VT100 转义码是什么

c++ - 命名空间和 undefined reference

c++ - Omnet++ 错误在子类中设置参数并在另一个子类中调用它