【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++ 开发指南》中的内容。

相关推荐
Scott9999HH1 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
码智社2 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海2 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖3 小时前
JDK 26 新特性详解
java·开发语言
马优晨4 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区6 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大6 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai6 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
এ慕ོ冬℘゜7 小时前
前端基础:什么是时间戳?JS获取时间戳三种方法与实战用途
开发语言·前端·javascript
执明wa7 小时前
LayoutInflater详解: XML是如何变成View的?
android·xml·开发语言·android studio