作者:doctorsc
转自:http://blog.csdn.net/doctorsc/article/details/6774983
这几天做迷彩设计相关编程用了很多QPainter相关的操作,写了不少Qt代码让我对Qt又有了进一步的认识。最近翻看Qt 的 Demos and Examples 发现在Graphics view里面有一个elastic node的示例,程序仅仅定义了背景和小球绘制操作便完成了复杂的鼠标,键盘等交互。在让我大为惊叹Qt 强大的同时,也决定学一学。查找了Qt 帮助文档,这一系列操作都用到了Qt Graphics View Framework。
Graphics View 框架提供了一个界面,用于管理和交互大量的用户自定义的2D图形物件,并且有一个view widget(QGraphicsView)用户可视化这些物件,支持zoom和rotate。这个框架包含了一个事件传播构架,允许了double精度的场景中物体交互能力。物件的键盘,鼠标的按下,移动,松开,双击事件都已经定义好了,并且可以跟踪鼠标移动。
Graphics view 构架
所有的item都被绘制到了一个场景中用于显示,这个场景就是QGraphicsScene,这个场景有如下功能:
1、 为管理大量item提供了一个快速交互界面。
2、 能够将键盘,鼠标等事件传递到每个item。
3、 可以管理item的状态,例如选择,焦点等。
4、 提供了不变形的绘制,主要用于打印绘画结果。
场景就像一个容易囊括了所有item,可以通过addItem()添加物体,可以通过item()查找物体,itemAt()返回最上面的item,所有item按照降序堆栈排列。第一个加入的在栈顶,最后一个加入的在栈底。
QGraphicsScene的事件传递架构非常给力,能够将场景获得的事件精确传递到相应item,例如鼠标在某点点击了一下,Scene能够将这个事件传递给在这个点上的item(最上面的那个item),于是这个item被选中或者执行别的动作,具体取决于item的mousePressEvent函数的处理。
QGraphicsScene中,你可以用setSelectionArea()选择区域中的许多个item,仅仅需要调用这个函数你就能获得选中item的结果,真是太强大了,如果要自己写函数鼠标选择的区域选了哪些item,你需要遍历每个item,对于每个item又要判断是否全部在区域内,于是item要包围核,要范围面积等等变量来表示。想想就觉得真心麻烦。这个函数确实为开发者节约了不少时间。
View
QGraphicsView提供了用于显示的widget,用于显示scene,你可以将多个view联系到同一个scene,给相同的数据提供多个视口,视口支持openGL,甚至可以将QGLWidget作为视口,只要调用一下QGraphicsView::setViewport()。View接收交互事件,进行坐标转换后变成scene event交给场景。QGraphicsView::mapToScene(), QGraphicsView::mapFromScene()等多个函数可以在view和scene之间转换坐标系。
- QGraphicsScene scene;
- myPopulateScene(&scene);
- QGraphicsView view(&scene);
- view.show();
Item
QGraphicsItem是场景中所有物件的基类,GraphicsView也提供了一些标准item,例如矩形QGraphicsRectItem,椭圆QGraphicsEllipseItem,还有文本,QGraphicsTextItem,最给力的当然是你自定义的item,item支持以下操作:
1、 鼠标点击,移动,松开,双击,飞越,滚轮,菜单事件。
2、 键盘输入。
3、 拖拽
4、 Group,可以通过父子关系或者QGraphicsItemGroup
5、 碰撞检测
每个Item都有一个局部坐标系,可以通过这个坐标系进行选择,移动等操作,transform()函数通过控制矩阵可以轻松实现这些操作。
有一个很NB的功能就是item支持碰撞检测,怎么检测呢,首先需要为每一类item定义好它的bounding box,同过boundingRect()函数可以设置这个item外围矩形包围盒,而更精确的是用shape()函数定义item外边框路径,这个路径是QPainterPath,这个路径可以是直接,贝塞尔曲线等任何线条,collidesWith()是一个虚函数,定义完成后便能完成碰撞检测功能了。
其他的Graphics类
QGraphicsAnchorLayout是用于将几个widget以锚点的形式随意排列的layout。
QGraphicsEffect提供了一些图形特效。
主要有以下4个标准特效:
Qt provides the following standardeffects:
· QGraphicsBlurEffect - blurs the item by a given radius
· QGraphicsDropShadowEffect - renders a dropshadow behind the item
· QGraphicsColorizeEffect - renders the item in shades of any given color
· QGraphicsOpacityEffect - renders the item with an opacity
Graphics view 坐标系
坐标系由(x,y)表示,一个单位就是屏幕上一个像素。在这个框架内一共有3个坐标系,item局部坐标系,场景坐标系,view坐标系,有函数用于在这些坐标系之间进行映射。
Item coordinates
Item的坐标建立在它的中心点(0,0)附近,这也是所有变形的中心位置,几何图元常常由局部坐标系中的点,线,矩形来构建。
构建自定义的item时,仅仅需要考虑局部坐标就行了,QGraphicsView和QGraphicsScene会进行其他所有的变形。鼠标事件用于item时,会自动将点转换到item坐标系中。Item的bounding rect 和 shape都在局部坐标系中定义。
子item的中心位置将会保存到父item的坐标系中用于自动查找判断,最上层item的中心坐标将会保存到scene坐标系中。
父item的旋转缩放操作将会影响子item跟着一起变,但不会影响子item与父Item的坐标系,例如,有父子2个item,在父坐标系中,子的位置在(10,0)。于是,子坐标系中位置为(0,10)的点在父坐标系下的坐标是(10,10)。然后,我对父item进行了旋转和缩放,子坐标系下(0,10)的点在父坐标系下仍然是(10,10),但是在scene坐标系下就会产生变化了,子item会随着父产生形变,如果父进行了缩放为(2x,2x),在scene坐标系下,子的位置为(20,0),子坐标系中(10,0)的点在scene下的坐标为(40,0)。QGraphicsItem::pos()是少数几个例外的,它返回item在父坐标系下的坐标.
旋转和缩放操作相当简单,在QGraphicsView中调用ratate()和scale()函数。
————————————————————————————————————————————————————————
光说不练嘴把式,必须随便搞个test project。
GraphicWidget继承自QGraphicsView,重载了一个函数,drawBackground().
EllipseItem继承自QGraphicsItem,重载了若干函数:
- QRectF boundingRect() const;
- void paint(QPainter *painter, const QStyleOptionGraphicsItem*option, QWidget *widget);
- protected:
- QVariant itemChange(GraphicsItemChange change, const QVariant &value);
- void mousePressEvent(QGraphicsSceneMouseEvent *event);
- void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
boundingRect必须得重载,不然编译器说这个是抽象类,不能够实例化。
在mousePressEvent()中调用QGraphicsItem::mousePressEvent(event);便万事大吉,方便shi了。
其他雷同。
在EllipseItem中保存了父节点指针。
在graphicWidget构造函数中new一个scene,设置一下,然后new一个EllipseItem出来,放入scene,指定位置,就可以了。
出来的结果是,你可以任意移动出现的小圆球。
待续...