Qt—2D绘图—1基本图型绘制与填充

x33g5p2x  于2022-04-20 转载在 其他  
字(8.5k)|赞(0)|评价(0)|浏览(340)

Qt中提供了强大的2D绘图系统,可以使用相同的API在屏幕和绘图设备上进行绘制,主要基于QPainter , QPaintDevice和 QPaintEngine这3个类。其中,QPainter用来执行绘图操作。QPaintDevice提供绘图设备,是一个二维空间的抽象,可以使用QPainter在其上进行绘制;是所有可以进行绘制的对象的基类,它的子类主要有QWidget ,QPixmap, QPicture,QImage,QPrinter和 QOpenGLPaintDevice等。
QPaintEngine提供了一些接口,用于QPainter和 QPaintDevice内部,使得QPainter可以在不同的设备上进行绘制;除了创建自定义的绘图设备类型,一般编程中不需要使用该类。它们三者的关系如图所示。

本节内容可从Paint System中查看

基本图形的绘制和填充

QPainter中提供了一些便捷函数来绘制常用的图形,还可以设置线条,边框的画—笔以及进行填充的画刷。

1 绘制图形

新建Qt Widgets应用,项目名称为mydrawing ,基类选择QWidget,类名为Widget。建立完成后,在widget.h文件中声明重绘事件处理函数:

protected:
    void paintEvent(QPaintEvent *event);

painEvent绘画事件,QPaintEvent 类

然后到widget.cpp文件中对painEvent函数进行如下定义:

void Widget::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    painter.drawLine(QPoint(0, 0), QPoint(100, 100));
}

这里先创建了一个QPainter对象,使用了QPainter: :QPainter( QPaintDevice *device )构造函数,并指定了this为绘图设备,即表明在 Widget部件上进行绘制。

使用这个构造函数创建的对象会立即开始在设备上进行绘制,自动调用 begin()函数﹐然后在QPainter 的析构函数中调用end()函数结束绘制。如果构建QPainter对象时不想指定绘制设备,那么可以使用不带参数的构造函数﹐然后使用QPainter: : begin(QPaintDevice * device)在开始绘制时指定绘制设备,等绘制完成后再调用end()函数结束绘制。

就是说会自动从0,0到 (100,100)结束,绘画出一个线段。drawline —》line线段

上面函数中的代码等价于:

QPainter painter;
painter.begin(this);
painter.drawLine(QPoint(0,0),QPoint(100,100));
painter.end();

begin和end是默认自动调用的

这两种方式都可以完成绘制,无论使用哪种方式,都要指定绘图设备,否则无法进行绘制。

第二行代码使用drawLine()函数绘制了一条线段,这里使用了该函数的一种重载形式QPainter: :drawLine (const QPoint & p1, const QPoint & p2),其中,p1和p2分别是线段的起点和终点。
这里的QPoint(0, 0)就是窗口的原点,默认是窗口的左上角(不包含标题栏)。现在可以运行程序查看效果。

除了绘制简单的线条以外,QPainter还提供了一些绘制其他常用图形的函数,其中最常用的几个如表10-1所列。

2 使用画笔

在paintEvent函数中继续添加如下代码:

//创建画笔
    QPen pen(Qt::green, 5, Qt::DotLine, Qt::RoundCap, Qt::RoundJoin);
    //使用画笔
    painter.setPen(pen);
    QRectF rectangle(70.0, 40.0, 80.0, 60.0);
    int startAngle = 30 * 16;
    int spanAngle = 120 * 16;
    //绘制圆弧
    painter.drawArc(rectangle, startAngle, spanAngle);

QPen类为QPainter提供了画笔来绘制线条和形状的轮廓,这里使用的构造函数为

QPen::QPen (const QBrush &brush, qreal width, Qt::PenStyle style=Qt::SolidLine, 
Qt::PenCapStyle cap = Qt:: SquareCap,Qt::PenJoinStyle join = Qt::BevelJoin)

几个参数依次为画笔使用的画刷、线宽、画笔风格、画笔端点风格和画笔连接风格

也可以分别使用setBrush( )、setWidth()、setStyle()、setCapStyle( )和setJoinStyle()等函数进行设置。

其中,画刷可以为画笔提供颜色;线宽的默认值为0(宽度为一个像素);画笔风格有实线 、点线等,Qt中提供的画笔风格及其效果如图所示;

还有一个Qt::NoPen值,表示不进行线条或边框的绘制。
还可以使用setDashPattern()函数来自定义一个画笔风格。

画笔端点风格定义了怎样进行线条端点的绘制,Qt中提供的画笔端点风格及其效果如图10-3所示。

其中,Qt::SquareCap风格表示线条的终点为方形,并且向前延了线宽的一半的长度;
,,Qt::FlatCap风格也是方形端点,但并没有延长;
使用Qt::RoundCap风格的线条是圆形的端点,这些风格对宽度为0的线条没有作用。

最后的画笔连接风格定义了怎样绘制两个线条的连接,Qt提供的画笔连接风格及其效果如图10-4所示。

其中,Qt::BeveJoin风格填充了两个线条之间的空缺三角形;
Qt:: RoundJoin使用圆弧来填充这个三角形,这样显得更圆滑;
使用Qt::MiterJoin风格,是将两个线条的外部边线进行扩展而相交,然后填充形成的三角形区域。

这些风格对宽度为0的线条没有作用。

可以把很宽的线条看作一个矩形来理解这3种风格,如图10.5显示,QT中提供了一个Path Stroking演示程序,它可以显示画笔属性的各种组合效果,读者可以在帮助中查询该关键字。

创建完画笔后,使用setPen()来为Painter设置画笔,然后画笔绘制了一个圆弧。绘制圆弧
就是这段代码

QRectF rectangle(70.0, 40.0, 80.0, 60.0);
    int startAngle = 30 * 16;
    int spanAngle = 120 * 16;
    //绘制圆弧
    painter.drawArc(rectangle, startAngle, spanAngle);

QRectF 类使用浮点精度在平面中定义一个矩形(位置高宽)
然后定义一个起始角度startAngle
再定义一个跨越角度spanAngle

绘制圆弧函数的一种重载形式为QPainter::drawArc (const QRectF &.rectangle,intstartAngle,intspanAngle)
这里的3个参数分别对应弧线所在的矩形、起始角度和跨越角度
如图10-6所示。

QRectF::QRectF (qreal x, qreal y, qreal width,qreal height)可以使用浮点数为参数来确定一个矩形,需要指定左上角的坐标(x,y)、宽width和高height。
如果只想使用整数来确定一个矩形,那么可以使用QRect类。

这里角度的数值为实际度数乘以16,在时钟表盘中,0度指向3时的位置,角度数值为正,则表示逆时针旋转;角度数值为负,则表示顺时针旋转,整个一圈的数值为5760(360X16)。现在运行程序查看效果。

int startAngle = 30 * 16;
    int spanAngle = 120 * 16;

这里开始是30°,跨越正120°
相当于这样

运行结果也是这样

3 使用画刷

在paintEvent()函数中继续添加如下代码:

//重新设置画笔
    pen.setWidth(1);
    pen.setStyle(Qt::SolidLine);
    painter.setPen(pen);
    //绘制一个矩形
    painter.drawRect(160, 20, 50, 40);
    // 创建画刷
    QBrush brush(QColor(0, 0, 255), Qt::Dense4Pattern);
    // 使用画刷
    painter.setBrush(brush);
    // 绘制椭圆
    painter.drawEllipse(220, 20, 50, 50);
    // 设置纹理
    brush.setTexture(QPixmap("../mydrawing/yafeilinux.png"));
    // 重新使用画刷
    painter.setBrush(brush);
    // 定义四个点
    static const QPointF points[4] = {
        QPointF(270.0, 80.0),
        QPointF(290.0, 10.0),
        QPointF(350.0, 30.0),
        QPointF(390.0, 70.0)
    };
    // 使用四个点绘制多边形
    painter.drawPolygon(points, 4);

QBrush类提供了画刷来对图形进行填充,一个画刷使用它的颜色和风格(比如它的填充模式)来定义。

在Qt中使用的颜色一般都由QColor类来表示,它支持 RGB,HSV和 CMYK等颜色模型。
QColor还支持基于alpha的轮廓和填充(实现透明效果),而且 QColor类与平台、设备无关(颜色使用QColormap类向硬件进行映射)。
Qt中还提供了20种预定义的颜色﹐比如以前经常使用的Qt::red 等,可以在帮助中Qt::GlobalColor关键字查看。
填充模式使用Qt : : BrushStyle枚举类型来定义,包含了基本模式填充、渐变填充和纹理填充

Qt提供的画刷风格及其效果如图10–7所示。

前面程序中先绘制了一个矩形,这里没有指定画刷,那么将不会对矩形的内部进行填充;然后使用Qt :; Dense4Pattern风格定义了一个画刷并绘制了一个椭圆;最后使用setTexture()函数为画刷指定了纹理图片(需要向项目源码目录复制一张图片),这样会自动把画刷的风格改为Qt::TexturePattern,然后绘制了一个多边形。绘制多边形使用的是QPainter::drawPolygon(const QPointF *points, int pointCount, Qt::FillRule fillRule = Qt::OddEvenFill)函数,它需要指定各个顶点并且指定顶点的个数。
另外,还可以指定填充规则Qt : :FillRule。现在运行程序查看效果。

QPainter中还提供了fillRect()函数来填充一个矩形区域,以及eraseRect()函数来擦除一个矩形区域的内容。继续添加如下代码:

// 使用画刷填充一个矩形区域
    painter.fillRect(QRect(10, 100, 150, 20), QBrush(Qt::darkYellow));
    // 擦除一个矩形区域的内容
    painter.eraseRect(QRect(50, 0, 50, 120));

可以运行程序查看效果。

绘制和填充可以在帮助中通过 Drawing and Filling关键字查看,在Qt中还提供了一个 Basic Drawing的例子来演示画笔和画刷使用的方法,可以作为参考。

渐变填充

QGradient 类就是用来和QBrush一起指定渐变填充的

QT支持三种渐变填充:

  • 线性渐变(linear gradient):在开始点和结束点之间插入颜色;
  • 辐射渐变(radial gradient):在焦点和环绕它的圆环间插人颜色;
  • 锥形渐变(Conical):在圆心周围插入颜色;

这3种渐变分别由QGradient的3个子类来表示,QLinearGradient表示线性渐变,QRadialGradient表示辐射渐变,QConicalGradient表示锥形渐变。

在前面程序的paintEvent()函数中继续添加如下代码:

// 线性渐变
    QLinearGradient linearGradient(QPointF(40, 190), QPointF(70, 190));
    // 插入颜色
    linearGradient.setColorAt(0, Qt::yellow);
    linearGradient.setColorAt(0.5, Qt::red);
    linearGradient.setColorAt(1, Qt::green);
    // 指定渐变区域以外的区域的扩散方式
    linearGradient.setSpread(QGradient::RepeatSpread);
    // 使用渐变作为画刷
    painter.setBrush(linearGradient);
    painter.drawRect(10, 170, 90, 40);

    // 辐射渐变
    QRadialGradient radialGradient(QPointF(200, 190), 50, QPointF(275, 200));
    radialGradient.setColorAt(0, QColor(255, 255, 100, 150));
    radialGradient.setColorAt(1, QColor(0, 0, 0, 50));
    painter.setBrush(radialGradient);
    painter.drawEllipse(QPointF(200, 190), 50, 50);

    // 锥形渐变
    QConicalGradient conicalGradient(QPointF(350, 190), 60);
    conicalGradient.setColorAt(0.2, Qt::cyan);
    conicalGradient.setColorAt(0.9, Qt::black);
    painter.setBrush(conicalGradient);
    painter.drawEllipse(QPointF(350, 190), 50, 50);

    // 画笔使用线性渐变来绘制直线和文字
    painter.setPen(QPen(linearGradient,2));
    painter.drawLine(0, 280, 100, 280);
    painter.drawText(150, 280, tr("helloQt!"));

线性渐变

// 线性渐变
    QLinearGradient linearGradient(QPointF(40, 190), QPointF(70, 190));
    // 插入颜色
    linearGradient.setColorAt(0, Qt::yellow);
    linearGradient.setColorAt(0.5, Qt::red);
    linearGradient.setColorAt(1, Qt::green);
    // 指定渐变区域以外的区域的扩散方式
    linearGradient.setSpread(QGradient::RepeatSpread);
    // 使用渐变作为画刷
    painter.setBrush(linearGradient);
    painter.drawRect(10, 170, 90, 40);

线性渐变QLinearGradient::QLinearGradient(const QPointF &start, const QPointF &finalStop)需要指定开始点start和结束点finalStop,然后将开始点和结束点之间的区域进行等分,开始点的位置为0.0,结束点的位置为1.0,它们之间的位置按照距离比例进行设定,然后使用QGradient::setColorAt(qreal position, const QColor &color)函数在指定的位置position插入指定的颜色color。当然,这里的 position的值要在0到1之间。

这里还可以使用setSpread()函数来设置填充的扩散方式,即指明在指定区域以外的区域怎样进行填充。扩散方式由QGradient : : Spread枚举类型定义,它一共有3个值,分别是QGradient: :PadSpread使用最接近的颜色进行填充,这是默认值,如果不使用setSpread()指定扩散方式,那么就会默认使用这种方式
QGradient:: RepeatSpread在渐变区域以外的区域重复渐变;
QGradient:: ReflectSpread在渐变区域以外将反射渐变。

在线性渐变中,这3种扩散方式的效果如图10-8所示。

要使用渐变填充,可以直接在setBrush()中使用,这时画刷风格会自动设置为相应的渐变填充。
painter.setBrush(linearGradient);

辐射渐变

// 辐射渐变
    QRadialGradient radialGradient(QPointF(200, 190), 50, QPointF(275, 200));
    radialGradient.setColorAt(0, QColor(255, 255, 100, 150));
    radialGradient.setColorAt(1, QColor(0, 0, 0, 50));
    painter.setBrush(radialGradient);
    painter.drawEllipse(QPointF(200, 190), 50, 50);

辐射渐变QRadialGradient(const QPointF &center, qreal radius, const QPointF &focalPoint)需要指定圆心center和半径radius,这样就确定了一个圆,然后再指定一个焦点focalPoint。焦点的位置为0,圆环的位置为1,然后在焦点和圆环间插入颜色。辐射渐变也可以使用setSpread()函数设置渐变区域以外的区域的扩散方式。

这里就是设置圆心200,190 ,半径50的圆,焦点275,200 ,在这中间插入颜色
然后插入颜色开始时候到1结束时候

3种扩散方式的效果如图10-9所示。

程序中设置颜色时使用了QColor : : QColor ( int r,int g,int b,int a = 255 ),其中,参数r,g, b为三基色,分别是红(red) ,绿(green)和蓝(blue)。

它们的取值都在0~255之间,例如,QColor(255,0,0)表示红色,QColor(255,255,0)表示黄色,QColor(255,255,255)表示白色,QColor(0,0,0)表示黑色;

而a表示 alpha通道,用来设置透明度,取值也在0~255之间,0表示完全透明,255表示完全不透明。更多的可以看QColor类的参考文档

锥形渐变

// 锥形渐变
    QConicalGradient conicalGradient(QPointF(350, 190), 60);
    conicalGradient.setColorAt(0.2, Qt::cyan);
    conicalGradient.setColorAt(0.9, Qt::black);
    painter.setBrush(conicalGradient);
    painter.drawEllipse(QPointF(350, 190), 50, 50);

锥形渐变QConicalGradient(const QPointF &center, qreal angle)需要指定中心点center和一个角度angle(其值在0~360之间),然后沿逆时针从给定的角度开始环绕中心点插入颜色

这里给定的角度沿逆时针方向开始的位置为0,旋转一圈后为1。

setSpread()函数对于锥形渐变没有效果。

现在运行程序,效果如图所示。
在Qt中提供了一个Gradients演示程序,可以设置任意的渐变填充效果。

这里的代码就是从中心点QPointF(350, 190),60°开始。

conicalGradient.setColorAt(0.2, Qt::cyan);
 conicalGradient.setColorAt(0.9, Qt::black);

插入颜色,从60°逆时针开始青色

setColorAt是使用给定颜色在给定位置创建一个停止点。给定位置必须在0到1之间。

渐变文字(线条)

另外,如果为画笔设置了渐变颜色,那么可以绘制出渐变颜色的线条和轮廓,还可以绘制出渐变颜色的文字。

// 画笔使用线性渐变来绘制直线和文字
    painter.setPen(QPen(linearGradient,2));
    painter.drawLine(0, 280, 100, 280);
    painter.drawText(150, 280, tr("helloQt!"));

这里使用的是线性渐变

大家多练习下就会懂了图形

相关文章