二维画图 需要注意的

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的用法如下:

扫描二维码关注公众号,回复: 1931109 查看本文章
 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)来使鼠标可以在视图上拖出橡皮筋来选择图形项

猜你喜欢

转载自blog.csdn.net/weixin_38893389/article/details/80846829