PyQt5/QT中QGraphicsItem.setPos()方法及坐标系疑问
在理解PyQt5/QT中的QGraphicsItem.setPos()方法时遇到困惑,根源是对QGraphicsView和QGraphicsScene的坐标系体系不熟悉,查阅资料后仍未找到明确解释。
测试程序逻辑:创建QGraphicsScene和QGraphicsView后执行两步操作:
- 添加参数为
(100, 100, 20, 20)的椭圆; - 添加同样参数的椭圆并调用
setPos(50, 50)。
原本预期:两个椭圆初始都处于场景坐标(100,100)位置,尺寸20×20;调用setPos(50,50)的椭圆应移动到(50,50),但实际它出现在(150,150),相当于在初始位置上叠加了偏移量。
根据文档说明,QGraphicsItem.setPos()设置的是父坐标(由于椭圆无父项,即场景坐标),希望得到相关解释。
测试代码如下:
from PyQt5.QtWidgets import QApplication, QMainWindow, QGraphicsScene, QGraphicsView import sys from PyQt5.QtGui import QBrush, QPen from PyQt5.QtCore import Qt class Window(QMainWindow): def __init__(self): super(Window, self).__init__() self.title = "setPos() Example" self.left = 0 self.top = 0 self.width = 800 self.height = 600 # Init UI self.canvas = QGraphicsScene() self.graphics_view = QGraphicsView(self.canvas, self) self.graphics_view.setGeometry(0, 0, self.width, self.height) self.canvas.addLine(100, 100, 200, 200) # For reference ellipse1 = self.canvas.addEllipse(100, 100, 20, 20, QPen(Qt.black), QBrush(Qt.blue)) ellipse1.setPos(50, 50) ellipse2 = self.canvas.addEllipse(100, 100, 20, 20, QPen(Qt.black), QBrush(Qt.red)) self.setWindowTitle(self.title) self.setGeometry(self.left, self.top, self.width, self.height) self.show() def main(): app = QApplication(sys.argv) window = Window() sys.exit(app.exec()) if __name__ == '__main__': main()
核心解释:addEllipse参数与setPos的坐标系叠加
1. addEllipse的参数本质
QGraphicsScene.addEllipse(x, y, w, h, ...)中的x,y不是场景坐标,而是椭圆这个QGraphicsItem自身局部坐标系下的绘制起点。也就是说,椭圆的图形是画在自己的局部坐标(x,y)处,而这个局部坐标系的原点默认在场景坐标(0,0)位置。
当你调用addEllipse(100,100,20,20)时,椭圆的局部坐标原点在场景(0,0),而椭圆的图形从自身局部的(100,100)开始画,所以最终在场景中显示的位置是(0+100, 0+100),也就是(100,100)。
2. setPos()的作用
setPos(scene_x, scene_y)是设置该Item的局部坐标系原点在场景中的位置。当你给椭圆调用setPos(50,50),相当于把椭圆自身的局部坐标原点移到了场景的(50,50)处。
此时椭圆的图形是从自身局部的(100,100)开始画,所以最终在场景中的位置是(50+100,50+100),也就是(150,150),这就是你看到的叠加偏移效果。
3. 两种实现预期效果的方式
方式一:用addEllipse时只传尺寸,通过setPos定位
如果你想让椭圆在场景(50,50)处显示,应该让椭圆的局部绘制起点为(0,0),再通过setPos把局部原点移到场景(50,50):
ellipse1 = self.canvas.addEllipse(0, 0, 20, 20, QPen(Qt.black), QBrush(Qt.blue)) ellipse1.setPos(50, 50)
方式二:直接用addEllipse的场景坐标参数(无需调用setPos)
如果你不想用setPos,可以直接通过addEllipse的参数指定椭圆在场景中的位置,本质是让椭圆局部原点在场景(x,y),同时图形从局部(0,0)开始画:
# 等价于 addEllipse(0,0,20,20) + setPos(50,50) ellipse1 = self.canvas.addEllipse(50, 50, 20, 20, QPen(Qt.black), QBrush(Qt.blue))
修改后的测试代码
from PyQt5.QtWidgets import QApplication, QMainWindow, QGraphicsScene, QGraphicsView import sys from PyQt5.QtGui import QBrush, QPen from PyQt5.QtCore import Qt class Window(QMainWindow): def __init__(self): super(Window, self).__init__() self.title = "setPos() Example" self.left = 0 self.top = 0 self.width = 800 self.height = 600 # Init UI self.canvas = QGraphicsScene() self.graphics_view = QGraphicsView(self.canvas, self) self.graphics_view.setGeometry(0, 0, self.width, self.height) self.canvas.addLine(50, 50, 150, 150) # 参考线:从(50,50)到(150,150) # 方式一:addEllipse用(0,0)起点,setPos定位到场景(50,50) ellipse1 = self.canvas.addEllipse(0, 0, 20, 20, QPen(Qt.black), QBrush(Qt.blue)) ellipse1.setPos(50, 50) # 方式二:直接用addEllipse指定场景位置(100,100) ellipse2 = self.canvas.addEllipse(100, 100, 20, 20, QPen(Qt.black), QBrush(Qt.red)) self.setWindowTitle(self.title) self.setGeometry(self.left, self.top, self.width, self.height) self.show() def main(): app = QApplication(sys.argv) window = Window() sys.exit(app.exec()) if __name__ == '__main__': main()
内容的提问来源于Stack Exchange,提问作者Ecko

