无法在场景中正确定位 QGraphicsRectItem
Cannot properly position QGraphicsRectItem in scene
我一辈子都弄不明白这个问题,但我已将其归结为一个独立的问题。
我想做的是在 QGraphicsScene
中选择的项目周围画一个 QGraphicsRectItem
。绘制矩形后,可以以将所有项目一起移动的方式移动它。我已经研究过 QGraphicsItemGroup
并确定它在我的实际用例中不可行。
问题:我已经能够完成上面提到的所有事情,除了我无法得到正确的项目位置正确,即大小合适,通过移动它,所有项目都被移动,但它没有与所选项目的联合边界矩形对齐。我试图将所有内容都保留在场景坐标中,所以我不确定为什么会有偏移。
为什么似乎存在偏差,如何减轻这种偏差?
这是可运行的代码,可以通过 ctrl-clicking 或橡皮筋选择来测试(我知道这是一个很好的代码量,但相关部分已被注释)。
#####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)并将场景坐标中的矩形传递给 setRect
。 QGraphicsRectItem::setRect
基本上将边界矩形设置为传递的值。因此,如果您根本不调用 setPos
(在 onSelectionChange
中),只将场景坐标传递给 setRect
,您应该没问题。
DiagramSelBox
中的mouseEvents也需要调整。我的方法是这样的:
- mousePress:将
e.pos
(映射到场景)和self.rect.topLeft()
之间的差异存储在self.diffPos
中,并将self.rect.topLeft
复制到self.startPos
- mouseMove:通过移动
self.rect
来确保 e.pos
(映射到场景)和 self.rect.topLeft()
之间的差异保持不变(使用 self.diffPos
进行计算)
- mouseRelease: 将项目移动
self.rect.topLeft()
和 self.startPos
之间的差异。
希望对您有所帮助。
我一辈子都弄不明白这个问题,但我已将其归结为一个独立的问题。
我想做的是在 QGraphicsScene
中选择的项目周围画一个 QGraphicsRectItem
。绘制矩形后,可以以将所有项目一起移动的方式移动它。我已经研究过 QGraphicsItemGroup
并确定它在我的实际用例中不可行。
问题:我已经能够完成上面提到的所有事情,除了我无法得到正确的项目位置正确,即大小合适,通过移动它,所有项目都被移动,但它没有与所选项目的联合边界矩形对齐。我试图将所有内容都保留在场景坐标中,所以我不确定为什么会有偏移。
为什么似乎存在偏差,如何减轻这种偏差?
这是可运行的代码,可以通过 ctrl-clicking 或橡皮筋选择来测试(我知道这是一个很好的代码量,但相关部分已被注释)。
#####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)并将场景坐标中的矩形传递给 setRect
。 QGraphicsRectItem::setRect
基本上将边界矩形设置为传递的值。因此,如果您根本不调用 setPos
(在 onSelectionChange
中),只将场景坐标传递给 setRect
,您应该没问题。
DiagramSelBox
中的mouseEvents也需要调整。我的方法是这样的:
- mousePress:将
e.pos
(映射到场景)和self.rect.topLeft()
之间的差异存储在self.diffPos
中,并将self.rect.topLeft
复制到self.startPos
- mouseMove:通过移动
self.rect
来确保e.pos
(映射到场景)和self.rect.topLeft()
之间的差异保持不变(使用self.diffPos
进行计算) - mouseRelease: 将项目移动
self.rect.topLeft()
和self.startPos
之间的差异。
希望对您有所帮助。