【Qt】绘图

绘图

Qt 提供了画图相关的 API,可以允许我们在窗口上绘制任意的图形形状,来完成更复杂的界面设计。
绘图API核心类

说明
QPainter "画家" 用来绘图的对象,提供了一系列 drawXXX 方法,可以允许我们绘制各种图形
QPaintDevice "画板" 描述了 QPainter 把图形画到哪个对象上,之前的 QWidget 也是一种 QPaintDevice
QPen "画笔" 描述了 QPainter 画出来的线是什么样的
QBrush "画刷" 描述了 QPainter 填充了一个区域是什么样的

QPainter



这里画笔的样式有以下几种:

QBrush

其中画刷的样式有以下几种:


例如使用 Qt::DiagCrossPattern

绘制图片

平移坐标轴:translate(x, y) 表示水平方向上平移 x 单位,垂直方向上平移 y 单位。窗口默认大小为800*600,所以 translate(800,600) 后,坐标原点就跑右下角了。
旋转坐标轴:rotate(angel) 表示将坐标原点旋转 angel 角度,单位是度。angel 为正数表示顺时针。

视口和窗口

视口是指绘图设备的任意一个矩形区域,它使用物理坐标系。我们可以只选取物理坐标系中的一个矩形区域来绘图,默认情况下,视口等于绘图设备的整个矩形区域。窗口与视口是同一个矩形区域,但是窗口是用逻辑坐标系定义的,窗口可以直接定义矩形区域的逻辑坐标范围。

如下图矩形区域就是代表绘图设备的物理大小和坐标范围,假设大小为300*200像素。取其中间的一个正方形区域作为视口,卡其色的正方形区域就是视口。在绘图设备的物理坐标系中,视口的左上角坐标为 (50,0), 右下角坐标为 (250,200)。

QPainter 的函数 setViewport() 用于定义视口,有两种参数形式,其函数原型定义如下:

void QPainter::setViewport(const QRect &rectangle)

void QPainter::setViewport(int x, int y, int width, int height)

其中,(x, y) 是视口左上角在物理坐标系中的坐标,width 是视口宽度, height 是视口高度。上图中的视口需使用下面的语句:

painter.setViewport(50, 0, 200, 200);

这表示从绘图设备的物理坐标系中的一个点(50, 0) 开始,取宽度 300,高度 200像素的一个区域作为视口。对于上图视口所表示的正方形区域,我们定义如上图所示的一个窗口,窗口坐标系的原点在正方形的中心,并设置正方形的逻辑边长为 100。QPainter 的函数 setWindow() 用于定义窗口,有两种参数形式,其函数原型如下:

void QPainter::setWindow(const QRect &rectangle)

void QPainter::setWindow(int x, int y, int width, int height)

其中,(x, y) 是窗口左上角的坐标,width 是窗口逻辑宽度, height 是窗口逻辑高度。上图中的窗口需使用下面的语句:

painter.setWindow(-50, -50, 100, 100);

这表示对应于视口的正方形区域,其窗口左上角的逻辑坐标是 (-50, -50),窗口宽度为 100,高度为 100.这里设置的窗口仍为一个正方形,使得从视口变换到窗口时,长和宽的比例是相同的。实际可以任意指定窗口的逻辑坐标范围,长和宽的比例不同也是可以的。

视口窗口使用实例

使用窗口坐标系的优点是:在绘图时只需按照窗口坐标系定义来绘图,而不用关注实际的物理坐标范围。例如在一个固定边长为 100 的正方形窗口内绘图,当实际绘图设备大小变化时,绘图的图形会自动相应改变大小。这样,绘图功能与绘图设备是分离的,绘图功能可适用于不同大小、不同类型的绘图设备。

取高度和宽度中较小的一边为正方形边长,且图形是自动缩放的。

代码如下:

cpp 复制代码
void Widget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    int W = width();
    int H = height();
    int side = qMin(W,H);
    QRect rect((W-side)/2,(H-side)/2,side,side);
    painter.drawRect(rect);
    painter.setViewport(rect);
    painter.setWindow(-100,-100,200,200);
    QPen pen;
    pen.setWidth(1);
    pen.setColor(Qt::red);
    pen.setStyle(Qt::SolidLine);
    pen.setCapStyle(Qt::FlatCap);
    pen.setJoinStyle(Qt::BevelJoin);
    painter.setPen(pen);

    for(int i = 0; i < 36; i++)
    {
        painter.drawEllipse(QPoint(50,0),50,50);
        painter.rotate(10);//画一个圆旋转10度
    }
}

增加渐变填充和叠加效果的设置:

代码如下:

cpp 复制代码
void Widget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    int W = width();
    int H = height();
    int side = qMin(W,H);
    QRect rect((W-side)/2,(H-side)/2,side,side);
    painter.drawRect(rect);
    painter.setViewport(rect);
    painter.setWindow(-100,-100,200,200);
    QPen pen;
    pen.setWidth(1);
    pen.setColor(Qt::red);
    pen.setStyle(Qt::SolidLine);
    pen.setCapStyle(Qt::FlatCap);
    pen.setJoinStyle(Qt::BevelJoin);
    painter.setPen(pen);

    //线性渐变
    QLinearGradient linearGrad(0,0,100,0);//从左到右
    linearGrad.setColorAt(0,Qt::yellow);//起点颜色
    linearGrad.setColorAt(1,Qt::green);//终点颜色
    linearGrad.setSpread(QGradient::PadSpread);//展布模式
    painter.setBrush(linearGrad);

    //设置组合模式
    painter.setCompositionMode(QPainter::CompositionMode_Difference);
//    painter.setCompositionMode(QPainter::RasterOp_NotSourceXorDestination);
//    painter.setCompositionMode(QPainter::CompositionMode_Exclusion);

    for(int i = 0; i < 36; i++)
    {
        painter.drawEllipse(QPoint(50,0),50,50);
        painter.rotate(10);//画一个圆旋转10度
    }
}

以上窗口视口内容及代码为王维波等老师的书籍 《Qt6 C++ 开发指南》中的内容。

相关推荐
逻极9 分钟前
Rust流程控制(上):if_else与match模式匹配
开发语言·后端·rust
小雨下雨的雨13 分钟前
Rust专项——其他集合类型详解:BTreeMap、VecDeque、BinaryHeap
开发语言·后端·rust
渡我白衣14 分钟前
C++世界的混沌边界:undefined_behavior
java·开发语言·c++·人工智能·深度学习·语言模型
剑海风云29 分钟前
JDK 26:HTTP/3 支持已可在 HTTP 客户端 API 中使用
java·开发语言·http
下一站丶1 小时前
【JavaScript性能优化实战】
开发语言·javascript·性能优化
GIS好难学1 小时前
Three.js 粒子特效实战③:粒子重组效果
开发语言·前端·javascript
景彡先生1 小时前
Python NumPy广播机制详解:从原理到实战,数组运算的“隐形翅膀”
开发语言·python·numpy
不光头强1 小时前
springDI注入
java·开发语言
zhangfeng11332 小时前
亲测有效的mem 流行病预测,时间序列预测,r语言做移动流行区间法,MEM流行病阈值设置指南
开发语言·r语言·生物信息
小圆5312 小时前
java-learn(9):常见算法,collection框架
java·开发语言·算法