一 基础知识
图形视图框架是基于笛卡尔坐标系统的,一个图形项在场景中的位置和几何形状由x坐标和y坐标来表示。当使用一个没有变换的视图来观察场景时,场景中的一个单元代表屏幕上的一个像素。图形视图框架的三个坐标系分别是视图坐标系、场景坐标系和图元坐标系。三个坐标系分别由视图、场景和图元使用。为了方便应用,图形视图框架中提供了一些便捷函数来完成3个坐标系统之间的映射。当进行绘图时,场景坐标对应QPainter的逻辑坐标,视图坐标对应设备坐标。
比如在给场景中添加图元时,图元的位置就是相对于场景坐标系而言的:
1.1 场景坐标系
场景坐标系是所有图元对象的绝对坐标. 有四个象限,场景坐标是所有图形项的基础坐标系统。场景坐标系统描述了每一个顶层图形項的位置,也形成了所有从视图传到场景上的事件的基础。场景坐标的原点在场景的中心,x轴正方向向右,y轴正方向向下。
每一个在场景中的图形项除了拥有一个图形項的本地坐标和边界矩形外,还都拥有一个场景坐标(QGraphicsItem: :scenePos())和一个场景中的边界矩形(QGraphicsItem::sceneBoundingRect())。场景坐标用来描述图形项在场景坐标系统中的位置,而图形项的场景边界矩形用于QGraphicsScene判断场景中的哪些区域进行了更改。
常用添加图元函数:
QGraphicsScene::setBackgroundBrush //填充背景色
QGraphicsScene::setForegroundBrush //填充前景色
QGraphicsScene::addSimpleText //添加简单文本
QGraphicsScene::addLine //添加直线
QGraphicsScene::addRect //添加矩形
QGraphicsScene::addEllipse //添加椭圆
QGraphicsScene::addWidget //添加窗口
QGraphicsScene::addPixmap //添加图片
操作图元函数:
QGraphicsScene::itemAt //查找场景某个中最表层的item
QGraphicsScene::setSelectionArea //设置选定区域
QGraphicsScene::setSceneRect //设置场景的区域大小
QGraphicsScene::itemsBoundingRect //根据所有的item计算区域大小
QGraphicsScene:: selectedItems //获取被选中的item,item必须为可选QGraphicsItem::ItemIsSelectable
QGraphicsScene类的坐标系以中心为原点(0,0),如下图所示。
1.2 视图坐标
视图的坐标就是窗口部件的坐标。视图坐标的每一个单位对应一个像素。QGraphicsView视图的左上角是(0,0),x轴正方向向右,y轴正方向向下。
所有的鼠标事件最开始都是使用视图坐标。 QGraphicsView类继承自QWidget类,因此它与其他的QWidget类一样,以窗口的左上角作为自己坐标系的原点,如图所示。
1.3 图形项坐标
图形项使用自己的本地坐标,这个坐标系统通常以图形项中心为原点,这也是所有变换的原点。图形项坐标方向是x轴正方向向右,y轴正方向向下。创建图形项后,只需注意图形项坐标就可以了,QGraphicsScene和QGraphicsView会完成所有的变换。
一个item的位置是item坐标系下的中心点在其父对象坐标系下的位置,有时也被称为父坐标。对于所有没有父对象的对象来说,场景就是其父对象。因此最顶层items的位置就是其在scene中的位置。
子对象坐标系是相对于父对象坐标系来说的一个概念。如果子节点没有进行矩阵变换,那么在子对象坐标系和父对象坐标系的差异就和这些对象在父对象中的偏移一样。比如:如果一个未经变换的子对象精确的位于父对象的中心点,那么这两个对象的坐标系就是完全一致的。如果子对象的位置是(10, 0),那么子对象的(0, 10)点就位于父对象的(10, 10)点的位置。
由于items的位置和转换是相对于父对象来说的,因此虽然父对象的变换隐式地变换了子对象,子对象的坐标系不会因父对象坐标系改变而改变。在上面的例子中,即使父对象经过了旋转和缩放,子对象的(0, 10)点依然相对于父对象是(10, 10)点。不过相对于scene来说,子对象将随着父对象进行变换和偏移 。如果父对象缩放了(2x, 2x),那么子对象在场景坐标系下将会位于(20, 0)的位置,同时其(10, 0) 点将会对应于场景中的(40, 0)点。
不管item或父对象进行了什么样的变化, QGraphicsItem的函数一般总是表示在item坐标系下的位置,其操作也作用于item坐标系内。比如:一个对象的矩形边界(QGraphicsItem::boundingRect())总是在item坐标系下给出的。但是QGraphicsItem::pos()是例外之一,该函数表示其在父对象中的位置 。
QGraphicsItem::rect //不带边框的图形区域
QGraphicsItem::boundingRect //带边框的图形区域
QGraphicsItem::collidesWithItem //碰撞检测
QGraphicsItem::setScale //缩放
QGraphicsItem::setRotation //旋转
QGraphicsItem::setZValue //设置z坐标,图元的叠加先后顺序可以用它来设置
QGraphicsItem::setPos //设置位置坐标
设置item的属性:
void QGraphicsItem::setFlags(GraphicsItemFlags flags);
/*
*参数GraphicsItemFlags flags 为枚举类型,可以以下值
* QGraphicsItem::ItemIsMovable 是否可以移动
* QGraphicsItem::ItemIsSelectable 是否可以被选中
* QGraphicsItem::ItemIsFocusable 是否可以设置为焦点item
*/
QGraphicsItem类的坐标系,若在调用QGraphicsItem类的paint()函数重绘图元时,则以此坐标系为基准,如下图所示。
二 、 坐标映射转换
当处理场景中的图形项时,将坐标或者一个任意的形状从场景映射到图形项、或者从一个图形项映射到另一个图形项、或者从视图映射到场景,这些坐标变换都是非常有用的。例如:
- 当在QGraphicsView的视口上单击了鼠标,便可以调用QGraphicsView::mapToScene()以及 QGraphicsScene::itemAt()来获取光标下的图形项;
- 如果要获取一个图形项在视口中的位置,那么可以先在图形项上调用QGraphicsItem::mapToScene(),然后在视图上调用QGraphicsView: :mapFromScene();如果要获取在视图的一个椭圆形中包含的图形项,可以先传递一个QPainterPath对象作为参数给mapToScene()函数,然后传递映射后的路径给QGraphicsScene::items()函数。
不仅可以在视图、场景和图形项之间使用坐标映射,还可以在子图形项和父图形项或者图形项和图形项之间进行坐标映射 。图形视图框架提供的所有映射函数如下表所列,所有的映射函数都可以映射点、矩形、多边形和路径。
映 射 函 数 | 转 换 类 型 |
---|---|
QGraphicsView::mapToScene() | 视图到场景 |
QGraphicsView::mapFromScene() | 场景到视图 |
QGraphicsItem:: mapFromScene() | 场景到图形项 |
QGraphicsItem:: mapToScene() | 图形项到场景 |
QGraphicsItem:: mapToParent() | 子图形项到父图形项 |
QGraphicsItem:: mapFromParent() | 父图形项到子图形项 |
QGraphicsItem:: mapToItem() | 本图形项到其他图形项 |
QGraphicsItem:: mapFromItem() | 其他图形项到本图形项 |
调整坐标系的的类是QTransform、QMatrix,这两个类都提供了缩放、旋转、变形坐标系的方法。
2.1 调整QGraphicsVeiw坐标系
#include <QApplication> #include <QGraphicsView> #include <QGraphicsRectItem> #include <QLabel> #include <QTransform> int main(int argc, char** argv) { QApplication app(argc, argv); QGraphicsView view; QGraphicsScene scene; view.setScene(&scene); view.show(); view.resize(400, 400); QTransform transform; //transform.translate(100, 0); //移动坐标原点 transform.scale(1.5, 1); //缩放 //transform.rotate(10); //旋转 // transform.shear(0.1, 0.1); //变形 view.setTransform(transform); /*添加矩形*/ scene.addRect(0, 100, 100, 100, QPen(Qt::yellow), QBrush(Qt::blue)); return app.exec(); }
2.2 调整QGraphicsItem坐标系
#include <QApplication> #include <QGraphicsView> #include <QTransform> #include <QGraphicsRectItem> int main(int argc, char** argv) { QApplication app(argc, argv); QGraphicsView view; QGraphicsScene scene; view.setScene(&scene); view.show(); view.resize(400, 400); QTransform transform; //transform.translate(100, 0); //移动坐标原点 transform.scale(1.5, 1); //缩放 //transform.rotate(10); //旋转 // transform.shear(0.1, 0.1); //变形 /*添加矩形*/ QGraphicsRectItem *rect = scene.addRect(0, 100, 100, 100 , QPen(Qt::yellow), QBrush(Qt::blue)); rect->setTransform(transform); return app.exec(); }