目录
[1.3.2.2:代码示例2(样式的层叠特性)](#1.3.2.2:代码示例2(样式的层叠特性))
[1.3.5:使用Qt Designer 编辑样式](#1.3.5:使用Qt Designer 编辑样式)
[1.6.1 按钮](#1.6.1 按钮)
1.QSS
1.1:背景介绍
在网页前端开发领域中, CSS是⼀个至关重要的部分. 描述了⼀个的 "样式". 从而起到对网页美化的作用.
所谓样式, 包括不限于大小, 位置, 颜⾊, 背景, 间距, 字体等等.
现在的网页很难找到没有 CSS 的. 可以说让 "界面好看" 是⼀个刚需.
- 网页开发作为 GUI 的典型代表, 也对于其他客户端GUI开发产生了影响. Qt 也是其中之⼀.Qt 仿照 CSS 的模式, 引入了 QSS, 来对 Qt 中的控件做出样式上的设定, 从而允许程序猿写出界面更好看的代码.
- 当然, 由于Qt 本⾝的设计理念和网页前端还是存在⼀定差异的, 因此QSS 中只能支持部分 CSS 属性.整体来说 QSS 要比CSS 更简单⼀些.
1.2:基本语法
对于CSS来说,基本的语法结构非常简单
选择器 {
属性名:属性值;
}
QSS 沿用了这样的设定.
选择器 {
属性名: 属性值;
}
其中:
- 选择器描述了 "哪个 widget 要应用样式规则".
- 属性则是⼀个键值对, 属性名表示要设置哪种样式, 属性值表示了设置的样式的值.
css
QPushButton { color: red; };
QPushButton {
color: red;
}
上述代码的含义表示, 针对界面上所有的 QPushButton,都把文本颜色设置为红色.
1.2.1:示例代码
cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
this->setStyleSheet("QPushButton {color: red; }");
}
Widget::~Widget()
{
delete ui;
}

上述代码中, 我们是只针对这⼀个按钮通过 setStyleSheet方法设置的样式. 此时这个样式
仅针对该按钮生效. 如果创建其他按钮, 其他按钮不会受到影响.
1.3:QSS设置方式
1.3.1:指定控件样式设置
- QWidget 中包含了 setStyleSheet方法,可以直接设置样式. 上述代码博主已经演示了上述设置方式.
- 另一方面,给指定控件设置样式之后,该控件的子元素也会受到影响.
1.3.1.1:代码示例1(子元素受到影响)
在界面上创建一个按钮.

修改 widget.cpp, 这次我们不再给按钮设置样式, 而是给 Widget 设置样式(Widget 是
QPushButton 的父控件).
widget.cpp
cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//给Widget本身设置样式
this->setStyleSheet("QPushButton { color: red;} ");
}
Widget::~Widget()
{
delete ui;
}
运行程序, 可以看到样式对于子控件按钮同样会生效.

1.3.2:全局样式设置
1.3.2.1:代码示例1
main.cpp
cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
a.setStyleSheet("QPushButton {color: red}");
Widget w;
w.show();
return a.exec();
}
运行程序,可以看到此时三个按钮的颜⾊都设置为红色了.

1.3.2.2:代码示例2(样式的层叠特性)
如果通过全局样式给某个控件设置了属性1, 通过指定控件样式给控件设置属性2, 那么这两个属性都会产生作用.
编写 main.cpp, 设置全局样式, 把按钮⽂本设置为红色
main.cpp
cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
a.setStyleSheet("QPushButton { color: red }");
Widget w;
w.show();
return a.exec();
}
编写 widget.cpp, 给第⼀个按钮设置字体大小
weight.cpp
cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->pushButton->setStyleSheet("QPushButton { font-size: 50px}");
}
Widget::~Widget()
{
delete ui;
}
运行程序, 可以看到, 对于第⼀个按钮来说, 同时具备了颜⾊和字体大小样式. 而第⼆个按钮只有颜色样式.说明针对第⼀个按钮, 两种设置方式设置的样式, 叠加起来了.

形如上述这种属性叠加的效果, 我们称为 "层叠性".
CSS 全称为 Cascading Style Sheets, 其中 Cascading 就是 "层叠性" 的意思. QSS 也继承了这样的设定.
实际上把 QSS 叫做 QCSS 也许更合适⼀些~
1.3.3:样式的优先级
如果全局样式, 和指定控件样式冲突, 则指定控件样式优先展示.
编辑 main.cpp, 把全局样式设置为红色.
代码示例1
main.cpp
cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
a.setStyleSheet("QPushButton { color : red;}");
Widget w;
w.show();
return a.exec();
}
weight.cpp
cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->pushButton->setStyleSheet("QPushButton { color: green;}");
}
Widget::~Widget()
{
delete ui;
}
运行程序, 观察效果. 可以看到第⼀个按钮已经成为绿色了, 但是第⼆个按钮仍然是红色.

1.3.4:从文件加载样式表
上述代码都是把样式通过硬编码的方式设置的. 这样使 QSS 代码和 C++ 代码耦合在⼀起了, 并不方便代码的维护.因此更好的做法是把样式到单独的文件中,然后通过读取文件的方式来加载样式.
1.3.4.1:代码示例1(从文件加载全局样式)
在界⾯上创建⼀个按钮

创建 resource.qrc ⽂件, 并设定前缀为 / .

创建 style.qss ⽂件, 并添加到 resource.qrc 中

- style.qss 是需要程序运行时加载的. 为了规避绝对路径的问题, 仍然使⽤ qrc 的方式来组
织. (即把资源文件内容打包到 cpp 代码中).
Qt Creator 没有提供创建 qss ⽂件的选项. 咱们直接 右键 -> 新建⽂件 -> 手动设置文件扩展名 为qss 即可
使用Qt Creator 打开 style.qss ,编写内容

修改 main.cpp, 新增⼀个函数用来加载样式并且调用该函数,来设置样式
main.cpp
cpp
#include "widget.h"
#include <QApplication>
#include <QFile>
QString LoadQss()
{
QFile file(":/style.qss");
// 读取⽂件内容. 虽然 readAll 返回的是 QByteArray, 但是 QString 提供了
//QByteArray 版本的构造函数
file.open(QFile::ReadOnly);
QString style = file.readAll();
file.close();
return style;
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
const QString & style = LoadQss();
a.setStyleSheet(style);
Widget w;
w.show();
return a.exec();
}

这里博主得吐槽⼀下. 理论上来说 Qt 应该要提供, 直接从文件加载样式表的接口. 类似于
setStyleSheetFromFile(const QString& path) 这种, 在内部把读文件操作封装好.
1.3.5:使用Qt Designer 编辑样式
QSS 也可以通过 Qt Designer 直接编辑, 从而起到实时预览的效果. 同时也能避免 C++ 和 QSS 代码的耦合.
代码示例1
在界面上创建⼀个按钮

右键按钮, 选择 "改变样式表"

在弹出的样式表编辑器中, 可以直接填写样式. 填写完毕, 点击 确定 即可.

此时 Qt Designer 的预览界面就会实时显示出样式的变化

运行程序, 可以看到样式确实发生了改变

这种方式设置样式, 样式内容会被以 xml 格式记录到 ui 文件中.

同时在控件的 styleSheet 属性中也会体现.

当我们发现⼀个控件的样式不符合预期的时候, 要记得排查这四个地⽅:
- 全局样式.
- 指定控件样式.
- qss 文件中的样式.
- ui文件中的样式.
1.4:选择器
1.4.1:选择器概况
|-------------------------------|-----------------------------------|-----------------------------------------------------------------------|
| 选择器 | 示例 | 说明 |
| 全局选择器 | * | 选择所有的widget |
| 类型选择器 (type selector) | QPushButton | 选择所有的 QPushButton和其子类的控件. |
| 类选择器 (class selector) | .QPushButton | 选择所有的 QPushButton 的控件. 不会选择子类 |
| ID 选择器 | #pushButton_2 | 选择 objectName 为 pushButton_2 的控件. |
| 后代选择器 | QDialog QPushButton | 选择QDialog的所有后代(子控件, 孙子控件等等)中的QPushButton. |
| 子选择器 | QDialog > QPushButton | 选择 QDialog 的所有子控件中的 QPushButton. |
| 并集选择器 | QPushButton, QLineEdit, QComboBox | 选择 QPushButton, QLineEdit, QComboBox 这三种控件. (即接下来的样式会针对这三种控件都生效). |
| 属性选择器 | QPushButton[flat="false"] | 选择所有 QPushButton 中, flat 属性为 false 的控件. |
总体来说, QSS 选择器的规则和 CSS 选择器基本⼀致. 上述选择器咱们也不需要全都掌握, 就只熟悉最常用的几个即可(上述加粗的)
代码示例1(使用全局选择器)
在界面上创建⼀个按钮.

- 修改 main.cpp, 设置全局样式.
- 注意, 此处选择器使用的是 QWidget . QPushButton 也是 QWidget 的子类, 所以会受到 QWidget 选择器的影响.
main.cpp
cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
a.setStyleSheet("QWidget{color:red}");
Widget w;
w.show();
return a.exec();
}
运行程序,可以看到按钮的文本颜色已经是红色了.

如果把上述样式代码修改为下列代码
cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
//a.setStyleSheet("QWidget{color:red}");
a.setStyleSheet(".QWidget { color: red; }");
Widget w;
w.show();
return a.exec();
}
此时按钮的颜色不会发生改变. 此时只是选择 QWidget 类, 而不会选择 QWidget 的子类
QPushButton 了.

代码示例2(使用id选择器)
在界面上创建 3 个按钮, objectName 为 pushButton , pushButton_2 , pushButton_3

- 编写 main.cpp, 设置全局样式
- 先通过 QPushButton 设置所有的按钮为黄色.
- 再通过#pushButton 和 #pushButton_2 分别设置这两个按钮为红色和绿色.
main.cpp
cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QString style = "";
style += "QPushButton{color: yellow;}";
style += "#pushButton{color: red;}";
style += "#pushButton_2{color: green;}";
a.setStyleSheet(style);
Widget w;
w.show();
return a.exec();
}
执行程序, 观察效果

- 当某个控件身上, 通过类型选择器和 ID 选择器设置了冲突的样式时, ID 选择器样式优先级更高.
- 同理, 如果是其他的多种选择器作用同⼀个控件时出现冲突的样式, 也会涉及到优先级问题.Qt文档上有具体的优先级规则介绍 (参照The Style Sheet Syntax 的 Conflict Resolution 章节).
- 这里的规则计算起来⾮常复杂(CSS 中也存在类似的设定), 咱们此处对于优先级不做进⼀步讨论.
- 实践中我们可以简单的认为, 选择器描述的范围越精准, 则优先级越高. ⼀般来说, ID 选择器优先级是最高的.
代码示例3(使用并集选择器)

编写 main.cpp, 设置全局样式
main.cpp
cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
a.setStyleSheet("QPushButton, QLabel, QLineEdit {color : red;} ");
Widget w;
w.show();
return a.exec();
}
运行程序, 可以看到这三种控件的文字颜色都设置为了红色

并集选择器是⼀种很好的代码复用的方式. 很多时候我们希望界面上的多个元素风格是统⼀
的, 就可以使用并集选择器, 把样式属性同时指定给多种控件.
1.4.2:子控件选择器
-
有些控件内部包含了多个 "子控件" . 比如 QComboBox 的下拉后的⾯板, 比如 QSpinBox 的上下按钮等.
-
可以通过子控件选择器 ::,针对上述⼦控件进行样式设置
哪些控件拥有哪些⼦控件, 参考文档Qt Style Sheets Reference 中 List of Sub-Controls 章
节.
代码示例1(设置下拉框的下拉按钮样式)
在界面上创建⼀个下拉框, 并创建几个选项

创建 resource.qrc , 并导入图片 Down.png.

- 修改 main.cpp, 编写全局样式.
- 使用子控件选择器 QComboBox::down-arrow 选中了 QComboBox 的下拉按钮.
- 再通过 image 属性设置图片.
main.cpp
cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QString style = "";
style += "QComboBox::down-arrow{image:url(:/Down.png)}";
a.setStyleSheet(style);
Widget w;
w.show();
return a.exec();
}
执行程序, 观察效果.

1.4.3:伪类选择器
伪类选择器, 是根据控件所处的某个状态被选择的. 例如按钮被按下, 输入框获取到焦点, 鼠标移动到某个控件上等.
- 当状态具备时, 控件被选中, 样式生效.
- 当状态不具备时, 控件不被选中, 样式失效.
- 使用:的方式定义伪类选择器.
常用的伪类选择器
|------------|----------|
| 伪类选择器 | 说明 |
| :hover | 鼠标放到控件上 |
| :pressed | 鼠标左键按下时 |
| :focus | 获取输入焦点时 |
| :enabled | 元素处于可用时 |
| :checked | 被勾选时 |
| :read-only | 元素为只读状态时 |
这些状态可以使用 ! 来取反. 比如 :!hover 就是鼠标离开控件时, :!pressed 就是鼠标松开时,
等等.
代码示例1(设置按钮的伪类样式)
在界面上创建⼀个按钮

编写 main.cpp, 创建全局样式
main.cpp
cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QString style = "";
style += "QPushButton{color:red;}";
style += "QPushButton:hover{color:green;}";
style += "QPushButton:pressed{color:blue;}";
a.setStyleSheet(style);
Widget w;
w.show();
return a.exec();
}
运行程序, 可以看到, 默认情况下按钮文字是红⾊, 鼠标移动上去是绿⾊, 鼠标按下按钮是蓝色



1.5:样式属性
QSS 中的样式属性非常多, 不需要都记住. 核⼼原则还是⽤到了就去查. 大部分的属性和 CSS 是⾮常相似的.
文档的 Qt Style Sheets Reference 章节详细介绍了哪些控件可以设置属性, 每个控
件都能设置哪些属性等.
1.5.1:盒模型

⼀个遵守盒模型的控件, 由上述几个部分构成.
- Content 矩形区域:存放控件内容. 比如包含的文本/图标等.
- Border 矩形区域:控件的边框.
- Padding 矩形区域:内边距. 边框和内容之间的距离.
- Margin 矩形区域:外边距. 边框到控件 geometry 返回的矩形边界的距离.
默认情况下,外边距,内边距,边框宽度都是 0.
可以通过⼀些 QSS 属性来设置上述的边距和边框的样式
|--------------|-----------------------------------------------------|
| QSS属性 | 说明 |
| margin | 设置四个方向的外边距.复合属性 |
| padding | 设置四个方向的内边距.复合属性. |
| border-style | 设置边框样式 |
| border-width | 边框的粗细 |
| border-color | 边框的颜⾊ |
| border | 复合属性,相当于 border-style + border-width + border-color |

代码示例1(设置边框和内边距)
在界面上创建⼀个 label

- border: 5px solid red 相当于 border-style: solid; border-width: 5px;
- border-color: red; 三个属性的简写形式.
- padding-left: 10px; 是给左侧设置内边距.
main.cpp
cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
a.setStyleSheet("QLabel{border:5px solid red; padding-left:10px;}");
Widget w;
w.show();
return a.exec();
}
运行程序, 可以看到样式发生了变化

代码示例2(设置外边距)
widget.cpp
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QPushButton>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QPushButton * Button = new QPushButton(this);
Button->setGeometry(0,0,100,100);
Button->setText("Hello World");
Button->setStyleSheet("QPushButton{border:5px solid red;margin: 20px}");
const QRect & Rect = Button->geometry();
qDebug() << Rect;
}
Widget::~Widget()
{
delete ui;
}

1.6:控件样式示例
1.6.1 按钮
下面给出⼀些常用控件的样式示例
界⾯上创建⼀个按钮

右键 -> 改变样式表, 使用Qt Designer 设置样式

cpp
QPushButton {
font-size: 20px;
border: 2px solid #8f8f91;
border-radius: 15px;
background-color: #dadbde;
}
QPushButton:pressed {
background-color: #f6f7fa;
}
执行程序, 可以看到效果
按钮点击之前

按钮点击之后

属性小结
|------------------|---------------------------|
| 属性 | 说明 |
| font-size | 设置文字大小 |
| border-radius | 设置圆⻆矩形. 数值设置的越大, 角就 "越圆". |
| background-color | 设置背景颜⾊. |
形如 #dadbde 是计算机中通过⼗六进制表示颜色的方式.
这⾥在 "常用控件" 的博客已经介绍过啦. 此处不再赘述.
另外, 在实际开发中, 颜色具体使⽤哪种好看, 是需要⼀定的 "艺术细菌" 的. 往往公司会有专门 的美工/设计人员来负责. 程序猿只需要按照设计稿实现程序即可.
1.6.2:复选框
代码示例(自定义复选框)
创建一个resource.qrc文件.并导入以下图片

- 使用黑色作为默认形态.
- 使用蓝色作为 hover 形态.
- 使用红色作为 pressed 形态.
注意这里的文件命名.

创建⼀个复选框

编辑复选框的样式
QCheckBox{
font-size:20px;
}
QCheckBox::indicator {
width: 20px;
height: 20px;
}
QCheckBox::indicator:unchecked {
image: url(:/unchecked.png);
}
QCheckBox::indicator:unchecked:hover {
image: url(:/unchecked-hover.png);
}
QCheckBox::indicator:unchecked:pressed {
image: url(:/unchecked-pressed.png);
}
QCheckBox::indicator:checked {
image: url(:/checked.png);
}
QCheckBox::indicator:checked:hover {
image: url(:/checked-hover.png);
}
QCheckBox::indicator:checked:pressed {
image: url(:/checked-pressed.png);
}

运行程序, 可以看到此时的复选框就变的丰富起来了.



|-------------|---------------------------------------------------|
| 要点 | 说明 |
| ::indicator | ⼦控件选择器.选中 checkbox 中的对钩部分. |
| :hover | 伪类选择器.选中⿏标移动上去的状态. |
| :pressed | 伪类选择器. 选中⿏标按下的状态. |
| :checked | 伪类选择器.选中 checkbox 被选中的状态. |
| :unchecked | 伪类选择器.选中 checkbox 未被选中的状态. |
| width | 设置⼦控件宽度.对于普通控件⽆效 (普通控件使⽤ geometry ⽅式设定尺⼨) |
| height | 设置⼦控件⾼度.对于普通控件⽆效 (普通控件使⽤ geometry ⽅式设定尺⼨). |
| image | 设置⼦控件的图片.像 QSpinBox, QComboBox 等可以使⽤这个属性来设置⼦控件的图⽚ |
1.6.3:单选框
代码示例:自定义单选框
创建 resource.qrc 文件, 并导入以下图片.

在界面上创建两个单选按钮.

- 在 Qt Designer 中编写样式.
- 此处为了让所有 QRadioButton 都能生效, 把样式设置在 Widget 上了. 并且使⽤后代选择器选中了 QWidget ⾥⾯的 QRadioButton .
PS:
QSS 中有些属性, 子元素能继承父元素(例如 font-size, color 等). 但是也有很多属性是不能继 承的.具体哪些能继承哪些不能继承, 规则⽐较复杂, 咱们不去具体研究. 实践中我们编写更精准的选择器是上策.

运行程序, 观察效果





|------------|-----------------------------------------------------|
| 要点 | 说明 |
| :indicator | ⼦控件选择器. 选中 radioButton 中的对钩部分 |
| :hover | 伪类选择器. 选中⿏标移动上去的状态. |
| :pressed | 伪类选择器. 选中⿏标按下的状态. |
| :checked | 伪类选择器. 选中 radioButton 被选中的状态. |
| :unchecked | 伪类选择器. 选中 radioButton 未被选中的状态 |
| width | 设置⼦控件宽度. 对于普通控件⽆效 (普通控件使⽤ geometry方式设定尺⼨) |
| height | 设置⼦控件⾼度. 对于普通控件⽆效 (普通控件使⽤ geometry方式设定尺⼨) |
| image | 设置子控件的图片. 像 QSpinBox, QComboBox 等可以使⽤这个属性来设置⼦控件的图片. |
1.6.4:输入框
代码示例(自定义单行编辑框)
在界面上创建⼀个单行编辑框.

在 Qt Designer 中编写样式.

cpp
QLineEdit {
border-width: 1px;
border-radius: 10px;
border-color: rgb(58, 58, 58);
border-style: inset;
padding: 0 8px;
color: rgb(255, 255, 255);
background:rgb(100, 100, 100);
selection-background-color: rgb(170, 170, 255);
selection-color: rgb(0, 255, 255);
}
执行程序观察效果.

|----------------------------|--------------|
| 属性 | 说明 |
| border-width | 设置边框宽度. |
| border-radius | 设置边框圆⻆. |
| border-color | 设置边框颜色. |
| border-style | 设置边框风格. |
| padding | 设置内边距. |
| color | 设置文字颜色. |
| background | 设置背景颜⾊. |
| selection-background-color | 设置选中文字的背景颜⾊. |
| selection-color | 设置选中文字的⽂本颜⾊ |
1.6.5:列表
代码示例(自定义列表框)
在界面上创建⼀个 ListWidget

添加样式

css
QListWidget::item:hover {
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
stop: 0 #FAFBFE, stop: 1 #DCDEF1);
}
QListWidget::item:selected {
border: 1px solid #6a6ea9;
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
stop: 0 #6a6ea9, stop: 1 #888dd9);
}
执行程序,观察结果

|-----------------|------------------------|
| 要点 | 说明 |
| ::item | 选中 QListWidget 中的具体条目. |
| :hover | 选中鼠标悬停的条目. |
| :selected | 选中某个被选中的条目. |
| background | 设置背景颜色. |
| border | 设置边框. |
| qlineargradient | 设置渐变⾊. |
1.6.6:渐变色
关于 qlineargradient,qlineargradient 有 6 个参数.
- x1, y1: 标注了⼀个起点.
- x2, y2: 标注了⼀个终点.
这两个点描述了⼀个 "方向".例如:
- x1: 0, y1: 0, x2: 0, y2: 1 就是垂直方向从上向下进行颜色渐变.
- x1: 0, y1: 0, x2: 1, y2: 0 就是水平方向从左向右进行颜色渐变.
- x1: 0, y1: 0, x2: 1, y2: 1 就是从左上往右下方向进行颜色渐变.
- stop0 和 stop1 描述了两个颜色. 渐变过程就是从 stop0 往 stop1 进行渐变的.
代码示例
界面不创建任何控件
编写样式
css
QWidget {
background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop: 0 #fff,
stop: 1 #000);
}
当前按照 垂直从上往下 从 白色 过渡到 黑色.

执行效果

修改代码

css
QWidget {
background-color: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop: 0 #fff, stop: 1 #000);
}
1.6.7:菜单栏
创建QMainWindow项目并且创建菜单栏

编写样式
css
QMenuBar
{
background-color : qlineargradient(x1 : 0, y1 : 0, x2 : 0, y2 : 1,
stop : 0 lightgray, stop : 1 darkgray);
spacing:3px; /* spacing between menu bar items */
}
QMenuBar::item
{
padding:1px 4px;
background:transparent;
border - radius : 4px;
}
QMenuBar::item : selected
{ /* when selected using mouse or keyboard */
background-color: rgb(0, 0, 255);
}
QMenuBar::item : pressed{
background-color: rgb(85, 255, 255);
}
QMenu
{
background-color : rgb(255,255,255);
margin:0 2px; /* some spacing around the menu */
}
QMenu::item
{
padding:2px 25px 2px 20px;
border:3px solid transparent; /* reserve space for selection border */
}
QMenu::item : selected
{
border - color : darkblue;
background:rgba(100, 100, 100, 150);
}
QMenu::separator
{
height:2px;
background:lightblue;
margin - left : 10px;
margin - right : 5px;
}
执⾏程序, 观察效果

|-------------------------|-----------------|
| QMenuBar::item | 选中菜单栏中的元素. |
| QMenuBar::item:selected | 选中菜单来中的被选中的元素. |
| QMenuBar::item:pressed | 选中菜单栏中的⿏标点击的元素. |
| QMenu::item | 选中菜单中的元素 |
| QMenu::item:selected | 选中菜单中的被选中的元素 |
| QMenu::separator | 选中菜单中的分割线 |
1.6.8:登录界面
基于上述学习过的 QSS 样式, 制作⼀个美化版本的登录界面
1.在界面上创建元素

使用布局管理器, 把上述元素包裹⼀下.
- 使用QVBoxLayout 来管理上述控件.
- 两个输入框和按钮的 minimumHeight 均设置为 50. (元素在布局管理器中无法直接设置 width 和height, 使用 minimumWidth 和 minimumHeight 代替, 此时垂直方向的 sizePolicy 要设为 fixed).
- 右键 QCheckBox , 选择 Layout Alignment 可以设置 checkbox 的对齐方式(左对齐, 居中对齐, 右对齐).

设置背景图片.
把上述控件添加⼀个父元素 QFrame, 并设置 QFrame和窗口一样大


创建resource.qrc, 并导入图片

编写 QSS 样式.
使用border-image 设置背景图片, 而不是 background-image . 主要是因为border- image 是可以自动缩放的. 这⼀点在窗口大小发生改变时是非常有意义的.
css
QFrame {
border-image: url(:/cat.jpg);
}
此时效果如下

设置输入框样式.
css
QLineEdit {
color: #8d98a1;
background-color: #405361;
padding: 0 5px;
font-size: 20px;
border-style: none;
border-radius: 10px;
}
运行程序效果:

设置 checkbox 样式
- 背景色transparent表示完全透明(应用父元素的背景).
css
QCheckBox {
color: white;
background-color: transparent;
}
执行效果

设置按钮样式
css
QPushButton {
font-size: 20px;
color: white;
background-color: #555;
border-style: outset;
border-radius: 10px;
}
QPushButton:pressed {
color: black;
background-color: #ced1db;
border-style: inset;
}

最终完整样式代码. 这些代码设置到 QFrame 的属性中即可.
css
QFrame {
border-image: url(":/cat.png");
}
QLineEdit {
color: #8d98a1;
background-color: #405361;
padding: 0 5px;
font-size: 20px;
border-style: none;
border-radius: 10px;
}
QCheckBox {
color: white;
background-color: transparent;
}
QPushButton {
font-size: 20px;
color: white;
background-color: #555;
border-style: outset;
border-radius: 10px;
}
QPushButton:pressed {
color: black;
background-color: #ced1db;
border-style: inset;
}
2:绘图
2.1:基本概念
- 虽然 Qt 已经内置了很多的控件, 但是不能保证现有控件就可以应对所有场景.很多时候我们需要更强的"自定制"能力. Qt 提供了画图相关的 API, 可以允许我们在窗口上绘制任意的图形形状, 来完成更复杂的界面设计.
- 所谓的 "控件" , 本质上也是通过画图的方式画上去的. 画图 API 和 控件 之间的关系, 可以类比成机器指令和高级语言之间的关系. 控件是对画图 API 的进⼀步封装; 画图 API 是控件的底层实现.
绘图API核心类
|--------------|-------------------------------------------------------------------------------------------------|
| 类 | 说明 |
| QPainter | "绘画者" 或者 "画家".用来绘图的对象, 提供了⼀系列 drawXXX ⽅法, 可以允许我们绘制各种图形. |
| QPaintDevice | "画板".描述了 QPainter 把图形画到哪个对象上. 像咱们之前⽤过的 QWidget 也是⼀种 QPaintDevice (QWidget 是 QPaintDevice 的子类) . |
| QPen | "画笔". 描述了 QPainter 画出来的线是什么样的. |
| QBrush | "画刷". 描述了 QPainter 填充⼀个区域是什么样的. |
PS:绘图 API 的使用,一般不会在 QWidget 的构造函数中使用,而是要放到 paintEvent 事件中.
paintEvent 会在以下情况下被触发:
- 控件首次创建.
- 控件被遮挡, 再解除遮挡.
- 窗口最小化, 再恢复.窗口最小化, 再恢复.
- 控件大小发生变化时.
- 主动调用repaint() 或者 update()方法. (这两个方法都是 QWidget 的方法).
因此, 如果把绘图 api 放到构造函数中调用, 那么一旦出现上述的情况, 界面的绘制效果就⽆法确保符合预期了.
2.2:绘制各种形状
2.2.1:代码示例一(画直线)
cpp
void drawLine(const QPoint &p1, const QPoint &p2);
参数:
p1:绘制起点坐标
p2:绘制终点坐标
1、在 "widget.h" 头文件中声明绘图事件.
widget.h
cpp
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
//声明绘画事件
void paintEvent(QPaintEvent * event);
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
2、在 "widget.cpp" 文件中重写 paintEvent() 方法;
widget.cpp
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QPainter>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::paintEvent(QPaintEvent *event)
{
//实例化画家对象, this:表示的是在当前窗口中绘画,即绘画设备不是指父对象
QPainter painter(this);
//画一条线
painter.drawLine(QPoint(20,20),QPoint(200,20));
}
实现效果如下:

2.2.2:代码示例二(画直线)
cpp
void drawLine ( int x1, int y1, int x2, int y2 );
参数:
x1,y1:绘制起点坐标
2,y2:绘制终点坐标
widget.cpp
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QPainter>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::paintEvent(QPaintEvent *event)
{
//实例化画家对象, this:表示的是在当前窗口中绘画,即绘画设备不是指父对象
QPainter painter(this);
//画一条线
painter.drawLine(QPoint(20,20),QPoint(200,20));
//再画一条直线
painter.drawLine(20,100,200,100);
}

2.2.3:代码示例三(绘制矩形)
cpp
void QPainter::drawRect(int x, int y, int width, int height);
参数:
x:窗⼝横坐标;
y:窗⼝纵坐标;
width:所绘制矩形的宽度;
height:所绘制矩形的⾼度;
1、在 "widget.h" 头文件中声明绘图事件.
widget.h
cpp
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
//声明绘画事件
void paintEvent(QPaintEvent * event);
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
2、在 "widget.cpp" 文件中重写 paintEvent() 方法;
widget.cpp
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QPainter>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::paintEvent(QPaintEvent *event)
{
//实例化画家对象, this:表示的是在当前窗口中绘画,即绘画设备不是指父对象
QPainter painter(this);
//绘制矩形
painter.drawRect(20,20,100,50);
}

2.2.4:代码示例四(绘制圆形)
cpp
void QPainter::drawEllipse(const QPoint ¢er, int rx, int ry)
参数:
center:中心点坐标
rx:横坐标
ry:纵坐标
widget.h
1、在 "widget.h" 头文件中声明绘图事件.
cpp
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
void paintEvent(QPaintEvent *);
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
widget.cpp
2、在 "widget.cpp" 文件中重写 paintEvent() 方法;
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QPainter>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::paintEvent(QPaintEvent *)
{
//实例化画家,this:表示的是在当前窗口绘画,即绘图设备
QPainter painter(this);
//绘制圆
painter.drawEllipse(QPoint(100,100),50,50);
}

2.2.5:代码示例五(绘制文本)
QPainter类中不仅提供了绘制图形的功能,还可以使用QPainter::drawText() 函数来绘制文字,也可以使用QPainter::setFont() 设置字体等信息。
1、在 "widget.h" 头文件中声明绘图事件.
cpp
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
void paintEvent(QPaintEvent*);
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
2、在 "widget.cpp" 文件中重写 paintEvent() 方法;
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QPainter>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::paintEvent(QPaintEvent *)
{
//实例化画家,this:表示的是在当前窗口绘画,即绘图设备
QPainter painter(this);
//绘制圆
painter.drawEllipse(QPoint(100,100),50,50);
}

2.2.6:设置画笔
- QPainter 在绘制时,是有⼀个默认的画笔的。在使⽤时也可以⾃定义画笔。在 Qt 中,QPen类中 定义了 QPainter 应该如何绘制形状、线条和轮廓。同时通过 QPen类 可以设置画笔的线宽、颜色、样式、画刷 等。
- 画笔的颜色可以在实例化画笔对象时进行设置,画笔的宽度是通过 setWidth() 方法进行设置,画笔的风格是通过setStyle()方法进行设置,设置画刷主要是通过 setBrush()方法。
- 设置画笔颜色:QPen::QPen(const QColor & color ).
- 设置画笔宽度:void QPen::setWidth(int width ).
- 设置画笔风格:void QPen::setStyle(Qt::PenStyle style ).
画笔的风格:

代码示例
1、在 "widget.h" 头文件中声明绘图事件.
cpp
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
void paintEvent(QPaintEvent*);
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
2、在 "widget.cpp" 文件中重写 paintEvent() 方法.
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QPainter>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::paintEvent(QPaintEvent *)
{
//实例化画家对象, this:表示的是在当前窗口中绘画,即绘画设备不是指父对象
QPainter painter(this);
//设置画笔
QPen pen(QColor(255,0,0));
//设置画笔宽度
pen.setWidth(3);
//设置画笔风格
pen.setStyle(Qt::DashLine);
//让画家使用画笔
painter.setPen(pen);
//绘制圆
painter.drawEllipse(QPoint(100,100),50,50);
}

2.2.7:设置画刷
在 Qt 中,画刷是使用QBrush类 来描述,画刷大多用于填充。QBrush定义了QPainter的填充模式,具有样式、颜色、渐变以及纹理等属性。
画刷的格式中定义了填充的样式,使用 Qt::BrushStyle 枚举,默认值是 Qt::NoBrush,也就是不进行任何填充。可以通过 Qt 助手查找画刷的格式。如下图示:

- 设置画刷主要通过 void QPen::setBrush(const QBrush &brush) 方法,其参数为画刷的格式。
代码示例
widget.h
cpp
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
void paintEvent(QPaintEvent*);
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
widget.cpp
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QPainter>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::paintEvent(QPaintEvent *)
{
//实例化画家对象 this:表示的是在当前窗口中绘画,即绘画设备
QPainter painter(this);
//设置画笔
QPen pen(QColor(255,0,0));
//设置画笔宽度
pen.setWidth(3);
//让画家使用画笔
painter.setPen(pen);
//设置画刷 给封闭的图形填充颜色
QBrush brush(Qt::cyan);
//设置画刷风格
brush.setStyle(Qt::Dense1Pattern);
//让画家使用画刷
painter.setBrush(brush);
//绘制圆
painter.drawEllipse(QPoint(100,100),50,50);
}

2.3:绘制图片
Qt 提供了四个类来处理图像数据:QImage、QPixmap、QBitmap 和 QPicture,它们都是常用的绘图设备。其中QImage主要用来进行 I/O 处理,它对 I/O 处理操作进行了优化,而且可以用来直接访问和操作像素;QPixmap 主要用来在屏幕上显示图像,它对在屏幕上显示图像进行了优化;QBitmap是QPixmap 的子类,用来处理颜色深度为1的图像,即只能显示黑白两种颜色;QPicture用来记录并重演 QPainter 命令。这里博主只讲解 QPixmap.
2.3.1:绘制简单图片
在 "widget.h" 头文件中声明绘图事件.
widget.h
cpp
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
//声明绘画事件
void paintEvent(QPaintEvent * event);
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
添加资源文件.

widget.cpp
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QPainter>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::paintEvent(QPaintEvent *event)
{
//实例化画家对象
QPainter painter(this);
//画图片
painter.drawPixmap(0,0,QPixmap(":/Down.png"));
}

2.3.2:平移图片
平移图片实际是通过改变坐标来实现。QPainter类中提供了 translate()函数 来实现坐标原点的改变。
widget.h
cpp
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
void paintEvent(QPaintEvent *);
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
widget.cpp
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QPainter>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::paintEvent(QPaintEvent *)
{
//实例化画家对象
QPainter painter(this);
painter.translate(100,100);
//画图片
painter.drawPixmap(0,0,QPixmap(":/Down.png"));
}

2.3.3:缩放图片
在 Qt 中,图片的放大和缩小可以使用 QPainter类 中的 drawPixmap()函数 来实现。
widget.h
cpp
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
void paintEvent(QPaintEvent*);
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
widget.cpp
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QPainter>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::paintEvent(QPaintEvent *)
{
//实例化画家对象
QPainter painter(this);
//以(0,0)点开始画图,图片的尺寸跟原图保持一致
painter.drawPixmap(0,0,QPixmap(":/Down.png"));
//以(300,400)点开始画图,图片尺寸:50 * 60
painter.drawPixmap(300,400,50,60,QPixmap(":/Down.png"));
}
实现效果如下:

2.3.4:旋转图片
图片的旋转使⽤的是 QPainter类中的 rotate()函数,它默认是以原点为中心进行旋转的。如果要改变旋转的中心,可以使用 translate()函数完成。
widget.h
cpp
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
void paintEvent(QPaintEvent*);
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
widget.cpp
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QPainter>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::paintEvent(QPaintEvent *)
{
//实例化画家对象
QPainter painter(this);
//让图片的中心作为旋转的中心
painter.translate(200,300);
//顺时针旋转90°
painter.rotate(90);
//使原点复原
painter.translate(-200,-300);
painter.drawPixmap(0,0,QPixmap(":/Up.png"));
}

2.4:其他设置
2.4.1:移动画家位置
有时候在绘制多个图形时,想使用同⼀坐标位置,那么绘制出来的图形肯定会重合,此时,可以通过移动画家的位置来使图形不发生重合。
代码示例1(未移动画家位置)
widget.h
cpp
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
void paintEvent(QPaintEvent*);
~Widget();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
widgte.cpp
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QPainter>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
void Widget::paintEvent(QPaintEvent *)
{
//实例化画家对象
QPainter painter(this);
//画圆
painter.drawEllipse(QPoint(100,200),50,50);
//画圆
painter.drawEllipse(QPoint(100,200),50,50);
}
Widget::~Widget()
{
delete ui;
}

实现效果如下.

代码示例2(移动画家位置)
widget.h
cpp
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
void paintEvent(QPaintEvent*);
~Widget();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
widget.cpp
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QPainter>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
void Widget::paintEvent(QPaintEvent *)
{
//实例化画家对象
QPainter painter(this);
//画圆
painter.drawEllipse(QPoint(100,200),50,50);
//让画家移动
painter.translate(200,0);
//画圆
painter.drawEllipse(QPoint(100,200),50,50);
}
Widget::~Widget()
{
delete ui;
}
实现效果如下

2.4.2:保存/加载画家的状态
在绘制图形的过程中,可以通过 save() 函数来保存画家的状态,使用restore()函数还原画家状态.
save() 函数原型如下:
cpp
void QPainter::save();

restore() 函数原型如下:
cpp
void QPainter::restore();

代码示例一
widget.h
cpp
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
void paintEvent(QPaintEvent*);
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
widgte.cpp
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QPainter>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.drawEllipse(QPoint(100,200),50,50);
painter.translate(200,0);
//保存画家状态
painter.save();
painter.drawEllipse(QPoint(100,200),50,50);
painter.translate(200,0);
//还原画家状态
painter.restore();
painter.drawEllipse(QPoint(100,200),50,50);
}

上述示例中,在画第三个圆之前,由于还原了画家的状态,所以此时画家的位置坐标会移动到画家状态保存的地方,所以在绘制第三个圆的位置时实际是和第⼆个圆发生了重叠。
2.5:特殊的绘图设备
前图的代码中我们是使用QWidget 作为绘图设备. 在 Qt 中还存在下列三个比较特殊的绘图设备. 此处我们也简要介绍.
- QPixmap 用于在显示器上显示图片.
- QImage 用于对图片进行像素级修改.
- QPicture 用于对 QPainter 的⼀系列操作进行存档.
2.5.1:QPixmap
QPixmap 核心特性:
- 使用QPainter 直接在上面进行绘制图形.
- 通过文件路径加载并显示图片.
- 搭配 QPainter 的 drawPixmap()函数, 可以把这个图片绘制到⼀个 QLabel、QPushButton 等控件上.
- 和系统/显示设备强相关, 不同系统/显示设备下, QPixmap 的显示可能会有所差别.
widget.h
cpp
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
widget.cpp
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QPixmap>
#include <QPainter>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QPixmap pix(500,500);
//实例化画家对象
QPainter painter(&pix);
//设置画笔颜色
painter.setPen(Qt::red);
//画圆
painter.drawEllipse(QPoint(100,100),100,100);
//保存绘制的图片
pix.save("D:/桌面/image/Test.png");
}
Widget::~Widget()
{
delete ui;
}
实现效果:


2.5.2:QImage
QImage 的核心特性:
- 使用QPainter 直接在上面进行绘制图形.
- 通过文件路径加载并显示图片.
- 能够针对图片进⾏像素级别的操作(操作某个指定的像素).
- 独立于硬件的绘制系统, 能够在不同系统之上提供一致的显示.
widget.h
cpp
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
widget.cpp
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QPainter>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QImage img(500,500,QImage::Format_RGB32);
//填充色为白色,默认背景色为黑色
img.fill(Qt::white);
//声明画家,画图设备为img
QPainter painter(&img);
//设置画笔颜色为蓝绿色(青色)
painter.setPen(QPen(Qt::cyan));
//画图
painter.drawEllipse(QPoint(200,200),100,100);
//保存画好的图片
img.save("D:\\桌面\\image:img.jpg");
}
Widget::~Widget()
{
delete ui;
}

2.5.3:QPicture
QPicture 核心特性:
- 使用QPainter 直接在上⾯进行绘制图形.
- 通过文件路径加载并显示图片.
- 能够记录 QPainter 的操作步骤.
- 独立于硬件的绘制系统, 能够在不同系统之上提供⼀致的显示.
注意:
QPicture加载的必须是自身的存档文件, 而不能是任意的 png, jpg 等图片文件.
- 如果要记录下 QPainter 的命令,首先要使用 QPainter::begin() 函数,将 QPicture 实例作为参数传递进去,以便告诉系统开始记录,记录完毕后使用 QPainter::end() 命令终止。如下示例.
widget.h
cpp
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
void paintEvent(QPaintEvent*);
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
widget.cpp
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QPainter>
#include <QPicture>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QPicture pic;
QPainter painter;
painter.begin(&pic);
painter.setPen(QPen(Qt::red));
painter.drawEllipse(QPoint(200,200),100,100);
painter.end();
pic.save("D:\\桌面\\image\\pic.pic");
}
Widget::~Widget()
{
delete ui;
}
void Widget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
//重现绘图指令
QPicture pic;
//加载图片
pic.load("D:\\桌面\\image\\pic.pic");
painter.drawPicture(0,0,pic);
}

通过 QPicture 重现绘图指令后,实现的效果如下:

