Qt 界面美化实战:QSS 样式表

📑 目录

  • [📌 1. 引言](#📌 1. 引言)
  • [🎨 2. QSS 详解](#🎨 2. QSS 详解)
    • [2.1 背景介绍](#2.1 背景介绍)
    • [2.2 基本语法](#2.2 基本语法)
    • [2.3 QSS 设置方式](#2.3 QSS 设置方式)
      • [2.3.1 指定控件样式设置](#2.3.1 指定控件样式设置)
      • [2.3.2 全局样式设置](#2.3.2 全局样式设置)
      • [2.3.3 从文件加载样式表](#2.3.3 从文件加载样式表)
      • [2.3.4 使用 Qt Designer 编辑样式](#2.3.4 使用 Qt Designer 编辑样式)
    • [2.4 选择器](#2.4 选择器)
      • [2.4.1 选择器概况](#2.4.1 选择器概况)
      • [2.4.2 子控件选择器 (Sub-Controls)](#2.4.2 子控件选择器 (Sub-Controls))
      • [2.4.3 伪类选择器 (Pseudo-States)](#2.4.3 伪类选择器 (Pseudo-States))
    • [2.5 样式属性](#2.5 样式属性)
      • [2.5.1 盒模型 (Box Model)](#2.5.1 盒模型 (Box Model))
    • [2.6 控件样式示例](#2.6 控件样式示例)
      • [2.6.1 按钮](#2.6.1 按钮)
      • [2.6.2 复选框](#2.6.2 复选框)
      • [2.6.4 输入框](#2.6.4 输入框)
      • [2.6.5 列表](#2.6.5 列表)
      • [2.6.6 菜单栏](#2.6.6 菜单栏)
  • [🎯 3. 总结与经典面试题](#🎯 3. 总结与经典面试题)
    • [3.1 本章总结](#3.1 本章总结)
    • [3.2 经典面试题](#3.2 经典面试题)

📌 1. 引言

在 Qt 开发中,界面优化是提升用户体验的关键环节。本章将围绕两大核心主题展开:

  • QSS(Qt Style Sheets):仿照 CSS 的模式,为 Qt 控件提供样式设定能力,让界面更美观。
  • 绘图(QPainter):提供底层绘图 API,允许我们在窗口上绘制任意图形,实现更复杂的界面设计。

🎯 学习目标:掌握 QSS 的基本语法、设置方式、选择器与常用控件样式;理解 QPainter 绘图的核心概念与常见图形绘制方法,为后续开发美观、专业的 Qt 应用打下基础。


🎨 2. QSS 详解

2.1 背景介绍

在网页前端开发领域中,CSS 是一个至关重要的部分,它描述了一个网页的"样式",从而起到对网页美化的作用。

所谓样式,包括但不限于大小、位置、颜色、背景、间距、字体等等。

现在的网页很难找到没有 CSS 的,可以说让"界面好看"是一个刚需。

网页开发作为 GUI 的典型代表,也对于其他客户端 GUI 开发产生了影响,Qt 也是其中之一 。

Qt 仿照 CSS 的模式,引入了 QSS ,来对 Qt 中的控件做出样式上的设定,从而允许程序猿写出界面更好看的代码。

同样受到 HTML 的影响,Qt 还引入了 QML 来描述界面,甚至还可以直接把一个原生的 html 页面加载到界面上。这部分内容课堂上并不讨论。

当然,由于 Qt 本身的设计理念和网页前端还是存在一定差异的,因此 QSS 中只能支持部分 CSS 属性。整体来说 QSS 要比 CSS 更简单一些。

🌟 注意:

如果通过 QSS 设置的样式和通过 C++ 代码设置的样式冲突,则 QSS 优先级更高。


2.2 基本语法

CSS的基本的语法结构比较简单:

css 复制代码
选择器 {
    属性名: 属性值; 
}

QSS 沿用了这样的设定:

css 复制代码
选择器 {
    属性名: 属性值; 
}

其中:

  • 选择器 描述了"哪个 widget 要应用样式规则"。
  • 属性 则是一个键值对,属性名表示要设置哪种样式,属性值表示了设置的样式的值。

例如:

css 复制代码
QPushButton { color: red; }

或者:

css 复制代码
QPushButton {
    color: red;
}

上述代码的含义表示,针对界面上所有的 QPushButton,都把文本颜色设置为 红色,此处的QPushButton是类型选择器。

编写 QSS 时使用单行的格式和多行的格式均可。

📝 代码示例:QSS 基本使用
  1. 在界面上创建一个按钮。
  2. 编写代码,设置样式:
cpp 复制代码
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    ui->pushButton->setStyleSheet("QPushButton { color: blue; }");
}
  1. 运行程序,观察效果。可以看到文本已经是蓝色了。

⚠️ 注意: 上述代码中,我们是只针对这一个按钮通过 setStyleSheet 方法设置的样式。此时这个样式仅针对该按钮生效。如果创建其他按钮,其他按钮不会受到影响。


2.3 QSS 设置方式

2.3.1 指定控件样式设置

QWidget 中包含了 setStyleSheet 方法,可以直接设置样式。

另一方面,给指定控件设置样式之后,该控件的子元素也会受到影响。

📝 代码示例:子元素受到影响
  1. 在界面上创建一个按钮。
  2. 修改 widget.cpp,这次我们不再给按钮设置样式,而是给 Widget 设置样式(Widget 是 QPushButton 的父控件):
cpp 复制代码
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    // 给 Widget 本身设置样式
    this->setStyleSheet("QPushButton { color: red;} ");
}
  1. 运行程序,可以看到样式对于子控件按钮同样会生效。
2.3.2 全局样式设置

还可以通过 QApplication 的 setStyleSheet 方法设置整个程序的全局样式。

全局样式优点:

  • 使同一个样式针对多个控件生效,代码更简洁。
  • 所有控件样式内聚在一起,便于维护和问题排查。
📝 代码示例:使用全局样式
  1. 在界面上创建三个按钮。
  2. 编辑 main.cpp,设置全局样式:
cpp 复制代码
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    // 设置全局样式
    a.setStyleSheet("QPushButton { color: green; }");
    Widget w;
    w.show();
    return a.exec();
}
  1. 运行程序,可以看到此时三个按钮的颜色都设置为绿色了。

💡 a.setStyleSheet("QPushButton { color: green; }");既对全部的子控件有效,而且必须是相关的控件,比如 QPushButton 控件。

📝 代码示例:样式的层叠特性

如果通过全局样式给某个控件设置了属性1,通过指定控件样式给控件设置属性2,那么这两个属性都会产生作用。

  1. 在界面上创建两个按钮。

❓ 思考:

  1. 如果设置了全局样式,然后在某个控件中又设置了其他的样式,会咋样?

    • 此时这两方面的样式会"叠加"起来 ------ 层叠性。
  2. 如果设置了全局样式,在某个控件中设置的样式和全局样式冲突了,又会咋样?

    • 局部更加优先 ------ 即局部样式的优先级更高,覆盖了对应的全局样式。

💡 实际开发过程中,就可以在全局样式中设置比较通用的样式,统一整个程序的界面风格;如果需要对某个界面进行微调,可以使用局部样式来做出调整。

  1. 编写 main.cpp,设置全局样式,把按钮文本设置为粉色:
cpp 复制代码
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    // 设置全局样式
    a.setStyleSheet("QPushButton { color: pink; }");
    Widget w;
    w.show();
    return a.exec();
}
  1. 编写 widget.cpp,给第一个按钮设置字体大小:
cpp 复制代码
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    // 设置指定控件样式
    ui->pushButton->setStyleSheet("QPushButton { font-size: 60px} ");
}
  1. 运行程序,可以看到,对于第一个按钮来说,同时具备了颜色和字体大小样式。而第二和第三个按钮只有颜色样式。

说明针对第一个按钮,两种设置方式设置的样式,叠加起来了。

🎹 形如上述这种属性叠加的效果,我们称为 "层叠性"。

CSS 全称为 Cascading Style Sheets,其中 Cascading 就是"层叠性"的意思。QSS 也继承了这样的设定。

📝 代码示例:样式的优先级

如果全局样式,和指定控件样式冲突,则指定控件样式优先展示。

  1. 在界面上创建两个按钮。
  2. 编辑 main.cpp,把全局样式设置为红色:
cpp 复制代码
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    // 设置全局样式
    a.setStyleSheet("QPushButton { color: red; }");
    Widget w;
    w.show();
    return a.exec();
}
  1. 编辑 widget.cpp,把第一个按钮样式设为绿色:
cpp 复制代码
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    // 设置第一个按钮颜色为绿色
    ui->pushButton->setStyleSheet("QPushButton { color: green; }");
}
  1. 运行程序,观察效果。可以看到第二个按钮已经成为绿色了,但是第一个按钮仍然是红色。

🌅 在 CSS 中也存在类似的优先级规则。通常来说都是 "局部"优先级高于"全局"优先级。

相当于全局样式先"奠定基调",再通过指定控件样式来"特事特办"。

2.3.3 从文件加载样式表

上述代码都是把样式通过硬编码的方式设置的。这样使 QSS 代码和 C++ 代码耦合在一起了,并不方便代码的维护。

因此更好的做法是把样式放到单独的文件中,然后通过读取文件的方式来加载样式。

📝 代码示例:从文件加载全局样式

💡 方案思路:

  1. 创建 qrc 文件,通过 qrc 文件来管理样式文件。
  2. 创建单独的 qss 文件,把这样的文件放到 qrc 文件中。
  3. 编写 C++ 代码,读取 qss 文件中的内容,并设置样式。

这个方案仅供参考,实际开发中可能还有更加方便的做法。

  1. 在界面上创建一个按钮。
  1. 创建 resource.qrc 文件,并设定前缀为 /。

  2. 创建 style.qss 文件,并添加到 resource.qrc 中。

  • style.qss 是需要程序运行时加载的。为了规避绝对路径的问题,仍然使用 qrc 的方式来组织(即把资源文件内容打包到 cpp 代码中)。
  • Qt Creator 没有提供创建 qss 文件的选项。咱们直接右键 -> 新建文件 -> 手动设置文件扩展名为 qss 即可。
  1. 使用 Qt Creator 打开 style.qss,编写内容:
css 复制代码
QPushBUtton{
    color : red;
}
  1. 修改 main.cpp,新增一个函数用来加载样式:
cpp 复制代码
QString loadQSS() {
    QFile file(":/style.qss");
    // 打开文件
    file.open(QFile::ReadOnly);
    // 读取文件内容。虽然 readAll 返回的是 QByteArray, 但是 QString 提供了 QByteArray 版本的构造函数。
    QString style = file.readAll();
    // 关闭文件
    file.close();
    return style;
}
  1. 修改 main.cpp,在 main 函数中调用上述函数,并设置样式:
cpp 复制代码
#include "widget.h"

#include <QApplication>
#include<QFile>

QString loadQSS(){
    QFile file(":/style.qss");

    //读取文件内容,虽然readAll返回的是QByteArray,但是QString提供了QByteArray版本
    //构造函数
    QString style = file.readAll();
    //关闭文件
    file.close();
    return style;
}


int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    //调用loadQSS函数加载上述样式
    const QString& style= loadQSS();
    a.setStyleSheet(style);

    Widget w;
    w.show();
    return a.exec();
}
  1. 运行程序,可以查看结果。
2.3.4 使用 Qt Designer 编辑样式

QSS 也可以通过 Qt Designer 直接编辑,从而起到实时预览的效果。同时也能避免 C++ 和 QSS 代码的耦合。

💡 更加方便的方式。

📝 代码示例:使用 Qt Designer 编辑样式
  1. 在界面上创建一个按钮。

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

  1. 在弹出的样式表编辑器中,可以直接填写样式。填填写完毕, 点击 OK 即可。
  2. 此时 Qt Designer 的预览界面就会实时显示出样式的变化。

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

xml 复制代码
   <property name="styleSheet">
    <string notr="true">QPushButton{color:rgb(85, 255, 0);}</string>
   </property>

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

⚠️ 注意: 由于设置样式太灵活,又很多地方都能设置,就导致当某个控件不符合预期的时候,排查起来就比较麻烦了。

  1. 全局样式(QApplication 设置的)
  2. 指定控件的样式(这个控件是否设置了样式)
  3. 指定控件的父控件(可能从父控件继承过来)
  4. qss 文件中的样式
  5. ui 文件中的样式

💡 在实际开发过程中,如果需要设置样式,建议最好统一使用某一种方式来设置。


2.4 选择器

2.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 的控件。

总体来说,QSS 选择器的规则和 CSS 选择器基本一致。上述选择器中只熟练使用最常用的几个即可(上述加粗的),其他的了解即可!

📝 代码示例:使用类型选择器选中子类控件
  1. 在界面上创建一个按钮。
  2. 修改 main.cpp,设置全局样式。

⚠️ 注意: 此处选择器使用的是 QWidget。QPushButton 也是 QWidget 的子类,所以会受到 QWidget 选择器的影响。

cpp 复制代码
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    // 设置全局样式
    a.setStyleSheet("QWidget { color: red; }");
    Widget w;
    w.show();
    return a.exec();
}
  1. 运行程序,可以看到按钮的文本颜色已经是红色了。

💡 此时当类型选择器和 id 选择器选中同一个控件的时候,并且设置的样式是冲突的,此时,id 选择器的优先级更高。如果不冲突,这两种效果都会生效。

  1. 如果把上述样式代码修改为下列代码:
cpp 复制代码
a.setStyleSheet(".QWidget { color: red; }");

此时按钮的颜色不会发生改变。此时只是选择 QWidget 类,而不会选择 QWidget 的子类 QPushButton 了。

📝 代码示例:使用 id 选择器
  1. 在界面上创建 3 个按钮,objectName 为 pushButton、pushButton_2、pushButton_3。
  2. 编写 main.cpp,设置全局样式:
  • 先通过 QPushButton 设置所有的按钮为黄色。
  • 再通过 #pushButton 和 #pushButton_2 分别设置这两个按钮为红色和绿色。
cpp 复制代码
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    // 设置全局样式
    QString style = "";
    style += "QPushButton { color: red; }";
    style += "#pushButton { color: green; }";
    style += "#pushButton_2 { color: blue; }";
    a.setStyleSheet(style);
    Widget w;
    w.show();
    return a.exec();
}
  1. 执行程序,观察效果。

📍 当某个控件身上,通过类型选择器和 ID 选择器设置了冲突的样式时,ID 选择器样式优先级更高。

同理,如果是其他的多种选择器作用同一个控件时出现冲突的样式,也会涉及到优先级问题。Qt 文档上有具体的优先级规则介绍(参见 The Style Sheet Syntax 的 Conflict Resolution 章节)。

这里的规则计算起来非常复杂(CSS 中也存在类似的设定),咱们此处对于优先级不做进一步讨论。

实践中我们可以简单的认为,选择器描述的范围越精准,则优先级越高。一般来说,ID 选择器优先级是最高的。

📝 代码示例:使用并集选择器
  1. 创建按钮、label、单行输入框。

  2. 编写 main.cpp,设置全局样式:

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();
}
  1. 运⾏程序, 可以看到这三种控件的⽂字颜⾊都设置为了红⾊

⛱ 并集选择器是一种很好的代码复用的方式。 很多时候我们希望多个不同类型的控件共享同一种样式,此时就可以使用并集选择器,把多个选择器用逗号分隔写在一起,从而避免重复编写相同的样式规则,让代码更加简洁、易于维护。

例如上面的例子中,QPushButton、QLabel、QLineEdit 三种控件都统一设置为红色文字,只需要写一条规则即可,而不需要分别给每种控件单独设置样式。

1.4.2 ⼦控件选择器 (Sub-Controls)

有些控件内部包含了多个 "⼦控件" . ⽐如QComboBox的下拉后的⾯板, ⽐如QSpinBox 的上下按钮等.

可以通过⼦控件选择器::, 针对上述⼦控件进⾏样式设置.

📝 代码示例:设置下拉框的下拉按钮样式
  1. 在界⾯上创建⼀个下拉框, 并创建⼏个选项
  2. 创建 resource.qrc , 并导⼊图⽚ down.svg
  3. 修改 main.cpp, 编写全局样式
  • 使⽤⼦控件选择器 QComboBox::down-arrow 选中了QComboBox的下拉按钮.
  • 再通过image属性设置图⽚.
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.svg)}";
    a.setStyleSheet(style);
    Widget w;
    w.show();
    return a.exec();
}
  1. 执⾏程序, 观察效果
📝 代码示例: 修改进度条的颜⾊
  1. 在界⾯上创建⼀个进度条.
  2. 在 Qt Designer 右侧的属性编辑器中, 找到 QWidget 的 styleSheet 属性.
    编辑如下内容:
    • 其中的 chunk 是选中进度条中的每个 "块" . 使⽤ QProgressBar::text 则可以选中⽂本.

QProgressBar::chunk {background-color: #FF0000;}

同时把 QProcessBar 的 alignment 属性设置为垂直⽔平居中.

  1. 执⾏程序, 可以看到如下效果. 我们就得到了⼀个红⾊的进度条.

    通过上述⽅式, 也可以修改⽂字的颜⾊, 字体⼤⼩等样式
2.4.3 伪类选择器 (Pseudo-States)

伪类选择器, 是根据控件所处的某个状态被选择的. 例如按钮被按下, 输⼊框获取到焦点, ⿏标移动到某个控件上等.

• 当状态具备时, 控件被选中, 样式⽣效.

• 当状态不具备时, 控件不被选中, 样常用的伪类选择器:

常⽤的伪类选择器

伪类选择器 说明
:hover 鼠标放到控件上时
:pressed 鼠标左键按下时
:focus 获取输入焦点时
:enabled 元素处于可用状态时
:checked 被勾选时
:read-only 元素为只读状态时

这些状态可以使⽤ !来取反. ⽐如 :!hover 就是⿏标离开控件时, :!pressed 就是⿏标松开时,等等.

📝 代码示例: 设置按钮的伪类样式.
  1. 在界⾯上创建⼀个按钮
  2. 编写 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. 运⾏程序, 可以看到, 默认情况下按钮⽂字是红⾊, ⿏标移动上去是绿⾊, ⿏标按下按钮是蓝⾊.

    上述代码也可以使⽤事件的⽅式来实现.
📝 代码示例:使⽤事件⽅式实现同样效果
  1. 创建 MyPushButton 类, 继承⾃ QPushButton
  2. 把⽣成代码中的构造函数改成带参数 QWidget* 版本的构造函数. (否则⽆法和 Qt Designer ⽣成的代码适配).
cpp 复制代码
********************mypushbutton.h**********************
#ifndef MYPUSHBUTTON_H
#define MYPUSHBUTTON_H

#include<QPushButton>
class myPushButton : public QPushButton
{
public:
    myPushButton(QWidget* parent);
};

#endif // MYPUSHBUTTON_H

*******************mypushbutton.cpp********************
#include "mypushbutton.h"

myPushButton::myPushButton(QWidget* parent):QPushButton(parent)
{
}
  1. 在界⾯上创建按钮, 并提升为 MyPushButton 类型

    右键按钮, 选择 "提升为"
    填写提升的类名和头⽂件.

    提升完毕后, 在右侧对象树这⾥, 就可以看到类型的变化.
  2. 重写 MyPushButton 的四个事件处理函数
    mypushbutton.h
cpp 复制代码
********************mypushbutton.h**********************
#ifndef MYPUSHBUTTON_H
#define MYPUSHBUTTON_H

#include<QPushButton>
#include<QMouseEvent>
#include<QEvent>
class myPushButton : public QPushButton
{
public:
    myPushButton(QWidget* parent);

    void mousePressEvent(QMouseEvent* e);
    void mouseReleaseEvent(QMouseEvent* e);
    void enterEvent(QEvent* e);
    void leaveEvent(QEvent* e);
};

#endif // MYPUSHBUTTON_H

修改 mypushbutton.cpp

• 初始化设为红⾊

• ⿏标进⼊时设为绿⾊, 离开是还原红⾊.

• ⿏标按下时设为蓝⾊, 松开时还原绿⾊(松开时⿏标还是在按钮⾥)

cpp 复制代码
#include "mypushbutton.h"

//按钮原始是红色
myPushButton::myPushButton(QWidget* parent):QPushButton(parent)
{
    this->setStyleSheet("QPushButton {color : red;}");
}
//鼠标点击按钮区域,按钮字体变为蓝色
void myPushButton::mousePressEvent(QMouseEvent *e)
{
    this->setStyleSheet("QPushButton {color : blue;}");
}
//鼠标点击并释放按钮区域,按钮字体变为绿色
void myPushButton::mouseReleaseEvent(QMouseEvent *e)
{
    this->setStyleSheet("QPushButton {color : green;}");
}
//鼠标进入按钮区域,按钮字体变为绿色
void myPushButton::enterEvent(QEvent *e)
{
    this->setStyleSheet("QPushButton {color : green;}");
}
//鼠标离开按钮区域,按钮字体变为红色
void myPushButton::leaveEvent(QEvent *e)
{
    this->setStyleSheet("QPushButton {color : red;}");
}
  1. 运⾏程序, 可以看到效果和上述案例⼀致.

✍ 很明显, 实现同样的功能, 伪类选择器要⽐事件的⽅式简单很多.

但是不能就说事件机制就不好. 事件可以完成的功能很多, 不仅仅是样式的改变, 还可以包含其

他业务逻辑. 这⼀点是伪类选择器⽆法替代的.

🌲2.5 样式属性

QSS 中的样式属性⾮常多, 不需要都记住. 核⼼原则还是⽤到了就去查.

当我们在翻阅官方⽂档的时候会涉及到⼀个关键术语 "盒模型" (Box Model),下面简要的介绍一下"盒模型"。

2.5.1 盒模型 (Box Model)

在⽂档的 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。

margin:10px 四个方向都是10px的外边距

margin:10px 20px:上下是10px,左右是20px

margin:10px,20px,30px,40px 上右下左(顺时针)

padding也是可以拆成4个属性

📝 代码⽰例: 设置边框和内边距
  1. 在界⾯上创建⼀个 label

  2. 修改 main.cpp, 设置全局样式

    • border: 5px solid red相当于 border-style: solid; border-width: 5px; border-color: red; 三个属性的简写形式.

    • padding-left: 10px; 是给左侧设置内边距.

cpp 复制代码
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();
}
  1. 运⾏程序, 可以看到样式发⽣了变化
📝 代码⽰例: 设置外边距

为了⽅便确定控件位置, 演⽰外边距效果, 我们使⽤代码创建⼀个按钮.

  1. 修改 widget.cpp, 创建按钮, 并设置样式.
cpp 复制代码
Widget::Widget(QWidget *parent)
 : QWidget(parent)
 , ui(new Ui::Widget)
 {
 ui->setupUi(this);

 QPushButton* btn = new QPushButton(this);
 btn->setGeometry(0, 0, 100, 100);
 btn->setText("hello");
 btn->setStyleSheet("QPushButton { border: 5px solid red; margin: 20px; }");

 const QRect& rect = btn->geometry();
 qDebug() << rect;
 }
  1. 运⾏程序, 可以看到, 当前按钮的边框被外边距挤的缩⼩了. 但是获取到的按钮的 Geometry 是不变的.

2.6 控件样式⽰例

下⾯给出⼀些常⽤控件的样式⽰例.

2.6.1 按钮
📝 代码⽰例: ⾃定义按钮
  1. 界⾯上创建⼀个按钮
  2. 右键 -> 改变样式表, 使⽤ Qt Designer 设置样式
css 复制代码
QPushButton {
 font-size: 20px;
 border: 2px solid #8f8f91;
 border-radius: 15px;
 background-color: #dadbde;
}
QPushButton:pressed {
 background-color: #f6f7fa;
}
  1. 执⾏程序, 可以看到效果

    属性⼩结
QSS 属性 说明
font-size 设置文字大小。
border-radius 设置圆角矩形,数值设置的越大,角就越"圆"。
background-color 设置背景颜色。
2.6.2 复选框
📝 代码⽰例: ⾃定义复选框
  1. 创建⼀个 resource.qrc ⽂件,导入对应的图标.
    使⽤⿊⾊作为默认形态.
    使⽤蓝⾊作为 hover 形态.
    使⽤红⾊作为 pressed 形态.

注意这⾥的⽂件命名.

  1. 创建⼀个复选框
  2. 编辑复选框的样式
css 复制代码
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);
}
  1. 运⾏程序, 可以看到此时的复选框就比较丰富了.

要点小结

要点 说明
::indicator 子控件选择器,选中 checkbox 中的对钩部分。
:hover 伪类选择器,选中鼠标移动上去的状态。
:pressed 伪类选择器,选中鼠标按下的状态。
:checked 伪类选择器,选中 checkbox 被选中的状态。
:unchecked 伪类选择器,选中 checkbox 未被选中的状态。
width 设置子控件宽度。对于普通控件无效(普通控件使用 geometry 方式设定尺寸)。
height 设置子控件高度。对于普通控件无效(普通控件使用 geometry 方式设定尺寸)。
image 设置子控件的图片。像 QSpinBox、QComboBox 等可以使用这个属性来设置子控件的图片。

🐳 单选框设置如上,具体操作请自行验证。

2.6.4 输⼊框
📝 代码⽰例: ⾃定义单⾏编辑框
  1. 在界⾯上创建⼀个单⾏编辑框
  2. 在Qt Designer中编写样式.
css 复制代码
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(187, 187, 187);
 selection-color: rgb(60, 63, 65);
}

属性小结

QSS 属性 说明
border-width 设置边框宽度。
border-radius 设置边框圆角。
border-color 设置边框颜色。
border-style 设置边框风格。
padding 设置内边距。
color 设置文字颜色。
background 设置背景颜色。
selection-background-color 设置选中文字的背景颜色。
selection-color 设置选中文字的文本颜色。
2.6.5 列表
📝 代码⽰例: ⾃定义列表框
  1. 在界⾯上创建⼀个 ListView
  2. 编写代码
css 复制代码
QListView::item:hover {
 background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
 stop: 0 #FAFBFE, stop: 1 #DCDEF1);
}
QListView::item:selected {
 border: 1px solid #6a6ea9;
 background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
 stop: 0 #6a6ea9, stop: 1 #888dd9);
}
  1. 执⾏程序, 观察效果

    属性小结
要点 说明
::item 选中 QListView 中的具体条目。
:hover 选中鼠标悬停的条目。
:selected 选中某个被选中的条目。
background 设置背景颜色。
border 设置边框。
qlineargradient 设置渐变色。

✏️ 总结 :关于 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 进⾏渐变的.

这里的取值比较有限,要么是0,要么是1

代码例⼦: 理解渐变⾊
  1. 界⾯不创建任何控件
  2. 编写样式
css 复制代码
QWidget {
 background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop: 0 #fff, 
stop: 1 #000);
}

当前按照 垂直从上往下 从 ⽩⾊ 过渡到 ⿊⾊

效果如下:

  1. 修改代码
css 复制代码
![QWidget {
 background-color: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop: 0 #fff, 
stop: 1 #000);
 }](https://i-blog.csdnimg.cn/direct/35bcbaf4f2de41008157f6782de15a08.png)

当前按照 ⽔平从左往右 从 ⽩⾊ 过渡到 ⿊⾊

效果如下:

2.6.6 菜单栏
📝代码⽰例: ⾃定义菜单栏
  1. 创建菜单栏
cpp 复制代码
#include "widget.h"
#include "ui_widget.h"


Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    //创建菜单栏
    QMenuBar* menuBar = new QMenuBar(this);

    //创建菜单
    QMenu* menu1 = new QMenu("文件");
    QMenu* menu2 = new QMenu("工具");
    QMenu* menu3 = new QMenu("编辑");

    //将菜单加入到菜单栏中
    menuBar->addMenu(menu1);
    menuBar->addSeparator();
    menuBar->addMenu(menu2);
    menuBar->addSeparator();
    menuBar->addMenu(menu3);

    //创建菜单项
    QAction* action1 = new QAction("打开");
    QAction* action2 = new QAction("关闭");
    QAction* action3 = new QAction("创建");
    QAction* action4 = new QAction("打开已有文件");

    //将菜单项加入到菜单中
    menu1->addAction(action1);
    menu1->addSeparator();
    menu1->addAction(action2);
    menu1->addSeparator();
    menu1->addAction(action3);
    menu1->addSeparator();
    menu1->addAction(action4);

}

Widget::~Widget()
{
    delete ui;
}
  1. 编写样式
css 复制代码
****************************style.qss************************************
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: #a8a8a8;
}

QMenuBar::item:pressed {
    background: #888888;
}

QMenu {
    background-color: white;
    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;
}

读取style.qss资源文件的代码如下:

cpp 复制代码
#include "widget.h"
#include <QApplication>
#include <QFile>
#include <QString>
#include <QDebug>

QString loadQSS()
{
    QFile file(":/style.qss");
    // 重点:以只读文本模式打开资源文件
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
    {
        qDebug() << "打开qss失败:" << file.errorString();
        return "";
    }
    QString style = file.readAll();
    file.close();
    return style;
}

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QString style = loadQSS();
    a.setStyleSheet(style);
    qDebug()<< style;

    Widget w;
    w.show();
    return a.exec();
}
  1. 执⾏程序, 观察效果

    要点小结
要点 说明
QMenuBar::item 选中菜单栏中的元素。
QMenuBar::item:selected 选中菜单栏中的被选中的元素。
QMenuBar::item:pressed 选中菜单栏中的鼠标点击的元素。
QMenu::item 选中菜单中的元素。
QMenu::item:selected 选中菜单中的被选中的元素。
QMenu::separator 选中菜单中的分割线。

🎯 3. 总结与经典面试题

3.1 本章总结

本章围绕 QSS(Qt Style Sheets) 展开,核心要点如下:

  • QSS 是什么 :Qt 仿照 CSS 引入的样式表语言,用于美化 Qt 控件,语法为 选择器 { 属性名: 属性值; }。
  • 设置方式 :可通过 QWidget::setStyleSheet 指定控件设置、QApplication::setStyleSheet 全局设置、从 qss 文件加载、或使用 Qt Designer 编辑。
  • 层叠性与优先级:全局样式与局部样式不冲突时叠加生效(层叠性);冲突时局部样式优先,ID 选择器优先级高于类型选择器。
  • 选择器 :类型、类、ID、后代、子、并集、属性、子控件(::)、伪类(:hover、:pressed 等)。
  • 盒模型:Content、Border、Padding、Margin 四部分,默认边距与边框宽度均为 0。
  • 常用控件样式 :按钮、复选框、输入框、列表、菜单栏等均可通过 QSS 定制,渐变可用 qlineargradient 实现。

💡 复习提示:QSS 属性繁多,核心原则是「用到了就去查」,重点掌握选择器与优先级规则即可。


3.2 经典面试题

面试题 1:QSS 和 CSS 有什么区别?

参考答案:

QSS(Qt Style Sheets)是 Qt 仿照 CSS 引入的样式表语言,语法结构基本一致(选择器 { 属性: 值; })。但 QSS 只支持部分 CSS 属性,整体比 CSS 更简单。QSS 用于美化 Qt 控件,而 CSS 用于美化网页元素。


面试题 2:QSS 有哪几种设置方式?分别有什么特点?

参考答案:

  1. 指定控件设置 :通过 widget->setStyleSheet() 设置,仅对该控件及其子元素生效。
  2. 全局设置 :通过 QApplication::setStyleSheet() 设置,对整个程序的所有相关控件生效,便于统一风格与维护。
  3. 从文件加载 :把样式写入独立的 .qss 文件,通过 QFile 读取并设置,避免 C++ 与 QSS 代码耦合。
  4. Qt Designer 编辑 :在 Designer 中直接编辑样式,可实时预览,样式会以 XML 形式记录到 .ui 文件中。

面试题 3:什么是 QSS 的「层叠性」?全局样式和局部样式冲突时谁优先?

参考答案:

层叠性(Cascading)指多个样式规则作用于同一控件时,不冲突的属性会叠加生效。例如全局样式设置了颜色,局部样式设置了字体大小,两者都会生效。当全局样式与局部样式冲突时,局部样式(指定控件样式)优先级更高,会覆盖全局样式。


面试题 4:QSS 支持哪些选择器?ID 选择器和类型选择器冲突时谁优先?

参考答案:

QSS 支持全局选择器(*)、类型选择器(QPushButton)、类选择器(.QPushButton)、ID 选择器(#objectName)、后代选择器、子选择器、并集选择器、属性选择器、子控件选择器(::)和伪类选择器(:)。

当 ID 选择器和类型选择器选中同一控件且样式冲突时,ID 选择器优先级更高。一般来说,选择器描述的范围越精准,优先级越高。


面试题 5:什么是子控件选择器?请举例说明。

参考答案:

子控件选择器使用 :: 语法,用于选中控件内部的子部件。例如 QComboBox::down-arrow 可以选中下拉框的下拉按钮,QCheckBox::indicator 可以选中复选框的对钩部分,QProgressBar::chunk 可以选中进度条的块。可以通过 image、width、height 等属性设置子控件的样式。


面试题 6:什么是伪类选择器?常用的有哪些?

参考答案:

伪类选择器根据控件所处的状态来选择控件,状态具备时样式生效。常用的伪类选择器包括:

伪类选择器 说明
:hover 鼠标放到控件上时
:pressed 鼠标左键按下时
:focus 获取输入焦点时
:enabled 元素处于可用状态时
:checked 被勾选时
:read-only 元素为只读状态时

这些状态还可以用 ! 取反,例如 :!hover 表示鼠标离开控件时。


面试题 7:请简述 QSS 的盒模型(Box Model)。

参考答案:

盒模型由四部分组成:

  • Content:存放控件内容(文本/图标)的矩形区域。
  • Border:控件的边框。
  • Padding:内边距,边框和内容之间的距离。
  • Margin :外边距,边框到控件 geometry 返回的矩形边界的距离。

默认情况下,外边距、内边距、边框宽度都是 0。可以通过 margin、padding、border-style、border-width、border-color 等属性设置。


面试题 8:如何实现按钮的悬停变色效果?伪类选择器和事件方式哪个更好?

参考答案:

伪类选择器方式:

css 复制代码
QPushButton { color: red; }
QPushButton:hover { color: green; }
QPushButton:pressed { color: blue; }

事件方式 :继承 QPushButton,重写 enterEvent、leaveEvent、mousePressEvent、mouseReleaseEvent,在事件中调用 setStyleSheet 改变颜色。

对比:实现同样的功能,伪类选择器更简单、代码更少。但事件机制不仅能改变样式,还能处理其他业务逻辑,这是伪类选择器无法替代的。


面试题 9:如何从文件加载 QSS 样式?为什么要使用 qrc 资源文件?

参考答案:

将样式写入独立的 .qss 文件,通过 QFile 读取文件内容并调用 QApplication::setStyleSheet 设置。示例:

cpp 复制代码
QString loadQSS() {
    QFile file(":/style.qss");
    file.open(QFile::ReadOnly);
    QString style = file.readAll();
    file.close();
    return style;
}

使用 qrc 资源文件是为了规避绝对路径问题,把资源文件内容打包到 cpp 代码中,程序运行时通过 :/ 前缀访问,便于部署和移植。


面试题 10:如何理解 qlineargradient 渐变?它的参数含义是什么?

参考答案:

qlineargradient 用于设置线性渐变,有 6 个参数:x1, y1 标注起点,x2, y2 标注终点,两点描述渐变方向;stop: 0 和 stop: 1 描述两个颜色,渐变过程就是从 stop: 0 的颜色过渡到 stop: 1 的颜色。

例如:

  • x1: 0, y1: 0, x2: 0, y2: 1:垂直方向从上向下渐变。
  • x1: 0, y1: 0, x2: 1, y2: 0:水平方向从左向右渐变。
  • x1: 0, y1: 0, x2: 1, y2: 1:从左上往右下渐变。
相关推荐
杨运交4 小时前
[076][核心模块]构建优雅的Java异常处理框架:从错误码到全局异常处理
java·开发语言
可乐鸡翅yeah_4 小时前
video.js 集成 hls.js 开发 M3U8 播放器,新手高频踩坑
开发语言·前端·javascript·后端·ecmascript·m3u8·音视频在线播放
小鹿的周先生4 小时前
第11章-Structured-Output
开发语言·人工智能·python
泡海椒4 小时前
JQuick-Excel 实战:FORMAT 管理日期与数字的 Excel 显示
开发语言·python·excel
qq_401700415 小时前
Qt 串口/网口通信:Hex 与 ASCII 编码转换深度指南
开发语言·数据库·qt
佳児素花痴╮5 小时前
线程知识点
java·开发语言·jvm
学掌门5 小时前
老司机带你十分钟入门Python!
开发语言·python
zhangzeyuaaa6 小时前
深入 Ruby:return 退出规则完全指南
开发语言·ruby
shaibdoio6 小时前
AI主动获客的意图识别工程实现:从规则匹配到模型微调
java·开发语言·人工智能·长沙瞬维ai