C++ Qt学习笔记(4)绘图
Qt中的二维绘图基本功能是使用Qpainter在绘图设备上进行绘图,绘图设备由QpainterDevice提供,QPaintDevice是一个二维空间的抽象,可以使用QPainter在其上进行绘制,它是所有可以进行绘制的对象的基类。QPainterDevice的子类主要有QWidget, QPixmap,QPicture,QImage,QPinter,QOpenGIPainterDevice,这些设备相当于为QPainter提供了一个画布。通过绘制一些基本的图形,如点,线,圆组成自己需要的图图件,形,得到的图形是不可交互的。
Qt中的Graphics View架构,使用QGraphicsView,QGraphicsScene,QGraphicsItem类绘图,在一个场景中可以绘制,且图件是可以交互的。
1. QPainter基本绘图:
在绘图系统中,主要通过QPainter完成具体的绘图操作,QPaintDevice是一个可以用QPainter进行绘图的抽象二维画布。QPainterEngine为QPainter提供在不同设备上进行绘图的接口,QPainterEngine类一般只在QPainter类和QPaintDevice类的内部使用,一般不需要我们去操作它。
QPainter在一个Paint Event事件函数中进行绘图,从画布(QWidget, QPixmap, QPicture, QImage, QPinter, QOpenGIPainterDevice )继承的类都有Paint Event事件函数。例如创建一个Widget窗口,进行绘图,则这个Widget窗口就是需要使用的绘图设备。
绘图的区域就是Widget内部的区域,区域内的坐标系统的单位是像素,左上角为原点(0,0),向右是X轴正向,向下是Y轴正向。绘图区域的大小可以由Widget::width()以及Widget::height()得到。
QPainter绘图的属性:
1. pen属性:是一个QPen对象,用于控制线条的颜色,宽度,线型等属性
2. brush属性:是一个QBrush对象,控制区域填充特性,如颜色,填充方式,渐变特性
3. font:QFont对象,用于绘制文字时控制文字的属性,字体,大小等
1. 首先声明绘图事件处理函数:
protected:
void paintEvent(QPaintEvent* event);
2.在paintEvent()函数中编写绘制图像的代码:
void Widget::paintEvent(QPaintEvent *event)
{
QPainter painter(this); // this代表Widget,及绘图设备
painter.setRenderHint(QPainter::Antialiasing); // 绘画普通图形启用反走样, 即开启抗锯齿
painter.setRenderHint(QPainter::TextAntialiasing); // 绘画文字反走样, 即开启抗锯齿
int width = this->width(); // 获取绘图区域的大小
int height = this->height();
QRect rect(width/4, height/4, width/2, height/2); // 中间区域矩形框
// QPainter属性 pen,brush,font
QPen pen;
pen.setWidth(3); // 设置线宽
pen.setColor(Qt::blue);
pen.setStyle(Qt::SolidLine); // 线的样式
pen.setCapStyle(Qt::FlatCap); // 线条的端点样式
pen.setJoinStyle(Qt::BevelJoin); // 连接点的样式
painter.setPen(pen); // 设置QPainter的pen属性
// brush属性
QBrush brush;
brush.setColor(Qt::green); // 填充颜色
brush.setStyle(Qt::SolidPattern); // 填充样式
painter.setBrush(brush);
// 绘图
painter.drawRect(rect);
// 绘制直线
pen.setColor(Qt::red);
pen.setCapStyle(Qt::FlatCap);
pen.setJoinStyle(Qt::BevelJoin);
painter.setPen(pen);
painter.drawLine(QPoint(5, 5), QPoint(width-10, height-10));
}
绘图结果:
关于绘制图像中的抗锯齿,可以参考博客:https://blog.csdn.net/xiezhongyuan07/article/details/97116491
同时,可以擦除矩形中填充的颜色,以及重新填充:
painter.eraseRect(rect); // 擦除矩形区域的内容
// 可以重新填充矩形区域
brush.setColor(Qt::black);
painter.fillRect(rect, brush);
QPainter的三个属性介绍:
1. QPen的属性:
Qpen主要用于在绘图时对线条进行控制,包括线宽,颜色,线型的。
QPen主要的接口函数有:
更改器:(设置属性)
1.pen.setColor(Qt::red); 设置颜色
2.pen.setCapStyle(Qt::FlatCap); 设置端点样式
3. pen.setJoinStyle(Qt::BevelJoin); 设置连接样式
4. pen.setWidth() 设置宽度
5. pen.setStyle() 设置样式
访问器:(获取属性)
1.pen.color(Qt::red); 获取pen的颜色
2.pen.capStyle(Qt::FlatCap); 获取端点样式
3. pen.joinStyle(Qt::BevelJoin); 连接样式
4. pen.width() 宽度
5. pen.style() 样式
线条样式主要有六种:
1. SolidLine
2. DashLine
3. DotLine
4. DashDotLine
5. DashDotDotLine
6. CustomDashLine
7.NoPen
端点样式有三种:
1. SquareCap
2. FlatCap
3. RoundCap
连接样式也有三种:
1. BevelJoin
2. MiterJoin
3. RoundJoin
2. QBrush介绍:
QBrush定义了QPainter绘图时的填充性能,包括颜色,填充样式,填充材质。
QBrush的主要函数有以下几种:
void setColor ( Qt::GlobalColor color )
void setStyle ( Qt::BrushStyle style )
void setTexture ( const QPixmap & pixmap )
void setTextureImage ( const QImage & image )
填充样式setStyle()的参数是一个枚举类型,主要有以下几种:
例如,用几种不同的方式填充矩形区域:
void Widget::paintEvent(QPaintEvent *event)
{
QPainter painter(this); // this代表Widget,及绘图设备
painter.setRenderHint(QPainter::Antialiasing); // 绘画普通图形启用反走样, 即开启抗锯齿
painter.setRenderHint(QPainter::TextAntialiasing); // 绘画文字反走样, 即开启抗锯齿
int width = this->width(); // 获取绘图区域的大小
int height = this->height();
QRect rect(width/4, height/4, width/2, height/2); // 中间区域矩形框
// brush属性
QBrush brush;
brush.setColor(Qt::red); // 填充颜色
// brush.setStyle(Qt::SolidPattern); // 填充样式
// brush.setStyle(Qt::Dense7Pattern);
// brush.setStyle(Qt::CrossPattern);
brush.setStyle(Qt::FDiagPattern);
painter.setBrush(brush);
// 绘图
painter.drawRect(rect);
}
QBrush填充渐变颜色:
Qt中有三种渐变的效果,如下图所示:
1. QLinearGradient
线性渐变颜色,可以指定一个起点颜色,终点颜色,还可以指定中间颜色,起点和终点之间可以进行插值计算,得到线性渐变的填充颜色。
void Widget::paintEvent(QPaintEvent *event)
{
QPainter painter(this); // this代表Widget,及绘图设备
painter.setRenderHint(QPainter::Antialiasing); // 绘画普通图形启用反走样, 即开启抗锯齿
painter.setRenderHint(QPainter::TextAntialiasing); // 绘画文字反走样, 即开启抗锯齿
int width = this->width(); // 获取绘图区域的大小
int height = this->height();
QRect rect(width/4, height/4, width/2, height/2); // 中间区域矩形框
QLinearGradient linearGrident(width/4, height/4, width/2, height/2); // 指定渐变区域
linearGrident.setColorAt(0, Qt::red);
linearGrident.setColorAt(0.5, Qt::yellow);
linearGrident.setColorAt(1, Qt::blue);
// brush属性
QBrush brush;
painter.setBrush(linearGrident);
// 绘图
painter.drawRect(rect);
}
填充效果:
2.QConicalGradient: 圆锥形渐变,即围绕一个中心点逆时针生成渐变颜色。
void Widget::paintEvent(QPaintEvent *event)
{
QPainter painter(this); // this代表Widget,及绘图设备
painter.setRenderHint(QPainter::Antialiasing); // 绘画普通图形启用反走样, 即开启抗锯齿
painter.setRenderHint(QPainter::TextAntialiasing); // 绘画文字反走样, 即开启抗锯齿
int width = this->width(); // 获取绘图区域的大小
int height = this->height();
QConicalGradient gradient(width/2, height/2, 0); // 填充参数参数cx, cy, start_angle,
gradient.setColorAt(0, Qt::red); // 起始角度的位置,与线性渐变一样,也是采用相对位置
gradient.setColorAt(0.4, Qt::green);
gradient.setColorAt(0.6, Qt::yellow);
gradient.setColorAt(1, Qt::black);
painter.setBrush(gradient);
painter.drawRect(this->rect()); // 画圆的区域
}
填充效果:
3. QRadialGradient: 辐射渐变填充,有简单辐射渐变和扩展辐射渐变两种方式,简单辐射渐变是在一个圆内一个焦点和一个端点之间生成渐变颜色,扩展辐射渐变是在一个焦点圆和一个中心圆之间生成渐变颜色。
void Widget::paintEvent(QPaintEvent *event)
{
QPainter painter(this); // this代表Widget,及绘图设备
painter.setRenderHint(QPainter::Antialiasing); // 绘画普通图形启用反走样, 即开启抗锯齿
painter.setRenderHint(QPainter::TextAntialiasing); // 绘画文字反走样, 即开启抗锯齿
int width = this->width(); // 获取绘图区域的大小
int height = this->height();
// 径向渐变 c_x, c_y, radius, fx, fy 其中fx,fy为焦点
// c_x, c_y是辐射的中心点,radius是辐射半径
QRadialGradient gradient(width/2, height/2, qMax(width/4, height/4), width/2, height/2); // 径向渐变
gradient.setColorAt(0, Qt::red);
gradient.setColorAt(1, Qt::black); // 这里使用的是逻辑坐标 0开始位置 1结束位置
// gradient.setSpread(QGradient::PadSpread); // 设置渐变区域之外的渐变方式,即延展方式
// gradient.setSpread(QGradient::RepeatSpread);
gradient.setSpread(QGradient::ReflectSpread);
painter.setBrush(gradient);
painter.drawRect(this->rect()); // 画圆的区域
}
同时,QRadialGradient需要通过setSpread()函数设置延展方式,也就是填充区域意外的填充方式,共有三种方式:
下面是填充的效果:
PadSpread:
RepeatSpread:
ReflectSpread:
QPainter绘制基本图形元件:
QPainter可以绘制包括点,直线,曲线,矩形,圆弧等各种基本线条。具体的函数以及参数可以参考Qt文档:(文档部分)
坐标系统和坐标变换:
QPainter在窗口上绘图的默认坐标是绘图设备的物理坐标,为了绘图方便,QPainter提供了一些坐标变换功能,通过平移,旋转等坐标变换,得到一个逻辑坐标系统,使用逻辑坐标系统在某些时候绘图会更加方便。
常用的坐标变换:
1. 平移变换:
void translate(dx, dy):表示将坐标系统的原点移动到(dx, dy)位置。移动的单位为像素。
2. 坐标旋转:
void rotate(angle): 将坐标系统绕坐标原点旋转指定的角度。angle>0表示顺势正旋转,angle<0表示逆时针旋转。
3.缩放:
void scale(sx, sy): 表示缩放,sx,sy表示沿着x,y轴的缩放比例。参数大于1表示放大,参数小于1表示缩小。
4. 状态保存于恢复:
在进行坐标变换的时候,QPainter内部会有一个坐标变换的矩阵,用save()函数保存坐标的状态。用restore()表示恢复保存的坐标状态。这两个函数必须配对使用。resetTransform表示复位所有的变换操作。
例子:
void Widget::paintEvent(QPaintEvent *event)
{
QPainter painter(this); // this代表Widget,及绘图设备
painter.setRenderHint(QPainter::Antialiasing); // 绘画普通图形启用反走样, 即开启抗锯齿
painter.setRenderHint(QPainter::TextAntialiasing); // 绘画文字反走样, 即开启抗锯齿
int width = this->width(); // 获取绘图区域的大小
int height = this->height();
// 生成五角星的五个顶点坐标
int radius = 100; // 半径
const double Pi = 3.1415926;
double deg = 360.0 / 5 * Pi / 180.0;
QPoint points[5] = {
QPoint(radius, 0),
QPoint(radius * cos(deg), radius * sin(deg)),
QPoint(radius * cos(2*deg), radius * sin(2*deg)),
QPoint(radius * cos(3*deg), radius * sin(3*deg)),
QPoint(radius * cos(4*deg), radius * sin(4*deg))
};
// 设置字体
QFont font;
font.setPointSize(12);
font.setBold(true);
painter.setFont(font);
// 设置画笔
QPen pen;
pen.setWidth(2); // 设置线宽
pen.setColor(Qt::blue); // 设置颜色
pen.setStyle(Qt::SolidLine); // 设置线型
pen.setCapStyle(Qt::FlatCap);
pen.setJoinStyle(Qt::BevelJoin);
painter.setPen(pen);
// 设置画刷
QBrush brush;
brush.setColor(Qt::red);
brush.setStyle(Qt::SolidPattern);
painter.setBrush(brush);
// painter_path, 可以重复使用
QPainterPath path;
path.moveTo(points[0]); // 起始点
path.lineTo(points[2]);
path.lineTo(points[4]);
path.lineTo(points[1]);
path.lineTo(points[3]);
path.closeSubpath(); // 闭合路径,相当于 path.lineTo(points[0]);
// 添加文本
path.addText(points[0], font, "1");
path.addText(points[1], font, "2");
path.addText(points[2], font, "3");
path.addText(points[3], font, "4");
path.addText(points[4], font, "5");
// 保存坐标状态
painter.save();
painter.translate(width/4, height/4); // 平移
painter.drawPath(path);
// 恢复坐标
painter.restore();
painter.translate(width/2, height/2); // 平移
painter.scale(0.6, 0.6); // 缩放,缩小操作
painter.rotate(30); // 旋转
painter.drawPath(path); // 这里的path重复利用了
}
绘制效果图:
QPainterPath可以记录几个点的连线过程,可以对绘制复杂图像的过程进行记录,便于重复使用。