13. 绘图和样式表

Qt 5中的图形主要通过命令式QPainter API或Qt的声明性UI语言Qt Quick及其场景图形后端来完成, Qt的图形功能还包括对打印的支持,以及对各种图像格式的加载和保存。

Qt GUI界面提供了OpenGL和OpenGL ES集成,2D图形,基本图像,字体和文本等类,使用这些类我们可以方便的绘制图形。本章将介绍下在窗口绘制二维图形以及控件的样式。

Qt的绘图可以使用相同的API在屏幕和打印设备上绘画, 并且主要基于 QPainterQPaintDeviceQPaintEngine 类。

  • QPainter 用于执行绘制操作,可以看作是画家;

  • QPaintDevice 是二维空间的抽象,想象成画板,就是绘图设备,常见的绘图设备有QWidget、QPixmap、QPicture等;

  • QPaintEngine 提供了绘制器用来在不同类型的绘图设备上绘制的接口。

QPaintDevice 相关类的继承关系:

QColor002
  • QWidget是所有界面组件的基类,常见的绘图设备。

  • QImage、QPixmap、QBitmap和QPicture这些类用于处理图像数据,其中QImage是针对I/O和直接像素访问和操作进行设计和优化的类;QPixmap是针对屏幕上显示的图像进行设计和优化的类; QBitmap是一个继承自QPixmap的便捷类,确保深度为1;QPicture类是一个绘画设备,可以记录和回放QPainter命令。

  • QSvgGenerator 是用于创建SVG图形的绘制设备,可缩放矢量图形(scalable vector graphics)是一种图片格式。

  • QOpenGLPaintDevice 是用于渲染到当前OpenGL(ES)2.0的绘制设备, QPainter将调用OpenGL绘制到QOpenGLPaintDevice实例。

  • QPagePaintDevice是支持多个页面的绘图设备类。

13.1. QPainter绘图

Qt的二维绘制功能是使用QPainter在绘图设备上绘制,提供了高度优化的功能,可以完成大多数图形GUI程序所需的功能, 它可以绘制从简单的线条到复杂的形状的所有内容,它还可以绘制对齐的文本和像素图。 通常,它在逻辑坐标系中绘制,但也可以进行逻辑坐标到物理坐标的转换。

QPainter可以对继承QPaintDevice类的任何对象进行操作。

QPainter的常见用法是在控件的绘画事件中:构造和自定义(例如,设置笔或画笔)Painter,然后进行图形绘制。

13.1.1. 基本概念

13.1.1.1. 绘图事件

QWidget类有一个事件处理函数paintEvent,当组件需要重新绘制时,系统会自动调用整函数。 QPainter的常见用途就是在窗口的绘画事件(paintEvent)中,在绘图事件处理函数中创建QPainter对象,然后使用这个QPainter在绘图设备上绘制。 例如:

void MainWindow::paintEvent(QPaintEvent *event)
{
    // 创建与绘制设备关联的对象QPainter
    QPainter painter(this);
    // 绘制图形,先创建一个画笔,设置颜色等,然后在窗口中心绘制文本"LubanCat"
    painter.setPen(Qt::red);
    painter.drawText(rect(), Qt::AlignCenter, "LubanCat"); //绘制文本
}

QPainter类除了绘制图像,还可以自定义QPainter设置及其渲染质量,支持剪辑功能。此外,还可以通过指定QPainter的构图模式来控制不同形状如何合并在一起。

13.1.1.2. 颜色

Qt中的任何颜色都由支持RGB,HSV和CMYK颜色模型的QColor类表示。 通常使用 QColor RGB(R,G,B) 来指定颜色。 QColor还支持alpha混合轮廓和填充(指定透明效果), 并且该类与平台和设备无关(使用QColormap类将颜色映射到硬件)。

// Specify semi-transparent red
painter.setBrush(QColor(255, 0, 0, 127));
painter.drawRect(0, 0, width()/2, height());

// Specify semi-transparent blue
painter.setBrush(QColor(0, 0, 255, 127));
painter.drawRect(0, 0, width(), height()/2);

预定义的颜色

QColorConstants命名空间中有20个预定义的QColor对象, 包括黑色,白色,原色和辅助色,这些颜色的较暗版本以及三种灰度。

QColor002
brush.setColor(Qt::blue);
pen.setColor(Qt::red);

13.1.1.3. 窗口坐标

我们通常用x轴y轴组成的平面坐标系来限定2D绘图,Qt中坐标系由QPainter类控制。

window001

绘画设备的默认坐标系的原点位于左上角。该X值增加向右和ÿ值向下增加。 在基于像素的设备上,默认单位是一个像素。

逻辑QPainter坐标到物理QPaintDevice坐标的映射由QPainter的变换矩阵,视口和窗口处理所决定。 默认情况下,逻辑坐标系和物理坐标系重合。

13.1.1.4. 窗口和视口

在使用QPainter进行绘制时,我们使用逻辑坐标指定点,然后将其转换为绘画设备的物理坐标,也就是可以通过窗口和视口来设置组件大小位置。

视口是基于QPaintDevice类组件坐标实现,是物理坐标,可以通过setViewport()函数设置;窗口是基于自身的逻辑坐标,不是真实坐标,可以通过setWindow()函数设置。

逻辑坐标到物理坐标的映射由QPainter的world transformation worldTransform()以及QPainter的viewport()和window()处理。 视口表示指定任意矩形的物理坐标。“窗口”在逻辑坐标中描述相同的矩形。 默认情况下,逻辑坐标系和物理坐标系重合,相当于绘制设备的矩形。

使用窗口-视口转换,可以使逻辑坐标系符合您的首选项。该机制还可用于使绘图代码独立于绘图设备。 例如,可以通过调用QPainter::setWindow()函数使逻辑坐标从(-50,-50)扩展到(50,50),其中(0,0)位于中心:

QPainter painter(this);
qp->setWindow(QRect(-50, -50, 100, 100));

当QPainter初始化的时候,视口和窗口的坐标是一致的,通过上面setWindow函数设置,逻辑坐标(-50,-50)对应于绘图设备的物理坐标(0,0), 与绘画设备无关,绘画代码将始终在指定的逻辑坐标上运行。

13.1.2. 绘画相关类

13.1.2.1. QPen

QPen 类用于设置绘制时的线条特性,控制线条的颜色,宽度,线型等。

笔的宽度可以指定为整数(width())和浮点(widthF())精度,线宽为零表示修饰笔,这意味着笔的宽度始终绘制为一个像素宽,与QPainter上设置的变换无关。 使用*setColor()* , setWidth() 函数分别设置颜色和线宽。

PenStyle

QPen可以设置其线条样式, 通过setStyle()函数设置,参数是枚举类型Qt::PenStyle:

QPen001

样式

描述

Qt::NoPen

0

完全没有线。例如,QPainter::drawRect()填充但不绘制任何边界线

Qt::SolidLine

1

一条简单的宽线

Qt::DashLine

2

短划线隔开几个像素,虚线

Qt::DotLine

3

点分开几个像素,点划线

Qt::DashDotLine

4

交替的点和破折号

Qt::DashDotDotLine

5

一个破折号,两个点,一个破折号,两个点

Qt::CustomDashLine

6

使用QPainterPathStroker::setDashPattern()定义的自定义模式

除了上面基本的样式,用户还可以自定义线条样式,需要使用函数setDashOffset(qreal offset)和setDashPattern(const QVector<qreal> &pattern)函数。

PenCapStyle

使用函数setCapStyle()设置线条的端点样式,参数是枚举Qt::PenCapStyle,它仅适用于宽线(宽度为1或更大)。 在Qt的 Qt:: PenCapStyle 枚举提供了以下样式:

QPen002

样式

描述

Qt::FlatCap

0x00

一个方形的线条端,不覆盖线条的端点

Qt::SquareCap

0x10

一个方形的线条端,覆盖线条的端点并延伸1/2的线宽长度

Qt::RoundCap

0x20

一个圆角的线条端

PenJoinStyle

线条连接样式定义了如何使用QPainter绘制两条连接线之间的连接。连接样式仅适用于宽线(宽度为1或更大)。 在Qt的 Qt:: PenJoinStyle 枚举提供了以下样式,setJoinStyle()设置:

QPen003

样式

描述

Qt::MiterJoin

0x00

线连接的外边缘延伸成一定角度相交,并填充

Qt::BevelJoin

0x40

两条线之间的三角形缺口,直接用线连接填充

Qt::RoundJoin

0x80

连接之间缺口,用圆弧连接填充

Qt::SvgMiterJoin

0x100

与SVG 1.2 Tiny规范中的斜接定义相对应的斜接

13.1.2.2. QBrush

QBrush对象用于设置QPainter绘制时一个区域的填充效果,具有样式,颜色,渐变和纹理等属性。

QBrush使用setColor()设置笔刷颜色,setStyle()设置笔刷样式,setTexture()设置图片作为笔刷。

笔刷样式使用 Qt::BrushStyle 枚举定义填充图案,Qt支持的画笔样式如下:

样式

描述

Qt::NoBrush

0

没有画笔图案

Qt::SolidPattern

1

颜色均匀,单一颜色填充

Qt::Dense1Pattern

2

极其密集的画笔图案

Qt::Dense2Pattern

3

非常密集的画笔图案

Qt::Dense3Pattern

4

有点密集的画笔图案

Qt::Dense4Pattern

5

半密实的画笔图案

Qt::Dense5Pattern

6

有点稀疏的画笔图案

Qt::Dense6Pattern

7

非常稀疏的画笔图案

Qt::Dense7Pattern

8

极稀疏的画笔图案

Qt::HorPattern

9

水平线填充

Qt::VerPattern

10

垂直线填充

Qt::CrossPattern

11

跨越水平和垂直线

Qt::BDiagPattern

12

向后的对角线

Qt::FDiagPattern

13

向前的对角线

Qt::DiagCrossPattern

14

交叉对角线

Qt::LinearGradientPattern

15

线性渐变(使用专用的QBrush构造函数设置)

Qt::ConicalGradientPattern

17

锥形渐变(使用专用的QBrush构造函数设置)

Qt::RadialGradientPattern

16

辐射形渐变(使用专用的QBrush构造函数设置)

Qt::TexturePattern

24

自定义填充(请参阅QBrush::setTexture()函数)

其效果如下:

QBrush001

13.1.2.3. QGradient

QGradient 和QBrush组合使用,可以实现包括从均匀颜色到非常稀疏的图案的基本图案,各种线条组合,渐变填充和纹理等等效果。

Qt提供了三种渐变填充类:QLinearGradient,QConicalGradient和QRadialGradient,这三种都继承自QGradient。

  • QLinearGradient 是线性渐变,指定一个起点和颜色,一个终点和颜色,起点到终点的颜色按线性插值计算,得到渐变的填充颜色。 其中,还可以知道中间点的颜色。

  • QRadialGradient 是辐射形渐变,有简单的辐射渐变和扩展的辐射渐变两种方式,简单的方式是在圆内一个焦点和一个端点之间渐变, 扩展辐射是在一个焦点圆和一个中心圆之间渐变。

  • QConicalGradient 是圆锥渐变,围绕一个中心点逆时针渐变。

三种渐变的效果:

QBrush002

13.1.3. QPainter绘图基础图形

QPainter提供了许多接口用于绘制基础图像,包括点,直线,椭圆等,每个接口函数 都有很多参数形式,下面将简单介绍下这些函数。

13.1.3.1. drawPoint

绘制点函数原型:

void QPainter::drawPoint(const QPointF &position)

示例程序:

lubancat_qt_tutorial_code/Graphics/QPainter/mainwindow.cpp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
//绘制点
void MainWindow::myDrawPoints(QPainter *qp)
{
    qp->setPen(QColor(255,0,0));
    qp->setBrush(QColor(255,0,0));
    QPolygonF points;

    for(int i=0; i<10; i++)
        for(int j=0; j<10; j++)
        {
            qp->drawPoint(QPoint(305+i*10,110+j*10));  //绘制单个点,在指定位置
            points.append(QPoint(405+i*10,110+j*10));
        }

    qp->setPen(QColor(0,0,255));     //重新设置颜色
    qp->setBrush(QColor(0,0,255));   //重新设置填充颜色
    qp->drawPoints(points);          //批量绘制点
}

13.1.3.2. drawLine

绘制直线,函数原型(更多重载函数看下Qt帮助手册):

void QPainter::drawLine(const QLineF &line)

void QPainter::drawLines(const QLineF *lines, int lineCount)

示例程序:

lubancat_qt_tutorial_code/Graphics/QPainter/mainwindow.cpp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
void MainWindow::myDrawLines(QPainter *qp)
{
    QPen pen;

    //画直线
    pen.setWidth(5);
    pen.setStyle(Qt::SolidLine);    //宽线
    pen.setCapStyle(Qt::SquareCap); //端点样式是方形线条端
    qp->setPen(pen);
    qp->drawLine(QPointF(5, 110), QPointF(195, 110));

    pen.setStyle(Qt::DashLine);     //虚线
//    pen.setColor(QColor(255, 0, 0));
    qp->setPen(pen);
    qp->drawLine(QPointF(5, 130), QPointF(195, 130));

    pen.setStyle(Qt::DotLine);
    pen.setCapStyle(Qt::FlatCap);
    qp->setPen(pen);
    qp->drawLine(QPointF(5, 150), QPointF(195, 150));

    pen.setStyle(Qt::DashDotLine);
//    pen.setColor(QColor(255, 255, 0));
    qp->setPen(pen);
    qp->drawLine(QPointF(5, 170), QPointF(195, 170));

    pen.setStyle(Qt::DashDotDotLine);
    pen.setCapStyle(Qt::RoundCap);
    qp->setPen(pen);
    qp->drawLine(QPointF(5, 190), QPointF(195, 190));

    pen.setStyle(Qt::CustomDashLine);
    pen.setWidth(2);
    qp->setPen(pen);
    qp->drawLine(QPointF(5, 210), QPointF(195, 210));
}

13.1.3.3. drawRect

绘制矩形,函数原型:

void QPainter::drawRect(const QRectF &rectangle)

示例程序:

lubancat_qt_tutorial_code/Graphics/QPainter/mainwindow.cpp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
void MainWindow::myDrawRect(QPainter *qp)
{
    QPen pen;
    QBrush brush;

    //画矩形,有填充矩形
    pen.setColor(QColor(255, 0, 0));
    brush.setStyle(Qt::SolidPattern);
    brush.setColor(QColor(0, 255, 0, 125));
    qp->setPen(pen);
    qp->setBrush(brush);
    qp->drawRect(505, 110, 90, 90);

    //画矩形,无填充矩形
    pen.setStyle(Qt::SolidLine);
    pen.setColor(QColor(255, 0, 0));
    brush.setStyle(Qt::NoBrush);
    qp->setPen(pen);
    qp->setBrush(brush);
    qp->drawRect(605, 110, 90, 90);
}

13.1.3.4. drawRoundedRect

绘制圆角的矩形,函数原型:

void QPainter::drawRoundedRect(const QRectF &rect, qreal xRadius, qreal yRadius, Qt::SizeMode mode = Qt::AbsoluteSize)

示例程序:

lubancat_qt_tutorial_code/Graphics/QPainter/mainwindow.cpp
1
2
QRectF rectangle(705.0, 110.0, 90.0, 90.0);
qp->drawRoundedRect(rectangle, 20.0, 20.0);

13.1.3.5. drawEllipse

绘制圆,函数原型:

void QPainter::drawEllipse(const QRectF &rectangle)

示例程序:

lubancat_qt_tutorial_code/Graphics/QPainter/mainwindow.cpp
1
2
3
4
5
//椭圆
qp->drawEllipse(QRectF(305,220,90,60));

//圆
qp->drawEllipse(QRectF(405,210,90,90));

13.1.3.6. drawArc

绘制弧线,函数原型:

void QPainter::drawArc(const QRectF &rectangle, int startAngle, int spanAngle)

示例程序:

lubancat_qt_tutorial_code/Graphics/QPainter/mainwindow.cpp
1
2
3
int startAngle = 30 * 16;//起始角度
int spanAngle = 120 * 16;//跨越度数
qp->drawArc(QRectF(5.0, 210.0, 90.0, 90.0), startAngle, spanAngle);

13.1.3.7. drawPie

绘制扇形,函数原型:

void QPainter::drawPie(const QRectF &rectangle, int startAngle, int spanAngle)

示例程序:

lubancat_qt_tutorial_code/Graphics/QPainter/mainwindow.cpp
1
2
3
int startAngle = 30 * 16;//起始角度
int spanAngle = 120 * 16;//跨越度数
qp->drawPie(QRectF(105.0, 210.0, 90.0, 90.0), startAngle, spanAngle);

13.1.3.8. drawChord

绘制带弦的弧,函数原型:

void QPainter::drawChord(const QRectF &rectangle, int startAngle, int spanAngle)

示例程序:

lubancat_qt_tutorial_code/Graphics/QPainter/mainwindow.cpp
1
2
3
int startAngle = 30 * 16;//起始角度
int spanAngle = 120 * 16;//跨越度数
qp->drawChord(QRect(205, 210, 90, 90), startAngle, spanAngle);

13.1.3.9. drawPolyline

绘制折线,函数原型:

void QPainter::drawPolyline(const QPointF *points, int pointCount)

示例程序:

lubancat_qt_tutorial_code/Graphics/QPainter/mainwindow.cpp
1
2
3
4
5
6
7
8
QPointF Polylinepoints[5] = {
    QPointF(505.0, 210.0),
    QPointF(555.0, 290.0),
    QPointF(535.0, 275.0),
    QPointF(595.0, 295.0),
    QPointF(505.0, 215.0),
};
qp->drawPolyline(Polylinepoints, 5);

13.1.3.10. drawConvexPolygon

绘制多边形,函数原型:

void QPainter::drawPolygon(const QPolygon &points, Qt::FillRule fillRule = Qt::OddEvenFill)

示例程序:

lubancat_qt_tutorial_code/Graphics/QPainter/mainwindow.cpp
1
2
3
4
5
6
7
QPointF Polygonpoints[4] = {
    QPointF(610.0, 280.0),
    QPointF(620.0, 210.0),
    QPointF(680.0, 230.0),
    QPointF(690.0, 270.0),
};
qp->drawConvexPolygon(Polygonpoints, 4);

13.1.3.11. 渐变

绘制多边形,函数原型:

void QPainter::drawPolygon(const QPolygon &points, Qt::FillRule fillRule = Qt::OddEvenFill)
lubancat_qt_tutorial_code/Graphics/QPainter/mainwindow.cpp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
void MainWindow::myDrawGradient(QPainter *qp)
{
    //线性渐变
    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);
    qp->setBrush(linearGradient);
    qp->drawRect(5, 310, 90, 90);

    //辐射渐变
    QRadialGradient radialGradient(QPointF(150, 350),40,QPointF(550,40));
    radialGradient.setColorAt(0, QColor(255, 255, 100, 150));
    radialGradient.setColorAt(1, QColor(0, 0, 0, 50));
    qp->setBrush(radialGradient);
    qp->drawEllipse(QPointF(150, 350), 40, 40);

    //锥形渐变
    QConicalGradient conicalGradient(QPointF(250, 350), 100);
    conicalGradient.setColorAt(0.2, Qt::cyan);
    conicalGradient.setColorAt(0.9, Qt::black);
    qp->setBrush(conicalGradient);
    qp->drawEllipse(QPointF(250, 350), 40, 40);
}

13.1.3.12. 文字

绘制文字,函数原型:

void QPainter::drawPolygon(const QPolygon &points, Qt::FillRule fillRule = Qt::OddEvenFill)

示例程序:

lubancat_qt_tutorial_code/Graphics/QPainter/mainwindow.cpp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void MainWindow::myDrawText(QPainter *qp)
{
    //设置一个矩形
    QRectF rect(305, 310, 190, 90);
    qp->drawRect(rect);
    qp->setPen(QColor(Qt::red));
    qp->drawText(rect, Qt::AlignHCenter, "野火电子");

    //绘制文字
    QFont font("宋体", 10, QFont::Bold, true);
    font.setLetterSpacing(QFont::AbsoluteSpacing,5);
    qp->setFont(font);
    qp->drawText(310, 350, "Qt 嵌入式教程");

    //绘制文字
    qp->setPen(Qt::green);
    font.setLetterSpacing(QFont::AbsoluteSpacing,0);
    font.setBold(false);
    font.setFamily("黑体");
    font.setPointSize(10);
    font.setItalic(true);
    qp->setFont(font);
    qp->drawText(310, 380, "绘制文字");
}

13.1.3.13. QPainterPath

QPainterPath类为绘制操作提供了一个容器,使绘制图形能够被构建和重用,函数原型:

void QPainter::drawPolygon(const QPolygon &points, Qt::FillRule fillRule = Qt::OddEvenFill)

示例程序:

lubancat_qt_tutorial_code/Graphics/QPainter/mainwindow.cpp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
void MainWindow::myDrawPath(QPainter *qp)
{

    //简单的使用QPainterPath,使绘制图形能够被构建和重用
    QPainterPath path1;
    path1.addEllipse(510, 320, 50, 50);  //绘制一个圆
    path1.lineTo(505, 380);              //绘制一直线
    qp->setPen(Qt::blue);
    qp->setBrush(Qt::red);               //填充红色
    qp->drawPath(path1);

    //复制图形
    QPainterPath path2;
    path2.addPath(path1);
    path2.translate(70,0);
    qp->drawPath(path2);

    //绘制线
    QPainterPath path4;
    qp->setBrush(Qt::transparent);
    path4.lineTo(0,105);
    path4.lineTo(this->width(),105);
    path4.lineTo(this->width(),205);
    path4.lineTo(0,205);
    path4.lineTo(0,305);
    path4.lineTo(this->width(),305);
    path4.lineTo(this->width(),405);
    path4.lineTo(0,405);
    qp->drawPath(path4);
}

13.1.3.14. 图片处理

绘制图片,函数原型:

void QPainter::drawPolygon(const QPolygon &points, Qt::FillRule fillRule = Qt::OddEvenFill)
lubancat_qt_tutorial_code/Graphics/QPainter/mainwindow.cpp
1
2
3
4
5
6
7
8
//简单绘制图片
QPixmap pix;
pix.load(":/image/test.png");
painter.drawPixmap(650, 305, 25, 25, pix);

//缩放
pix = pix.scaled(pix.width()*2, pix.height()*2,Qt::KeepAspectRatio);
painter.drawPixmap(650, 330, pix);

13.2. QSS

在Web开发中, CSS(层叠样式表) 主要用来设计网页的样式,美化网页; 它不仅可以静态地修饰网页,还可以配合各种脚本语言动态地对网页各元素进行格式化。 CSS能够对网页中元素位置的排版进行像素级精确控制,可以有效地对页面的布局、字体、颜色、背景和其它效果实现更加精确的控制, 拥有对网页对象和模型样式编辑的能力。

在Qt中也引入了类似的概念,可以称之为 QSS (Qt Style Sheets, Qt样式表),它可以定义界面的组件样式,使界面程序呈现特殊的显示效果。

13.2.1. QSS语句格式

Qt样式表的句法与HTML CSS的规则几乎相同。QSS中包含一系列的样式规则, 样式规则由一个选择器(selector)和一个声明(declaration )组成。选择器指定哪些小部件受规则影响;声明指定应该在小部件上设置哪些属性。例如:

QPushButton { color: red; background-color: white }

其中QPushButton是选择器,{ color: red; background-color: white }是声明,声明中的每条样式规则由属性和值组成,即 property: value , 每条样式以分号隔开。 例如声明中的 color: red; 表示color属性,值为red。

Qt样式表支持各种属性、伪状态和子控件,使自定义小部件的外观成为可能,关于样式表属性可以参考下:https://doc.qt.io/qt-5/stylesheet-reference.html

13.2.1.1. 选择器

到目前为止,所有的示例都使用了最简单的选择器类型,即类型选择器。Qt样式表支持CSS2中定义的所有选择器,下表总结了最有用的选择器类型(以QPushButton示例):

选择器

示例

描述

通用选择器

所有组件

类型选择器

QPushButton

所有QPushButton类及其子类的组件

属性选择器

QPushButton[flat=false]

匹配flat属性为false的QPushButton类及其子类的组件,也可以使用动态属性

非子类选择器

.QPushButton

所有QPushButton类的组件,但是不包括 OPushButton 的子类

ID选择器

QPushButton#okButton

对象名称为 btnOK的OPushButton实例

从属对象选择器

QDialog QPushButton

匹配从属于QDialog的 QPushButton类的实例

子对象选择器

QDialog > QPushButton

所有直接从属于ODialog的OPushButton类的实例

选择器可以包含伪状态,伪状态表示基于小部件的状态限制规则的应用。伪状态出现在选择器的末尾,中间有一个冒号(:)。例如下面按鼠标的状态:

  • :hover 鼠标移动到控件上

  • :pressed 鼠标按下

  • :checked 鼠标点击

  • :release 鼠标抬起

lubancat_qt_tutorial_code/Graphics/QSS/mainwindow.ui
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
QToolButton{
    border-image: url(:/resource/image/light/6-social-person.png);
    border-style: flat;
    border-radius:5px;padding:2px 4px;
}

# 鼠标移动到控件上时
QToolButton:hover{
    border-image: url(:/resource/image/dark/6-social-person.png);
    border-radius:5px;padding:2px 4px;
}

# 鼠标按下时
QToolButton:pressed{

    border-image: url(:/resource/image/light/6-social-person.png);
    border-style: flat;
    border-radius:5px;padding:2px 4px;
}

# 鼠标点击时
QToolButton:checked{
    border-image: url(:/resource/image/light/6-social-person.png);
    border-radius:5px;padding:2px 4px;
}

# 鼠标释放时
QToolButton:release{
    border-image: url(:/resource/image/light/6-social-person.png);
    border-radius:5px;padding:2px 4px;
}

这样就可以通过QSS来控制按键不同状态显示不同的样式。

关于选择器更多的规则,比如子控件、冲突解决、级联等等,可以参考下https://doc.qt.io/qt-5/stylesheet-syntax.html

13.2.2. 样式表使用

Qt Designer中集成了QSS的编辑功能,在Qt Designer界面我们右击选中组件,在弹出的窗口选择 更改样式表...,就可以弹出样式表编辑对话框。 这样样式表会自动保存在UI文件中,创建窗口时会自动调用。

也可以直接使用函数setStyleSheet()设置样式表,例如:

1
qApp->setStyleSheet("QLineEdit { background-color: yellow }");

这里使用全局变量qApp为QLineEdit设置样式,设置背景颜色为黄色。

13.2.2.1. 属性

在使用CSS或QSS时,使用样式表可以定义复杂的显示效果,每个界面组件具有四个同心矩形的框(box model,盒子模型):边距矩形,边框矩形,填充矩形和内容矩形。

QSS001
  • Content(内容) 控件的内容区域,显示文本和图像。

  • Padding(填充) 包围内容的区域,填充也是透明的。

  • Border(边框) 围绕在内边距和内容外的边框。

  • Margin(边距) 清除边框外的区域,边距是透明的,与父组件的之间的空白边距。

在使用QSS的时候,我们可以通过特定的属性来控制上述四个盒子的样式,从而使控件呈现出我们想要的效果。 比如说margin,分别定义了上下左右四个边距大小:

  • margin-top 上边距。

  • margin-right 右边距。

  • margin-bottom 底边距。

  • margin-left 左边距。

我们完全可以通过指定上下左右边距的值来实现下面的效果:

QSS002

除了上面,常见的属性比如background,是用来设置背景,其中又可以细分如下:

  • background-color 背景颜色

  • background-image 背景图像

  • background-repeat 背景图像的填充方式

  • background-position 背景图片的对齐方式

  • background-attachment 背景图像是相对于视口滚动还是固定的

  • background-clip background-color和background-image被剪切到的矩形

  • background-origin 背景矩形,与background-position和结合使用

lubancat_qt_tutorial_code/Graphics/QSS/mainwindow.ui
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
QPushButton {
    color: rgb(206, 246, 223);
    background-color:rgb(18, 210, 100);

    border-radius:20px;padding:10px 40px;
}

QPushButton :pressed{
    color: rgb(206, 246, 223);
    background-color:rgba(18, 216, 105, 5);

    border-radius:20px;padding:10px 40px;
}

例如在示例中,我们对控件设置如上面的样式,其中设置控件的背景颜色(background-color)为rgba(18, 216, 105, 5), 文字颜色(color)为rgb(206, 246, 223),并通过border-radius设置控件显示为圆角。

QSS003

Qt中支持的属性可参考下: https://doc.qt.io/qt-5/stylesheet-reference.html#list-of-properties

样式表详细的描述和使用使用参考下: https://doc.qt.io/qt-5/stylesheet-customizing.html

13.3. 例程说明

本章配套例程在 lubancat_qt_tutorial_code/Graphics 目录下

例程中包含两个示例demo,QtPainter和QSS,QtPainter演示了Qt 2D绘图,QSS则演示了在Qt中使用样式表

13.3.1. 编程思路

QtPainter

  • 新建空白工程

  • Qt绘图事件 paintEvent()

  • 在paintEvent()中绘制各种2D图形

QSS

  • 使用Qt Designer 模仿360界面,对控件进行布局

  • 使用QSS控制控件样式,最终达到相近的效果

13.3.2. 主要代码讲解

绘图和QSS代码在前面都有讲解,这里讲一下如何实现点击窗口任意位置都可拖动窗口。 实现思路如下:

当鼠标按下时,接收鼠标按下事件,记录下鼠标和窗口的初始位置; 按下过程中,移动鼠标会产生鼠标移动事件,根据鼠标和窗口的相对位置,移动窗口; 松开鼠标时,会产生鼠标抬起事件,窗口移动结束。

lubancat_qt_tutorial_code/Graphics/QSS/mainwindow.h
1
2
3
4
5
6
bool        windowsDrag;
QPoint      mouseStartPoint;
QPoint      windowTopLeftPoint;
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
lubancat_qt_tutorial_code/Graphics/QSS/mainwindow.h
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//拖拽操作
void MainWindow::mousePressEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton)
    {
        windowsDrag = true;
        //获得鼠标的初始位置
        mouseStartPoint = event->globalPos();
        //mouseStartPoint = event->pos();
        //获得窗口的初始位置
        windowTopLeftPoint = this->frameGeometry().topLeft();
    }
}

void MainWindow::mouseMoveEvent(QMouseEvent *event)
{
    if(windowsDrag)
    {
        //获得鼠标移动的距离
        QPoint distance = event->globalPos() - mouseStartPoint;
        //QPoint distance = event->pos() - mouseStartPoint;
        //改变窗口的位置
        this->move(windowTopLeftPoint + distance);
    }
}

void MainWindow::mouseReleaseEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton)
    {
        windowsDrag = false;
    }
}

13.3.3. 运行结果

在PC 虚拟机上测试,直接点击编译并运行程序,结果如下:

QtPainter例程:

result001

QSS例程:

result001