目录
1、背景介绍
2、基本语法
3、QSS 设置方式
3.1 指定控件样式设置
代码示例: 子元素受到影响
3.2 全局样式设置
代码示例: 使用全局样式
代码示例: 样式的层叠特性
代码示例: 样式的优先级
3.3 从文件加载样式表
代码示例: 从文件加载全局样式
3.4 使用 Qt Designer 编辑样式
代码示例: 使用 Qt Designer 编辑样式
4、选择器
4.1 选择器概况
代码示例: 使用类型选择器选中子类控件
代码示例: 使用 id 选择器
代码示例: 使用并集选择器
4.2 子控件选择器 (Sub-Controls)
代码示例: 设置下拉框的下拉按钮样式
4.3 伪类选择器 (Pseudo-States)
代码示例: 设置按钮的伪类样式.
1、背景介绍
在网页前端开发领域中, CSS 是一个至关重要的部分. 描述了一个网页的 "样式". 从而起到对网页美化的作用.
- 所谓样式, 包括不限于大小, 位置, 颜色, 背景, 间距, 字体等等.
现在的网页很难找到没有 CSS 的. 可以说让 "界面好看" 是⼀个刚需.
一个程序的界面是否好看,是否重要呢?
- 有些面向专业领域的程序,界面好看与否,不是很关键,更关键的是实际的效果。
- 有些面向普通用户领域的程序,界面好看还是很大的加分项。
网页开发作为 GUI 的典型代表, 也对于其他客户端 GUI 开发产生了影响. Qt 也是其中之一.
Qt 仿照 CSS 的模式, 引入了 QSS, 来对 Qt 中的控件做出样式上的设定, 从而允许程序猿写出界面更好看的代码.
- 同样受到 HTML 的影响, Qt 还引入了 QML 来描述界面, 甚至还可以直接把一个原生的 html 页面加载到界面上.
注意:
- 如果通过 QSS 设置的样式和通过 C++ 代码设置的样式冲突, 则 QSS 优先级更高.
2、基本语法
对于 CSS 来说, 基本的语法结构非常简单.
选择器 {
属性名: 属性值;
}
QSS 沿用了这样的设定.
选择器 {
属性名: 属性值;
}
其中:
- 选择器 描述了 "哪个 widget 要应用样式规则"。
- 属性 则是一个键值对, 属性名表示要设置哪种样式, 属性值表示了设置的样式的值。
例如:
QPushButton { color: red; }
或者:
QPushButton {
color: red;
}
上述代码的含义表示, 针对界面上所有的 QPushButton , 都把文本颜色设置为 红色 .
- 编写 QSS 时使用单行的格式和多行的格式均可.
代码示例: QSS 基本使用
1) 在界面上创建⼀个按钮.
2) 编写代码, 设置样式
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->pushButton->setStyleSheet("QPushButton { color : red;}");
}
3) 运行程序, 观察效果. 可以看到文本已经是红色了.
注意:
上述代码中, 我们是只针对这一个按钮通过 setStyleSheet 方法设置的样式. 此时这个样式仅针对该按钮生效. 如果创建其他按钮, 其他按钮不会受到影响.
3、QSS 设置方式
3.1 指定控件样式设置
QWidget 中包含了 setStyleSheet 方法, 可以直接设置样式.
上述代码我们已经演示了上述设置方式.
另一方面, 给指定控件设置样式之后, 该控件的子元素也会受到影响.
代码示例: 子元素受到影响
1) 在界面上创建一个按钮
2) 修改 widget.cpp, 这次我们不再给按钮设置样式, 而是给 Widget 设置样式 (Widget 是 QPushButton 的父控件).
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 给 Widget 本身设置样式
this->setStyleSheet("QPushButton { color : green;}");
}
3) 运行程序, 可以看到样式对于子控件按钮同样会生效.
3.2 全局样式设置
还可以通过 QApplication 的 setStyleSheet 方法设置整个程序的全局样式. 全局样式优点:
- 使同一个样式针对多个控件生效, 代码更简洁.
- 所有控件样式内聚在一起, 便于维护和问题排查.
代码示例: 使用全局样式
1) 在界面上创建三个按钮.
2) 编辑 main.cpp, 设置全局样式
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// 设置全局样式
a.setStyleSheet("QPushButton{ color : red; }");
Widget w;
w.show();
return a.exec();
}
3) 运行程序, 可以看到此时三个按钮的颜色都设置为红色了.
代码示例: 样式的层叠特性
- 如果通过全局样式给某个控件设置了属性1, 通过指定控件样式给控件设置属性2, 那么这两个属性都会产生作用.
1) 在界面上创建两个按钮
2) 编写 main.cpp, 设置全局样式, 把按钮文本设置为红色.
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// 设置全局样式
a.setStyleSheet("QPushButton{ color : red; }");
Widget w;
w.show();
return a.exec();
}
3) 编写 widget.cpp, 给第一个按钮设置字体大小.
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 设定指定控件样式
ui->pushButton->setStyleSheet("QPushButton{ font-size : 60px; }");
}
4) 运行程序, 可以看到, 对于第一个按钮来说, 同时具备了颜色和字体大小样式. 而第二个按钮只有颜色样式.
- 说明针对第一个按钮, 两种设置方式设置的样式, 叠加起来了.
形如上述这种属性叠加的效果, 我们称为 "层叠性".
- CSS 全称为 Cascading Style Sheets, 其中 Cascading 就是 "层叠性" 的意思. QSS 也继承了这样的设定.
实际上把 QSS 叫做 QCSS 也许更合适⼀些~
代码示例: 样式的优先级
- 如果全局样式, 和指定控件样式冲突, 则指定控件样式优先展示.
1) 在界面上创建两个按钮
2) 编辑 main.cpp, 把全局样式设置为红色.
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// 设置全局样式
a.setStyleSheet("QPushButton{ color : red; }");
Widget w;
w.show();
return a.exec();
}
3) 编辑 widget.cpp, 把第一个按钮样式设为绿色.
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 设定指定控件样式
ui->pushButton->setStyleSheet("QPushButton{ color : green; }");
}
4) 运行程序, 观察效果. 可以看到第一个按钮已经成为绿色了, 但是第二个按钮仍然是红色。
在 CSS 中也存在类似的优先级规则. 通常来说都是 "局部" 优先级高于 "全局" 优先级. 相当于全局样式先 "奠定基调" , 再通过指定控件样式来 "特事特办" .
3.3 从文件加载样式表
- 上述代码都是把样式通过硬编码的方式设置的. 这样使 QSS 代码和 C++ 代码耦合在一起了, 并不方便代码的维护.
- 因此更好的做法是把样式放到单独的文件中, 然后通过读取文件的方式来加载样式.
代码示例: 从文件加载全局样式
1) 在界面上创建一个按钮
2) 创建 resource.qrc 文件, 并设定前缀为 / .
3) 创建 style.qss 文件, 并添加到 resource.qrc 中.
- style.qss 是需要程序运行时加载的. 为了规避绝对路径的问题, 仍然使用 qrc 的方式来组织. (即把资源文件内容打包到 cpp 代码中).
- Qt Creator 没有提供创建 qss 文件的选项. 咱们直接 右键 -> 新建文件 -> 手动设置文件扩展名为 qss 即可.
4) 使用 Qt Creator 打开 style.qss , 编写内容
QPushButton {
color : red;
}
5) 修改 main.cpp, 新增一个函数用来加载样式
QString loadQss(){
QFile file(":/style.qss");
// 打开文件
file.open(QFile::ReadOnly);
// 读取文件内容,虽然 readAll 返回的是 QByteArray, 但是 QString 提供了QByteArray 版本的构造函数
QString style = file.readAll();
// 关闭文件
file.close();
return style;
}
6) 修改 main.cpp, 在 main 函数中调用上述函数, 并设置样式.
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// 调用上述函数加载样式
a.setStyleSheet(loadQss());
Widget w;
w.show();
return a.exec();
}
7) 运行程序, 可以看到样式已经生效了.
3.4 使用 Qt Designer 编辑样式
QSS 也可以通过 Qt Designer 直接编辑, 从而起到实时预览的效果. 同时也能避免 C++ 和 QSS 代码的耦合.
代码示例: 使用 Qt Designer 编辑样式
1) 在界面上创建⼀个按钮
2) 右键按钮, 选择 "改变样式表"
3) 在弹出的样式表编辑器中, 可以直接填写样式. 填写完毕, 点击 OK 即可.
4) 此时 Qt Designer 的预览界面就会实时显示出样式的变化.
5) 运行程序, 可以看到样式确实发生了改变.
这种方式设置样式, 样式内容会被以 xml 格式记录到 ui 文件中.
<property name="styleSheet">
<string notr="true">QPushButton { color : blue; }</string>
</property>
同时在控件的 styleSheet 属性中也会体现.
当我们发现一个控件的样式不符合预期的时候, 要记得排查这四个地方:
- 全局样式
- 指定控件样式
- qss 文件中的样式
- ui 文件中的样式
4、选择器
4.1 选择器概况
QSS 的选择器支持以下几种:
选择器 | 示例 | 说明 |
全局选择器 | * | 选择所有的 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 的控件. |
代码示例: 使用类型选择器选中子类控件
1) 在界面上创建一个按钮.
2) 修改 main.cpp, 设置全局样式
- 注意, 此处选择器使用的是 QWidget . QPushButton 也是 QWidget 的子类, 所以会受到 QWidget 选择器的影响.
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// 设置全局样式
a.setStyleSheet("QWidget { color : red; }");
Widget w;
w.show();
return a.exec();
}
3) 运行程序, 可以看到按钮的文本颜色已经是红色了.
5) 如果把上述样式代码修改为下列代码
// 设置全局样式
a.setStyleSheet(".QWidget { color : red; }");
此时按钮的颜色不会发生改变. 此时只是选择 QWidget 类, 而不会选择 QWidget 的子类 QPushButton 了.
代码示例: 使用 id 选择器
1) 在界面上创建 3 个按钮, objectName 为 pushButton , pushButton_2 , pushButton_3
2) 编写 main.cpp, 设置全局样式
- 先通过 QPushButton 设置所有的按钮为黄色.
- 再通过 #pushButton 和 #pushButton_2 分别设置这两个按钮为红色和绿色.
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// 设置全局样式
QString style = "";
style += "QPushButton { color : yellow; }";
style += "#pushButton_2 { color : red; }";
style += "#pushButton_3 { color : green; }";
a.setStyleSheet(style);
Widget w;
w.show();
return a.exec();
}
3) 执行程序, 观察效果
- 当某个控件身上, 通过类型选择器和 ID 选择器设置了冲突的样式时, ID 选择器样式优先级更高.
- 同理, 如果是其他的多种选择器作用同一个控件时出现冲突的样式, 也会涉及到优先级问题. Qt 文档上有具体的优先级规则介绍 (参见 The Style Sheet Syntax 的 Conflict Resolution 章节).
- 实践中我们可以简单的认为, 选择器描述的范围越精准, 则优先级越高. 一般来说, ID 选择器优先级是最高的.
代码示例: 使用并集选择器
1) 创建按钮, label, 单行输入框
2) 编写 main.cpp, 设置全局样式
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// 设置全局样式
a.setStyleSheet("QPushButton, QLabel, QLineEdit { color : red; }");
Widget w;
w.show();
return a.exec();
}
3) 运行程序, 可以看到这三种控件的文字颜色都设置为了红色
并集选择器是⼀种很好的代码复用的方式. 很多时候我们希望界面上的多个元素风格是统⼀的, 就可以使用并集选择器, 把样式属性同时指定给多种控件.
4.2 子控件选择器 (Sub-Controls)
有些控件内部包含了多个 "子控件" . 比如 QComboBox 的下拉后的面板, 比如 QSpinBox 的上下按钮等.
可以通过子控件选择器 :: , 针对上述子控件进行样式设置.
🌴哪些控件拥有哪些子控件, 参考文档 Qt Style Sheets Reference 中 List of Sub-Controls 章节.
代码示例: 设置下拉框的下拉按钮样式
1) 在界面上创建⼀个下拉框, 并创建几个选项
2) 创建 resource.qrc , 并导入图片 down.png
3) 修改 main.cpp, 编写全局样式
- 使用子控件选择器 QComboBox::down-arrow 选中了 QComboBox 的下拉按钮.
- 再通过 image 属性设置图片.
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();
}
4) 执行程序, 观察效果
4.3 伪类选择器 (Pseudo-States)
伪类选择器, 是根据控件所处的某个状态被选择的. 例如按钮被按下, 输入框获取到焦点, 鼠标移动到某个控件上等.
- 当状态具备时, 控件被选中, 样式生效.
- 当状态不具备时, 控件不被选中, 样式失效.
使用 : 的方式定义伪类选择器.
常用的伪类选择器:
伪类选择器 | 说明 |
:hover | 鼠标放到控件上 |
:pressed | 鼠标左键按下时 |
:focus | 获取输入焦点时 |
:enabled | 元素处于可用状态时 |
:checked | 被勾选时 |
:read-only | 元素为只读状态时 |
这些状态可以使用 ! 来取反. 比如 :!hover 就是鼠标离开控件时, :!pressed 就是鼠标松开时, 等等.
更多伪类选择器的详细情况, 参考 Qt Style Sheets Reference 的 Pseudo-States 章节.
代码示例: 设置按钮的伪类样式.
1) 在界面上创建⼀个按钮
2) 编写 main.cpp, 创建全局样式
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QString style = "QPushButton { color : red; }";
style += "QPushButton:hover { color : green; }";
style += "QPushButton:pressed { color : blue; }";
a.setStyleSheet(style);
Widget w;
w.show();
return a.exec();
}
3) 运行程序, 可以看到, 默认情况下按钮文字是红色, 鼠标移动上去是绿色, 鼠标按下按钮是蓝色.