Qt的二维图形引擎是基于QPainter类的,而QPaintDevice 不直接绘制物理显示画面,而利用逻辑界面的中间媒介。例如,绘制矩形图形时,为了将对象绘制到 QWidget、QGLPixelBuffer、QImage、QPixmap、QPicture 等多种界面中间,必须使用 QPaintDevice。QPaintDevice是所有绘图设备的基类。
QPainter painter;
painter.begin(this);
...
painter.end():
QPainter painter(this);
上面这两个操作的效果相同,this表明在Widget部件上进行绘制,使用这个构造函数创建的对象会立即开始在设备上进行绘制,而调用begin就可以在创建QPainter时不指定使用设备,使用时再指定.
对于画笔
QPen::QPen(const QBrush &brush,qreal width,Qt::PenStyle style=Qt::SolidLine,Qt::PenCapStyle cap=Qt::SquareCap,Qt::PenJoinStyle join=Qt::BevelJoin);
参数分别为画笔使用的画刷,线宽,画笔风格,画笔端点风格和画笔连接风格,需要注意的是拐点风格和连接风格,拐点风格有FlatCap,SquareCap,RoundCap等,连接风格有MiterJoin,BevelJoin,RoundJoin。
再值得一提的是画弧线时,角度被分成了十六分之一,就是说,如果要 30 度,就需是 30*16。它有起始角度和跨度,还有位置矩形,所以,要想画出自己想要的弧线,就需要大概估算出各个参数的预估值。
在绘图时,像素渲染由 QPainter::Antialiasing 来控制。枚举 RenderHint 用于指定 QPainter 的渲染标志,绘图引擎会用到。QPainter::Antialiasing 表明引擎应该尽可能的让图元边缘抗锯齿,即:使用不同的颜色亮度让边缘平滑
Qt有三种渐变,为线性渐变,辐射渐变和锥形渐变,分别使用方法,注意它们都可以设置显示模式,效果会不同
QLinearGradient linear(QPointF(80, 80), QPointF(150, 150)); //两个控制点,渐变在它们之间
linear.setColorAt(0, Qt::black);
linear.setColorAt(1, Qt::white);
// 设置显示模式
linear.setSpread(QGradient::PadSpread);
QRadialGradient radial(110, 110, 50, 130, 130); //1,2参数为起始点,3为半径,4,5为结束点
radial.setColorAt(0, Qt::black);
radial.setColorAt(1, Qt::white);
QConicalGradient conical(110, 110, 45); //1,2为起始点,3为角度
conical.setColorAt(0, Qt::black);
conical.setColorAt(1, Qt::white);
如果想要绘制复杂的路径,特别是重复复制,那么使用QPainterPath就十分合适了,要为一个指定的绘图路径生成可填充的轮廓,可以使用 QPainterPathStroker 类。一旦创建,可以使用 lineTo()、arcTo()、cubicTo() 和 quadTo() 函数将直线和曲线添加到路径中,直线和曲线从 currentPosition() 处伸展到其传递的参数的所在点的位置。QPainterPath 提供了 contains() 函数,用于确定给定点或矩形是否在路径内。以及 intersects() 函数,用于确定给定矩形内的任何点是否也在该路径内。默认从原点开始,可以通过设置moveTo等来改变
void QPainterPath::addEllipse(const QRectF & boundingRectangle);
void QPainterPath::addPolygon(const QPolygonF & polygon);
void QPainterPath::addRect(const QRectF & rectangle);
void QPainterPath::addText(const QPointF & point, const QFont & font, const QString & text)
painter.drawPath(myPath);
至于QPainterPathStroker的用法如下:
QPainterPathStroker stroker;
stroker.setCapStyle(Qt::RoundCap); // 端点风格
stroker.setJoinStyle(Qt::RoundJoin); // 连接样式
stroker.setDashPattern(Qt::DashLine); // 虚线图案
stroker.setWidth(10); // 宽度
// 生成一个新路径(可填充区域),表示原始路径 path 的轮廓
QPainterPath outlinePath = stroker.createStroke(path);
// 绘制轮廓时所用的画笔(轮廓外边框灰色部分)
QPen pen = painter->pen();
pen.setColor(QColor(0, 160, 230));
pen.setWidth(10);
// 用指定的画笔 pen 绘制 outlinePath
// painter->strokePath(outlinePath, pen);
painter->setPen(pen);
painter->drawPath(outlinePath);
// 用指定的画刷 brush 填充路径 outlinePath
painter->fillPath(outlinePath, QBrush(Qt::yellow));
QPainter可以使用多种坐标变换来改变坐标,使用QPainter进行绘制时,会使用逻辑坐标进行绘制,然后再转换为绘图设备的物理坐标,窗口使用的是逻辑坐标下的矩形,而视口表示物理坐标下的矩形,它们指的是同一个矩形,可以通过
painter.setWindow(x,y,w,h)来改变逻辑坐标
当使用 QPainter 绘图时,我们使用逻辑坐标来指定点,然后将逻辑坐标转换成绘图设备的物理坐标。
然后还有一些经常使用的转换函数如:
QTransform transform;
transform.shear(0.5, 0);
painter.setTransform(transform);
QTransform transform;
// 平移
transform.translate(120, 20);
// 旋转
transform.rotate(45);
// 缩放
transform.scale(0.5, 0.5);
由于历史原因,QRect::right() 和 QRect::bottom() 的返回值并不是矩形右下角的真实坐标值。
QRect::right() 返回的是 left() + width() – 1,QRect::bottom() 返回的是 top() + height() – 1,上图中右下角的绿色点指出了这两个函数返回的坐标值。
为避免这个问题,建议直接使用 QRectF 来代替:QRectF 使用浮点坐标来定义一个平面矩形(QRect 则使用整形坐标)以确保更加精确,并且函数 QRectF::right() 和 QRectF::bottom() 会返回真正的右下角坐标值。
如果要使用 QRect,可以利用 x() + width() 和 y() + height() 来替代 right() 和 bottom()。
当准确率比效率更为重要时,我们可以画到一个QImage上,然后把结果复制到屏幕上。Qt提供了4个类来处理图像数据,QImage,QPixmap,QBitmap,QPicture都是常用的绘图设备,其中,QImage主要用来进行I/0处理,它对I/0处理操作进行了优化,而且也可以直接用来访问和操作像素,QPixmap主要用来在屏幕上显示图像,它对在屏幕上显示图像进行了优化,QBitmap是QPixmap的子类,用来处理颜色深度为1的图像,即只能显示白色和黑色,QPicture用来记录并重演QPainter的命令
QPainter提供了复合模式来定义如何完成数字图像的复合,即如何将原图像的像素和目标图像的像素进行合并如:
painter.setCompositionMode(QPainter::CompositionMode_SourceIn);//默认是SourceOver
当需要绘制文字时,可能需要使用QTextOption,可以设置对齐方式、换行方式以及文本显示方向等效果还可以设置换行,如:
QTextOption option(Qt::AlignLeft | Qt::AlignVCenter);
option.setWrapMode(QTextOption::WordWrap);
painter.drawText(...)
在绘图中,如果需要处理从几个到几万的项时,而且要求用户能够单击,拖动和选取项,就需要使用到视图体系,Qt的视图体系包括一个由QGraphicsScene充当的场景和一些由QGraphicsItem的子类充当的场景中的项。QGraphicsScene是一个图形项的集合,一共有3层:背景层,前景层和项层,背景层和前景层通常由QBrush指定。任何应用于项的变换都会自动应用于它的子对象中。视图体系提供了两种分组项的方法,使一个项成为另一个项的子项,和使用QGraphicsItemGroup。QGraphicsView是一个窗口部件用来显示场景,提供滚动条等功能,默认使用二维图形引擎,调用setViewport改为使用OPenGL窗口部件,可以附加多个视图到同一个场景,从而针对同一数据集提供几个视口(viewport)。它有3个坐标,视口坐标是QGraphicsView的坐标,场景坐标是逻辑坐标,项坐标是针对某一个项以0,0为中心的坐标。项的位置是项的中心点在其父坐标系下的坐标,创建自定义图元时,只需考虑图元坐标即可,不过相对于场景来说,子图元将随着父图元进行转换和偏移。要从视图映射到图元,第一步是映射到场景,然后才能从场景映射到图元。图形视图框架为场景、每个图元提供了拖放支持。当视图接收到一个拖拽动作,它将拖放事件转换为一个QGraphicsSceneDragDropEvent,然后将其转发给场景。场景则会接管该事件的调度,并将其发送给鼠标下面第一个接受放下动作的图元。要拖拽一个图元,只需要创建一个QDrag对象,将指针传给开始拖拽的部件。图元可以同时被多个视图观察,但是只有一个视图可以进行拖拽。图形视图在几个层面上提供了对动画的支持。你可以用Animation Framework轻松地设置动画:只需要让你的图元从QGraphicsObject继承,然后将QPropertyAnimation绑定到上面。QPropertyAnimation可以为任何QObject属性实现动画效果。QGraphicsWidget建立在QGraphicsItem之上,具有QGraphicsItem的所有功能,保持了较小的资源占用,同时提供了两者的优势:来自QWidget的额外的功能,例如:样式、字体、调色板、布局、几何形状,来自QGraphicsItem的分辨率独立性和坐标转换的支持。常见的图形视图类如下:
类 | 描述 |
---|---|
QAbstractGraphicsShapeItem | 所有路径图元的共同基类 |
QGraphicsAnchor | 表示一个QGraphicsAnchorLayout中两个图元之间的anchor |
QGraphicsAnchorLayout | 布局可以anchor部件到图形视图中 |
QGraphicsEffect | 所有图形特效的基类 |
QGraphicsEllipseItem | 可以添加到QGraphicsScene的椭圆图元 |
QGraphicsGridLayout | 图形视图中管理部件的网格布局 |
QGraphicsItem | QGraphicsScene中所有图元的基类 |
QGraphicsItemGroup | 一个将图元组当做单个图元来看待的容器 |
QGraphicsLayout | 图形视图中所有布局类的基类 |
QGraphicsLayoutItem | 可以被继承,允许布局类管理的自定义图元 |
QGraphicsLineItem | 可以添加到QGraphicsScene的直线图元 |
QGraphicsLinearLayout | 图形视图中管理部件的水平或垂直布局 |
QGraphicsObject | 所有需要信号、槽、属性的图元的基类 |
QGraphicsPathItem | 可以添加到QGraphicsScene的路径图元 |
QGraphicsPixmapItem | 可以添加到QGraphicsScene的图形图元 |
QGraphicsPolygonItem | 可以添加到QGraphicsScene的多边形图元 |
QGraphicsProxyWidget | 代理,用于将一个QWidget对象嵌入到QGraphicsScene中 |
QGraphicsRectItem | 可以添加到QGraphicsScene的矩形图元 |
QGraphicsScene | 管理大量2D图元的管理器 |
QGraphicsSceneContextMenuEvent | 图形视图框架中的上下文菜单事件 |
QGraphicsSceneDragDropEvent | 图形视图框架中的拖放事件 |
QGraphicsSceneEvent | 所有图形视图相关事件的基类 |
QGraphicsSceneHelpEvent | Tooltip请求时的事件 |
QGraphicsSceneHoverEvent | 图形视图框架中的悬停事件 |
QGraphicsSceneMouseEvent | 图形视图框架中的鼠标事件 |
QGraphicsSceneMoveEvent | 图形视图框架中的部件移动事件 |
QGraphicsSceneResizeEvent | 图形视图框架中的部件大小改变事件 |
QGraphicsSceneWheelEvent | 图形视图框架中的鼠标滚轮事件 |
QGraphicsSimpleTextItem | 可以添加到QGraphicsScene的简单文本图元 |
QGraphicsSvgItem | 可以用来呈现SVG文件内容的QGraphicsItem |
QGraphicsTextItem | 可以添加到QGraphicsScene的文本图元,用于显示格式化文本 |
QGraphicsTransform | 创建QGraphicsItems高级矩阵变换的抽象基类 |
QGraphicsView | 显示一个QGraphicsScene内容的部件 |
QGraphicsWidget | QGraphicsScene中所有部件图元的基类 |
QStyleOptionGraphicsItem | 用于描述绘制一个QGraphicsItem所需的参数 |
对于文本项,大致都
QGraphicsSimpleTextItem *newText=new QGraphicsSimpleTextItem(QString("123");
QGraphicsScene *newScene=new QGraohicsScene();
newScene->addItem(QGraphicsTextItem);
QGraphicsView *newView=new QGraphicsView();
newView->setScene(newScene);
newView->show():
当编写自定义的图形项时,通常的做法就是继承 QGraphicsItem,然后重新实现它的boundingRect和Paint函数,QRectF boundingRect() ,将 item 的外边界作为矩形返回,由 QGraphicsView 调用以确定什么区域需要重绘,也可以实现shape()来实现它的特定形状,并且由 contains() 和 collidesWithPath() 用于碰撞检测,shape() 默认实现调用 boundingRect() 返回一个简单的矩形形状。
QGraphicsWidget 继承自 QObject 和 QGraphicsItem,是 QGraphicsScene 中所有 widget items 的基类。由于 QGraphicsWidget 类似于 QWidget,并有着相似的 API,更容易从 QWidget 到 QGraphicsWidget,而不是 QGraphicsItem。使用的大概逻辑:
QGraphicsScene *pScene = new QGraphicsScene();
QGraphicsProxyWidget *pPixmapWidget = pScene->addWidget(pPixmapLabel);
QGraphicsGridLayout *pLayout = new QGraphicsGridLayout();
pLayout->addItem(pPixmapWidget, 0, 0, 3, 1);
QGraphicsWidget *pWidget = new QGraphicsWidget();
pWidget->setLayout(pLayout);
// 将 item 添加至场景中
pScene->addItem(pWidget);
QGraphicsItemGroup 的 boundingRect() 函数返回位于其中所有 items 的边界矩形。最普通的
QGraphicsItemGroup *group = scene->createItemGroup(scene->selecteditems());
// 销毁 group,并删除 group item
scene->destroyItemGroup(group);
QGraphicsItem 分组比较简单,但在分组之后 group 中的 QGraphicsItem 无法捕获自己的相关事件,处理方式有两种:
void QGraphicsItem::setHandlesChildEvents(bool enabled) //设置为真,那么都会由QGraphicsItemGroup处理,为false,就自己处理
要让 QGraphicsItemGroup 中的 item 处理自己的事件,还需要在构造 group 后,再手动调用:
QGraphicsItemGroup::setHandlesChildEvents(false);
sceneEvent() 接收一个 item 的所有事件,非常类似于 QWidget::event(),也是实现item自己处理自己时事件的方法
Graphics View提供了一个平台,用于大量自定义 2D 图元的管理与交互,框架包括一个事件传播架构,支持场景 Scene 中的图元 Item 进行精确的双精度交互功能。Item 可以处理键盘事件、鼠标按下、移动、释放和双击事件,同时也能跟踪鼠标移动。
QGraphicsItem 中包含两个与类型相关的枚举值:
enum {
Type = 1,
UserType = 65536
};
QGraphicsItem::Type 是标准 item 类中 virtual type() 函数返回的类型值。所有标准 item 与唯一的 Type 值相关联
QGraphicsItem::UserType 是自定义 item(QGraphicsItem 或任何标准 item 的子类)的最小允许类型值。该值与 QGraphicsItem::type() 的重新实现结合使用,并声明一个 Type 枚举值
QGraphicsObject 类为需要信号/槽和属性的所有 items 提供一个基类,将 QGraphicsItem 的许多基本 setters 和 getters 映射到属性,并为其中的许多添加了通知信号。
QGraphicsScene::itemAt()函数返回指定点的最上层的图形,要获取当前所有项的列表,可以使用QGraphicsScene::selectedItems(),图形项都包含一个Z值来设置层叠顺序,越大越上。一个场景分为3层:图形项层,前景层和背景层,绘制时从里到外。只有视口坐标是以左上角为原点的,场景坐标和图形项坐标都是以自己的中心为坐标原点,要获取一个图形项在视口的位置可以先在图形项上调用QGraphicsItem::mapToScene(),然后再视图上调用QGraphicsView::mapFromScene(),,具体的映射函数
映射函数 | 描述 |
QGraphicsView::mapToScene() | 从视图坐标系统映射到场景坐标系统 |
QGraphicsView::mapFromScene() | 从场景坐标系统映射到视图坐标系统 |
QGraphicsItem::mapToScene() | 从图形项的坐标系统映射到场景的坐标系统 |
QGraphicsItem::mapFromScene() | 从场景坐标系统映射到图形项的坐标系统 |
QGraphicsItem::mapToParent() | 从本图形的坐标系统映射到其父图形项的坐标系统 |
QGraphicsItem::mapFromParent() | 从父图形项的坐标系统映射到本图形项的坐标系统 |
QGraphicsItem::mapToItem() | 从本图形项的坐标系统映射到另一个图形项的坐标系统 |
QGraohicsItem::mapFromItem() | 从另一个图形项的坐标系统映射到本图形项的坐标系统 |
场景的背景图片位置的变化也是场景位置的变化,默认的,如果场景中没有添加任何图形项,那么场景的中心,默认是原点会和视图的中心重合,如果添加了图形项,那么视图就会以图形项的中心为中心来显示场景,因此图形项的大小或者位置变化了,那么视口的位置也会变化。场景还有一个很重要的属性就是场景矩形,它是场景的边界矩形,场景矩形定义了场景的范围,它只会增加不会减小,默认就是包含所有图形项的最大边界矩形,如果设置了场景矩形,就可以指定视图显示的场景区域,比如讲场景的原点显示在视图的左上角,还可以使用centerOn()函数来设置场景中的一个点或者一个图形项作为视图的显示中心。
默认的,如果场景没有获得焦点,那么所有的键盘事件都会被丢弃。可以调用hasFocus()来检查是否获得焦点。如果一个图形项可以接受悬停事件,那么当鼠标进入它的区域中时,它就会收到一个GraphicsSceneHoverEnter事件,移动和离开会有GraphicsSceneHoverMove事件和GraphicsSceneHoverLeave事件,图形项默认是无法接受悬停事件的,可以使用setAcceptHoverEvents()函数接受悬停事件
QGraphicsEffect类是所有图形效果的基类,只需先创建一个图形效果对象,然后调用setGraphicsEffect()函数来使用这个图形效果即可,也可以自定义效果,创建QGraphicsEffect的子类就可。标注图形效果由如下:
效果 | 介绍 |
QGraphicsBlurEffect | 提供一个模糊效果,用来减少源对象细节的显示,可以通过setBluerRadius和setBlurHints设置 |
QGraphicsColorizeEffect | 提供一个染色效果,为源对象染色,使用setColor改变颜色,setStrength修改效果强度 |
QGraphicsDropShadowEffect | 提供一个阴影效果,为源对象提供一个阴影,使用setColor改变颜色,setOffset改变阴影偏移值 |
QGraphicsOpacityEffect | 提供一个透明效果,可以是源对象透明,通过设置setOpacity来修改透明度 0-1之间0为完全透明 |
QGraphicsScene::advance可以推进场景,它会自动调用场景中所有图形项的advance函数,然后可以重新实现advance来创建动画。
图形之间的碰撞检测取决于他们的shape,可以重新实现他们的shape函数来返回准确的形状,然后使用默认的collidesWithItem函数通过两个图形项形状之间的交集来判断是否发生碰撞,如果没有重新实现就会默认的boundingRect函数返回一个简单的矩形。还可以使用collidesWithPath判断是否与指定路径碰撞,使用collidingItems获取与该图形项碰撞的所有图形项的列表,它们都有一个Qt::ItemSelectionMode参数来指定怎样进行图形项的选取。QGraphicsView的对象通过调用setDragMode(QGraphicsView::RubberBandDrag)来使鼠标可以在视图上拖出橡皮筋来选择图形项