当前位置: 首页 > article >正文

[Qt] QSS | Qt Designer | 选择器

目录

一、CSS

1、背景介绍

2、基本语法

3、QSS 设置方式

(1)指定控件样式设置

(2)全局样式设置

(3)从文件加载样式表

(4)使用 Qt Designer 编辑样式

4、选择器

(1)选择器概况

(2)子控件选择器(Sub-Controls)

(3)伪类选择器(Pseudo-States)


一、CSS

1、背景介绍

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

  • 所谓样式,包括不限于大小、位置、颜色、背景、间距、字体等等。
  • 现在的网页很难找到没有 CSS 的,可以说让 “界面好看” 是一个刚需。
  • 对于针对特定专业领域用户的软件产品,界面设计是否重要?
  • 可参考:(11 封私信 / 80 条消息) 对于针对特定专业领域用户的软件产品,界面设计是否重要? - 知乎 (zhihu.com)

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

  • Qt 仿照 CSS 的模式,引入了 QSS,来对 Qt 中的控件做出样式上的设定,从而允许我们写出界面更好看的代码。
  • 同样受到 HTML 的影响,Qt 还引入了 QML 来描述界面,甚至还可以直接把一个原生的 html 页面加载到界面上。
  • 当然,由于 Qt 本身的设计理念和网页前端还是存在一定差异的,因此 QSS 中只能支持部分 CSS 属性。整体来说 QSS 要比 CSS 更简单一些。

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


2、基本语法

对于 CSS 来说,基本的语法结构非常简单。

QSS 沿用了其设定:

选择器 {
    属性名: 属性值; 
}

其中:

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

例如:

QPushButton { color: red; }
//或者:
QPushButton {
    color: red;
}

上述代码的含义表示,针对界面上所有的 QPushButton,都把文本颜色设置为红色。

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


【QSS 基本使用】

(1)在界面上创建一个按钮

(2)编写代码,设置样式

在.cpp 文件中设置:

ui->setupUi(this);
ui->pushButton->setStyleSheet("QPushButton {color:green;}");

(3)运行程序

观察效果,可以看到文本已经是绿色了:

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

其它格式:


3、QSS 设置方式

(1)指定控件样式设置

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

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

【子元素受到影响】

A. 在界面上创建两个按钮和一个单行编辑框

B. 修改 widget.cpp

这次不再给按钮设置样式,而是给 Widget 设置样式(Widget 是 QPushButton 的父控件):

C. 运行程序

可以看到样式对于 this 的子控件按钮同样会生效,但是必须是和选择器相关的。


(2)全局样式设置

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

全局样式优点:

  • 使同一个样式针对多个控件生效,代码更简洁。
  • 把界面上所有控件样式内聚在⼀起,便于维护和问题排查。

【使用全局样式】

A. 在界面上创建三个按钮

B. 编辑 main.cpp,设置全局样式

C. 运行程序

可以看到此时三个按钮的颜色都设置为红色了:

【样式的层叠特性】

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

A. 在界面上创建三个按钮

B. 编写 main.cpp,设置全局样式,把按钮文本设置为红色

C. 编写 widget.cpp,给第一个按钮设置字体大小

ui->pushButton->setStyleSheet("QPushButton{font-size:50px;}");

D. 运行程序

可以看到,对于第一个按钮来说,同时具备了颜色和字体大小样式,而第二个按钮只有颜色样式。

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

形如上述这种属性叠加的效果被称为 “层叠性”。

CSS 全称为 Cascading Style Sheets,其中 Cascading 就是 “层叠性” 的意思,QSS 也继承了这样的设定。实际上把 QSS 叫做 QCSS 也许更合适一些。


【样式的优先级】

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

A. 在界面上创建三个按钮

B. 编辑 main.cpp,把全局样式设置为红色

C. 编辑 widget.cpp,把第二个按钮样式设为绿色

ui->pushButton_2->setStyleSheet("QPushButton{color:green;}");

D. 运行程序

观察效果,可以看到第二个按钮已经成为绿色了,但是第一个按钮和第三个按钮仍然是红色。

在 CSS 中也存在类似的优先级规则。通常来说都是 “局部” 优先级高于 “全局” 优先级,相当于全局样式先 “奠定基调”,再通过指定控件样式来 “特事特办”。

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

(3)从文件加载样式表

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

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

【从文件加载全局样式】

A. 在界面上创建一个按钮

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

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

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

D. 使用 Qt Creator 打开 style.qss,编写内容

E. 修改 main.cpp,新增一个函数用来加载样式

#include <QFile>

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

F. 修改 main.cpp,在 main 函数中调用上述函数,并设置样式

G. 运行程序

可以看到样式已经生效了:

理论上来说 Qt 应该要提供直接从文件加载样式表的接口。

类似于 setStyleSheetFromFile(const QString& path) 这种,在内部把读文件操作封好。


(4)使用 Qt Designer 编辑样式

QSS 也可以通过 Qt Designer 直接编辑,从而起到实时预览的效果

同时也能把 C++ 和 QSS 代码的解耦合。

【使用 Qt Designer 编辑样式】

A. 在界面上创建一个按钮

B. 选择最外层的窗口,右键按钮,选择 “改变样式表”

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

  • 这里进行的修改都会记录到 ui 文件中,并且在程序运行时自动生效,还能进行实时预览。

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

E. 运行程序

可以看到样式确实发生了改变

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

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

由于设置样式太灵活,有很多地方都能设置,所以当我们发现一个控件的样式不符合预期的时候,要记得排查这四个地方:

  • 全局样式(QAppplication 设置的)
  • 指定控件样式(这个控件是否设置了样式)
  • qss 文件中的样式
  • ui 文件中的样式
  • 指定控件的父控件的样式(可能是从父控件继承过来的)

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


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 选择器基本一致。


【使用类型选择器选中子类控件】

A. 在界面上创建一个按钮

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

a.setStyleSheet("QWidget{color:red;}");
  • 注意 :此处选择器使用的是 QWidget。QPushButton 也是 QWidget 的子类,所以会受到 QWidget 选择器的影响。

C. 运行程序

  • 可以看到按钮的文本颜色已经是红色了

D. 如果把上述样式代码修改为下列代码

a.setStyleSheet(".QWidget{color:red;}");
  • 此时按钮的颜色不会发送改变。此时只是选择 QWidget 类,而不会选择 QWidget 的子类 QPushButton 了。

【使用 id 选择器】

A. 在界面上创建 3 个按钮,objectName 为 pushButton、pushButton_2、pushButton_3

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

  • 先通过 QPushButton 设置所有的按钮为红色。
  • 再通过 #pushButton 和 #pushButton_2 分别设置这两个按钮为绿色和黄色。
QString style = "QPushButton { color: red; }";
style += "#pushButton_2 { color: green; }";
style += "#pushButton_3 { color: yellow; }";

a.setStyleSheet(style);

C. 执行效果

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

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

这里的规则计算起来非常复杂(CSS 中也存在类似的设定)。可以简单的认为,选择器描述的范围越精准,则优先级越高。一般来说,ID 选择器优先级是最高的。

如果属性不冲突,还是会同时生效


【使用并集选择器】

A. 创建三个按钮、一个 label、一个单行输入框

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

QString style="QPushButton,QLineEdit,QLabel{color:red;}";

a.setStyleSheet(style);

C. 运行程序

可以看到这三种控件的文字颜色都设置为了红色:

  • 并集选择器是一种很好的代码复用的方式,很多时候我们希望界面上的多个元素风格是统一的,就可以使用并集选择器,把样式属性同时指定给多种控件。

也可以指定 id 选择器:

展示效果:

(2)子控件选择器(Sub-Controls)

有些控件内部包含了多个 “子控件”,比如 QComboBox 的下拉后的面板,比如 QSpinBox 的上下按钮等。

可以通过子控件选择器 ::,针对上述子控件进行样式设置。

哪些控件拥有哪些子控件,参考文档 Qt Style Sheets Reference 中 List of Sub-Controls 章节。


【设置下拉框的下拉按钮样式】

A. 在界面上创建一个下拉框,并创建几个选项

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

C. 修改 main.cpp,编写全局样式

  • 使用子控件选择器 QComboBox::down-arrow 选中了 QComboBox 的下拉按钮。
  • 再通过 image 属性设置图片。
QString style="QComboBox::down-arrow{ image:url(:/downPull.png)}";

a.setStyleSheet(style);

D. 执行程序


【修改进度条的颜色】

A. 在界面上创建一个进度条

B. 在 Qt Designer 右侧的属性编辑器中,找到 QWidget 的 styleSheet 属性

编辑如下内容:

  • 其中的 chunk 是选中进度条中的每个 “块”,使用 QProgressBar::text 则可以选中文本。
  • 同时把 QProcessBar 的 alignment 属性设置为垂直水平居中。

  • 此处如果不设置 alignment,进度条中的数字会跑到左上角(这个怀疑是 Qt 本身的 bug,暂时只能先使⽤ alignment 来手动调整一下)。

C. 执行程序

可以看到如下效果,就得到了一个红色的进度条:

通过上述方式,也可以修改文字的颜色,字体大小等样式。


(3)伪类选择器(Pseudo-States)

伪类选择器,是根据控件所处的某个状态被选择的。

例如按钮被按下,输入框获取到焦点,鼠标移动到某个控件上等。

  • 状态具备时,控件被选中,样式生效。
  • 当状态不具备时,控件不被选中,样式失效。

使用 : 的方式定义伪类选择器。

常用的伪类选择器:

伪类选择器

说明

:hover

鼠标放到控件上

:pressed

鼠标左键按下时

:focus

获取输入焦点时

:enabled

元素处于可用状态时

:checked

被勾选时

:read-only

元素为只读状态时

这些状态可以使用 ! 来取反,比如 :!hover 就是鼠标离开控件时,:!pressed 就是鼠标松开时,等等。更多伪类选择器的详细情况可以参考 Qt Style Sheets Reference 的 Pseudo-States 章节。


【设置按钮的伪类样式】

A. 在界面上创建一个按钮

B. 编写 main.cpp,创建全局样式

 QString style = "QPushButton { color: red; }";
    style += "QPushButton:hover { color: green; }";
    style += "QPushButton:pressed { color: blue; }";

    a.setStyleSheet(style);

C. 运行程序

可以看到默认情况下按钮文字是红色,鼠标移动上去是绿色,鼠标按下按钮是蓝色:

上述代码也可以使用事件的方式来实现。


【使用事件方式实现同样效果】

A. 创建 MyPushButton 类,继承自 QPushButton

B. 把生成代码中的构造函数 改成带参数 QWidget* 版本的构造函数(否则无法和 Qt Designer 生成的代码适配)

// mypushbutton.h
#include <QPushButton>
 
class MyPushButton : public QPushButton
{
public:
    MyPushButton(QWidget* parent);
};
 
 
 
// mypushbutton.cpp
#include "mypushbutton.h"
MyPushButton::MyPushButton(QWidget* parent) : QPushButton(parent)
{
 
}

C. 在界面上创建按钮,并提升为 MyPushButton 类型

  • 右键按钮,选择 “提升为...”
  • 填写提升的类名和头文件:

提升完毕后,在右侧对象树这里,就可以看到类型的变化。

D. 重写 MyPushButton 的四个事件处理函数

a. 修改 mypushbutton.h

class MyPushButton : public QPushButton
{
public:
    MyPushButton(QWidget* parent);
 
    void mousePressEvent(QMouseEvent* e);
    void mouseReleaseEvent(QMouseEvent* e);
    void enterEvent(QEvent* e);
    void leaveEvent(QEvent* e);
};

b. 修改 mypushbutton.cpp

  • 初始化设为红色
  • 鼠标进⼊时设为绿色,离开是还原红色
  • 鼠标按下时设为蓝色,松开时还原绿色(松开时鼠标还是在按钮里)
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; }");
}

E. 运行程序

可以看到效果和上述案例一致

很明显,实现同样的功能,伪类选择器要比事件的方式简单很多。

但是不能就说事件机制就不好,事件可以完成的功能很多,不仅仅是样式的改变,还可以包含其他业务逻辑,这一点是伪类选择器无法替代的。


http://www.kler.cn/a/506340.html

相关文章:

  • 《鸿蒙Next微内核:解锁人工智能决策树并行计算的加速密码》
  • 【Excel】【VBA】双列排序:坐标从Y从大到小排列之后相同Y坐标的行再对X从小到大排列
  • Linux浅谈——管道、网络配置和客户端软件的使用
  • arcgis中生成格网矢量带高度
  • Google常用语法解析
  • Docker
  • Linux/MacOS中如何远程调试C/C++程序
  • 无公网IP 实现外网访问本地 Docker 部署 Navidrome
  • NanoKVM简单开箱测评和拆解,让普通电脑实现BMC/IPMI远程管理功能
  • Redis基础3-主从复制
  • 得物App利用技术赋能,打造潮流消费“新玩法”
  • 全面了解 Web 前端技术:从基础到实践
  • JavaScript中的`void 0`:一个古老而安全的`undefined`获取方式
  • mac m4 安装 node
  • ResNet (Residual Network) - 残差网络:深度卷积神经网络的突破
  • Three.js+Vue3+Vite物体位移、缩放与旋转(二)
  • 【MySQL】使用C语言链接
  • R语言基础| 方差分析
  • 浅谈云计算11 | 虚拟机的主要功能
  • (RAG系列)FastGPT批量添加索引
  • WXML模版语法-事件绑定
  • 前端常见的设计模式之【单例模式】
  • 【机器学习:二十三、迁移学习】
  • 冯·诺依曼体系结构:计算机科学的奠基石
  • linux、华为modelarts、昇腾服务器、docker中,服务进程还在,但是不在运行状态,没有响应
  • 133、sqlserver查看哪个表被锁表了以及解锁方法