目录
[1.1 基本语法](#1.1 基本语法)
[1.2 QSS设置方法](#1.2 QSS设置方法)
[1.2.1 指定控件样式设置](#1.2.1 指定控件样式设置)
[1.2.2 全局样式设置](#1.2.2 全局样式设置)
[1.2.3 从文件加载样式表](#1.2.3 从文件加载样式表)
[1.2.4 使用Qt Designer编辑样式](#1.2.4 使用Qt Designer编辑样式)
[1.3 选择器](#1.3 选择器)
[1.3.1 介绍](#1.3.1 介绍)
[1.3.2 子控件选择器](#1.3.2 子控件选择器)
[1.3.3 伪类选择器](#1.3.3 伪类选择器)
[1.4 样式属性(盒模型)](#1.4 样式属性(盒模型))
[1.5 代码示例(登录界面)](#1.5 代码示例(登录界面))
[2.1 基础概念](#2.1 基础概念)
[2.2 绘制各种形状](#2.2 绘制各种形状)
[2.3 设置画笔](#2.3 设置画笔)
[2.4 设置画刷](#2.4 设置画刷)
[2.5 绘制图片](#2.5 绘制图片)
[2.6 画家设置](#2.6 画家设置)
[2.6.1 移动画家设置](#2.6.1 移动画家设置)
[2.6.2 保存/加载画家的状态](#2.6.2 保存/加载画家的状态)
[2.7 特殊的绘图设备](#2.7 特殊的绘图设备)
[2.7.1 QPixmap](#2.7.1 QPixmap)
[2.7.2 QImage](#2.7.2 QImage)
[2.7.3 QPicture](#2.7.3 QPicture)
一、QSS
1.1 基本语法
css
选择器 {
属性名: 属性值;
}
- **选择器:**描述了"哪个widget要应用样式规则"
- **属性:**是一个键值对,属性名表示要设置哪种样式,属性值表示了设置的样式的值
css
QPushButton {
color: red;
}
**注意:**若通过 QSS 设置的样式和通过 C++ 代码设置的样式冲突,则QSS优先级更高
1.2 QSS设置方法
1.2.1 指定控件样式设置
QWidget 中包含了 setStyleSheet 方法,可以直接设置样式。给指定控件设置样式之后,该控件的子元素也会受到影响
**代码示例:**子控件受影响
在界面上创建一个按钮
修改widget.cpp,不给按钮设置样式,而是给 Widget 设置样式(Widget是QPushButton的父控件)
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;
}
运行程序,可以看到样式对于子控件按钮同样会生效
1.2.2 全局样式设置
可以通过 QApplication 的 setStyleSheet 方法设置整个程序的全局样式
全局样式优点:
- 使同一个样式针对多个控件生效,代码更简洁
- 所有控件样式内聚在一起,便于维护和问题排查
**代码示例:**使用全局样式
在界面上创建三个按钮
编辑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,通过指定控件样式给控件设置属性2,那么这两个属性都会产生作用
在上一个示例代码的基础上,编写widget.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:30px; }");
}
Widget::~Widget()
{
delete ui;
}
运行程序可以看到,对于第一个按钮而言,同时具备了颜色和字体大小样式,而第二、三个按钮只有颜色样式,说明针对第一个按钮,两种设置方式设置的样式叠加了
**代码示例:**样式的优先级
若全局样式和指定控件样式冲突,则指定控件样式优先展示
在上一个示例代码的基础上,编写widget.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:30px; }");
ui->pushButton_2->setStyleSheet("QPushButton { color:green }");
}
Widget::~Widget()
{
delete ui;
}
在CSS中也存在类似的优先级规则,通常而言都是"局部"优先级高于"全局"优先级,相当于全局样式先"奠定基调",再通过指定控件样式来"特事特办"
1.2.3 从文件加载样式表
上述代码都是把样式通过硬编码的方式设置的,这样使QSS代码和C++代码耦合在一起了,并不方便代码的维护。因此更好的做法是把样式放到单独的文件中,然后通过读取文件的方式来加载样式
**代码示例:**从文件加载全局样式
在界面上创建一个按钮
创建 resource.qrc 文集,并设定前缀为 /
创建 style.qss 文件,并添加到 resource.qrc 中
style.qss 是需要程序运行时加载的,为了规避绝对路径的问题,仍然使用 qrc 的方式来组织(即把资源文件内容打包到cpp代码中)。Qt Creator没有提供创建 qss 文件的选项,直接右键->新建文件->手动设置文件扩展名为qss即可
使用Qt Creator打开 style.qss 编写内容
css
QPushButton {
color: red;
}
修改main.cpp,新增一个函数加载样式,并在main函数中调用上述函数设置样式
cpp
#include "widget.h"
#include <QApplication>
#include <QFile>
QString loadQSS()
{
QFile file(":/style.qss");
file.open(QFile::ReadOnly);
QString style = file.readAll();
file.close();
return style;
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QString style = loadQSS();
a.setStyleSheet(style);
Widget w;
w.show();
return a.exec();
}
1.2.4 使用Qt Designer编辑样式
QSS也可以通过Qt Designer直接编辑,实时预览,同时也能免C++和QSS代码的耦合
**代码示例:**使用Qt Designer编辑样式
右键按钮,选择"改变样式表"
在弹出的样式表编辑器中,可以直接填写样式。填写完毕点击OK即可
此时Qt Designer的预览界面就会实时显示出样式的变化
这种方式设置样式,样式内容会被以xml格式记录到ui文件中
XML
<property name="styleSheet">
<string notr="true">QPushButton { color: red; }</string>
</property>
同时在控件的 styleSheet 属性中也会体现
1.3 选择器
1.3.1 介绍
选择器 | 示例 | 说明 |
---|---|---|
全局选择器 | * | 选择所有的widget |
类型选择器(type selector) | QPushButton | 选择所有的QPushButton和其子类的控件 |
类选择器 | .QPushButton | 选择所有的QPushButton,不会选择子类 |
ID选择器 | #pushButton_2 | 选择objectName为pushButton_2的控件 |
后代选择器 | QDialog QPushButton | 选择QDialog的所有后代中的QPushButton(子控件、孙子控件等) |
子选择器 | QDialog>QPushButton | 选择QDialog的所有子控件中的QPushButton |
并集选择器 | QPushButton,QLineEdit | 选择QPushButton、QLineEdit这两种控件 |
属性选择器 | QPushButton[flat="false"] | 选择所有QPushButton中,flat属性为false的控件 |
当某个控件,通过 类型选择器 和 ID选择器 设置了冲突的样式时,ID选择器样式优先级更高。同理,若是其他的多种选择器作用同一个控件时出现冲突的样式,也会涉及到优先级问题。Qt文档上有具体的优先级规则介绍(参见The Style Sheet Syntax的Conflict Resolution章节)
实践中可以简单的认为,选择器描述的范围越精准,则优先级越高。⼀般来说,ID选择器优先级是最高的
1.3.2 子控件选择器
有些控件内部包含了多个"子控件",如QComboBox的下拉后的面板,如QSpinBox的上下按钮等。可以通过子控件选择器::,针对上述子控件进行样式设置
**注意:**哪些控件拥有哪些子控件,参考文档Qt Style Sheets Reference中List of Sub-Controls章节
**代码示例:**设置下拉框的下拉按钮样式
在界面上创建一个下拉框并创建几个选项
创建 resource.qrc 并导入图片 down.png
使用子控件选择器 QComboBox::down-arrow 选中 QComboBox 的下拉按钮,再通过 image 属性设置图片
cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QString style = "QComboBox::down-arrow { image:url(:/down.png); }";
a.setStyleSheet(style);
Widget w;
w.show();
return a.exec();
}
1.3.3 伪类选择器
伪类选择器是根据控件所处的某个状态被选择的。如按钮被按下、输入框获取到焦点、鼠标移动到某个控件上等
- 当状态具备时,控件被选中,样式生效
- 当状态不具备时,控件不被选中,样式失效
- 使用:的方式定义伪类选择器
常用的伪类选择器:
伪类选择器 | 说明 |
---|---|
:hover | 鼠标放在控件上 |
:pressed | 鼠标左键按下时 |
:focus | 获取输入焦点时 |
:enabled | 元素处于可用状态时 |
:checked | 被勾选时 |
:read-only | 元素为只读状态时 |
这些状态可以使用 ! 来取反,如 :!hover 就是鼠标离开控件时,:!pressed 就是鼠标松开时,等等
更多伪类选择器的详细情况,参考Qt Style Sheets Reference 的 Pseudo-States 章节
1.4 样式属性(盒模型)
文档的 Qt Style Sheets Reference 章节详细介绍了哪些控件可以设置属性,每个控件都能设置哪些属性等
在文档的 Customizing Qt Widgets Using Style Sheets 的 The Box Model 章节介绍了盒模型
- Content矩形区域:存放控件内容,如包含的文本/图标等
- Border矩形区域:控件的边框
- Padding矩形区域:内边距,边框和内容之间的距离
- Margin矩形区域:外边距,边框到控件 geometry 返回的矩形边界的距离
默认情况下,外边距、内边距、边框宽度都是0,可以通过一些QSS属性来设置上述的边距和边框的样式
QSS属性 | 说明 |
---|---|
margin | 设置四个方向的外边距,复合属性 |
padding | 设置四个方向的内边距,复合属性 |
border-style | 设置边框样式 |
border-width | 边框粗细 |
border-color | 边框的颜色 |
border | 复合属性,相当于border-style + border-width + border-color |
1.5 代码示例(登录界面)
在界面上创建元素
使用布局管理器,把上述元素包裹
两个输入框和按钮的minimumHeight均设置为50(元素在布局管理器中无法直接设置width和height,使用minimumWidth和minimumHeight代替,此时垂直方向的sizePolicy要设为fixed)
右键 QCheckBox,选择 Layout Alignment 可以设置checkbox的对齐方式(左对齐、居中对齐、右对齐)
上述控件添加一个父元素 QFrame 并设置 QFrame 和 窗口一样大
**注意:**顶层窗口的 QWidget 无法设置背景图片,因此需要再套上一层 QFrame。背景图片就设置到 QFrame 上即可
创建 resource.qrc并导入图片
编写QSS样式,使用 border-image 设置背景图片,而不是 background-image。主要是因为 border-image 是可以自动缩放的,这一点在窗口大小发生改变时是非常有意义的
背景色使用 transparent 表示完全透明(应用父元素的背景)
代码设置到QFrame的属性中即可
css
QFrame {
border-image: url(:/background.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.1 基础概念
Qt提供了画图相关的API,可以允许在窗口上绘制任意的图形形状,来完成更复杂的界面设计
**注意:**所谓的"控件",本质上也是通过画图的方式画上去的。画图 API 和 控件 之间的关系,可以类比成机器指令和高级语言之间的关系。控件是对画图 API 的进一步封装,画图 API 是控件的底层实现
绘图 API 的使用,一般不会在 QWidget 的构造函数中使用,而是要放到 paintEvent 事件中
paintEvent 事件会在以下情况下被触发:
- 控件首次创建
- 控件被遮挡,再解除遮挡
- 窗口最小化,再恢复
- 控件大小发生变化时
- 主动调用 repaint() 或者 update() 方法(这两个方法都是 QWidget 的方法)
- ......
2.2 绘制各种形状
绘制线段
cpp
void drawLine(const QPoint &p1, const QPoint &p2);
//p1:绘制起点坐标
//p2:绘制终点坐标
void drawLine (int x1, int y1, int x2, int y2);
//x1,y1:绘制起点坐标
//x2,y2:绘制终点坐标
绘制矩形
cpp
void QPainter::drawRect(int x, int y, int width, int height);
//x:窗⼝横坐标
//y:窗⼝纵坐标
//width:所绘制矩形的宽度
//height:所绘制矩形的⾼度
绘制圆形
cpp
void QPainter::drawEllipse(const QPoint ¢er, int rx, int ry)
//center:中⼼点坐标
//rx:外接矩形横坐标
//ry:外接矩形纵坐标
绘制文本
2.3 设置画笔
QPainter在绘制时,是有一个默认的画笔的。在使用时也可以自定义画笔。在Qt中,QPen类中定义了 QPainter 应该如何绘制形状、线条和轮廓。同时通过 QPen类 可以设置画笔的线宽、颜色、样式等
- 设置画笔颜色:QPen::QPen(const QColor &color) 画笔的颜色主要是通过 QColor 类设置
- 设置画笔宽度:void QPen::setWidth(int width)
- 设置画笔风格:void QPen::setStyle(Qt::PenStyle style)
2.4 设置画刷
在 Qt 中,画刷是使用 QBrush类 来描述,画刷大多用于填充。QBrush定义了QPainter的填充模式,具有样式、颜色、渐变以及纹理等属性
画刷的格式中定义了填充的样式,使用 Qt::BrushStyle 枚举,默认值是 Qt::NoBrush,也就是不进行任何填充。可以通过 Qt 助手 查找画刷的格式
2.5 绘制图片
Qt提供了四个类来处理图像数据:QImage、QPixmap、QBitmap和QPicture,都是常用的绘图设备。其中 QImage 主要用来进行 I/O 处理,对 I/O 处理操作进行了优化,而且可以用来直接访问和操作像素;QPixmap主要用来在屏幕上显示图像,对在屏幕上显示图像进行了优化;QBitmap是QPixmap的子类,用来处理颜色深度为1的图像,即只能显示黑白两种颜色;QPicture用来记录并重演 QPainter 命令
绘制图片
平移图片
平移图片实际是通过改变坐标来实现。QPainter类提供了 translate()函数 来实现坐标原点的改变
缩放图片
在 Qt 中,图片的放大和缩小可以使用 QPainter类 中的drawPixmap()函数来实现
旋转图片
图片的旋转使用的是 QPainter类 中的 rotate()函数,默认是以原点为中心进行旋转的。若要改变旋转的中心,可以使用translate()函数完成
2.6 画家设置
2.6.1 移动画家设置
有时候在绘制多个图形时,使用同⼀坐标位置,那么绘制出来的图形肯定会重合。可以通过移动画家的位置来使图形不发生重合
2.6.2 保存/加载画家的状态
在绘制图形的过程中,可以通过 save() 函数来保存画家的状态,使用 restore() 函数还原画家状态
代码示例
上述示例中,在画第三个圆之前,由于还原了画家的状态,所以此时画家的位置坐标会移动到画家状态保存的地方,所以在绘制第三个圆的位置时实际是和第二个圆发生了重叠
2.7 特殊的绘图设备
前⾯的代码中是使用 QWidget 作为绘图设备,在 Qt 中还存在下列三个特殊的绘图设备
- QPixmap 用于在显示器上显示图片
- QImage 用于对图片进行像素级修改
- QPicture 用于对 QPainter 的一系列操作进行存档
2.7.1 QPixmap
QPixmap核心特性
- 使用 QPainter 直接在上面进行绘制图形
- 通过文件路径加载并显示图片
- 搭配 QPainter 的 drawPixmap()函数,可以把这个图片绘制到QLabel、QPushButton等控件上
- 和系统/显示设备强相关,不同系统/显示设备下,QPixmap 的显示可能会有所差别
2.7.2 QImage
QImage核心特性
- 使用 QPainter 直接在上面进行绘制图形
- 通过文件路径加载并显示图片
- 能够针对图片进行像素级别的操作(操作某个指定的像素)
- 独立于硬件的绘制系统,能够在不同系统之上提供一致的显示
2.7.3 QPicture
QPicture核心特性
- 使用 QPainter 直接在上面进行绘制图形
- 通过文件路径加载并显示图片
- 能够记录 QPainter 的操作步骤
- 独立于硬件的绘制系统,能够在不同系统之上提供一致的显示
**注意:**QPicture加载的必须是自身的存档文件,而不能是任意的png、jpg等图片文件
认识
QPicture类似于很多游戏的Replay功能。如war3经典游戏,即使是一场60分钟的长时间局,生成的replay文件,也不过几百个KB
此处的Replay功能并非是把整个游戏画面都录制保存下来,而是记录了地图中发生的所有事件(地图元素,玩家单位操作,中立生物行为等...)
当回放Replay时,其实就是把上述记录的事件再一条一条的执行一遍,即可还原之前的游戏场景
代码示例
若要记录下 QPainter 的命令,先要使用 QPainter::begin() 函数,将 QPicture 实例作为参数传递进去,以便告诉系统开始记录,记录完毕后使用 QPainter::end() 命令终止