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

【QT】QT界面的美容院 -- QSS

一、背景介绍

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

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

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

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

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

二、基本语法

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

QSS 沿用了其设定:

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

其中:

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

例如:

QPushButton { color: red; }
//或者:
QPushButton {color: red;
}
  • 上述代码的含义表示,针对界面上所有的 QPushButton,都把文本颜色设置为红色

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

【基本使用】

新建项目,以 Widget 作为基类,构造函数代码如下:

Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QGridLayout* layout = new QGridLayout();QPushButton* button1 = new QPushButton("红色");QPushButton* button2 = new QPushButton("无色");button1->setStyleSheet("QPushButton{color:red;}");layout->addWidget(button1, 0, 0);layout->addWidget(button2, 0, 1);this->setLayout(layout);
}

结果如下:

image-20250201160501449

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

三、基本语法

1. 指定控件样式设置

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

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

【子元素受到影响】

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

B. 修改 widget.cpp

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

image-20250201163646291

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

2. 全局样式设置

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

全局样式优点:

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

image-20250201180411861

3. 样式的层叠特性

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

比如:基于上面全局样式的设计,我们给 按钮1 设置字体大小,如下:

image-20250201180624931

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

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

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

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

注意:局部样式无法叠加,除非写到一行中,如下:

image-20250201181111911

4. 样式优先级

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

  • 这里就不做演示了

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

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

5. 从文件加载样式表

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

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

操作如下:

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

B. 创建 resource.qrc 文件(Qt -> Qt Resource File),并设定前缀为 /

img

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

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

image-20250202112632742

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

image-20250202112750370

新增一个函数来加载样式,如下:
image-20250202113026885

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

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

6. 使用 Qt Designer 编辑样式表

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

  • 同时也能避免 C++ 和 QSS 代码的 耦合

image-20250202113445075

如下:

image-20250202113543789

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

img

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

img

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

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

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

四、选择器

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

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

① 在界面上创建一个按钮,修改 main.cpp,设置全局样式

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

image-20250202134425319

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

② 但是 💢如果把上述样式代码修改为下列代码

a.setStyleSheet(".QWidget{color:red;}");
【使用 id 选择器】

在开发中,如果期望不同的控件样式不同,此时就需要使用到 ID 选择器了

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

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

  • 先通过 QPushButton 设置所有的按钮为红色。

  • 再通过 #pushButton#pushButton_2 分别设置这两个按钮为绿色和黄色。

image-20250202135015078

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

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

img

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

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

【使用并集选择器】

创建三个按钮、一个 label、一个单行输入框,编写 main.cpp,设置全局样式

QString style="QPushButton,QLineEdit,QLabel{color:red;}";a.setStyleSheet(style);

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

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

也可以指定 id 选择器:

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

image-20250202140539465

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

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

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

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

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

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

image-20250202141045752

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

image-20250202141150914

img

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

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

结果如下:

image-20250202141519724

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

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

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

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

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

常用的伪类选择器:

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

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

【设置按钮的伪类样式】

在界面上创建一个按钮,编写 main.cpp,创建 全局样式

QString style = "QPushButton { color: red; }";
style += "QPushButton:hover { color: green; }";
style += "QPushButton:pressed { color: blue; }";a.setStyleSheet(style);

运行程序,结果如下:

img

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

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

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

① 创建 MyPushButton 类,继承自 QPushButton

② 把生成代码中的构造函数 改成带参数 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)
{}

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

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

img

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

img

④ 重写 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; }");
}
  • 运行程序可以看到效果和上述案例一致

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

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

五、样式属性

QSS 中的样式属性非常多,大部分的属性和 CSS 是非常相似的。

  • 文档的 Qt Style Sheets Reference 章节详细介绍了哪些控件可以设置属性,每个控件都能设置哪些属性等。

image-20250202143545891

1. 盒模型(Box Model)

  • 这个词 主要来自于 CSS

在文档的 Customizing Qt Widgets Using Style SheetsThe Box Model 章节介绍了盒模型

img

⼀个遵守盒模型的控件,由上述几个部分构成

  • Content 矩形区域:存放控件内容,比如包含的文本 / 图标等。
  • Border 矩形区域:控件的边框。
  • Padding 矩形区域:内边距,边框和内容之间的距离。
  • Margin 矩形区域:外边距,边框到控件 geometry 返回的矩形边界的距离。
  • 默认情况下,外边距、内边距、边框宽度都是 0。

可以通过一些 QSS 属性来设置上述的边距和边框的样式:

QSS 属性说明
margin设置四个方向的外边距。复合属性。
padding设置四个方向的内边距。复合属性。
border-style设置边框样式
border-width边框的粗细
border-color边框的颜色

这里的复合属性:由多个属性构成,margin 可以拆成如下:

  • margin-left
  • margin-right
  • margin-top
  • margin-bottom

当然实际运用的时候,写 margin: 10px; ==> 表示四个方向都是 10px 的外边距,而写 margin: 10px 20px; 则是上下都是 10px, 左右是 20px, margin: 10px 20px 30px 40px ==> 上右下左(顺时针)

  • 同样 padding 也可以拆成 4 个属性的

【设置边框和内边距】

在界面上创建一个 label,修改 main.cpp, 设置全局样式

QString style = "QLabel{border: 20px solid green; padding-left: 50px}";
a.setStyleSheet(style);
  • border: 20px solid green 相当于 border-style: solid; border-width: 2px; border-color: green; 三个属性的简写形式。
  • padding-left: 50px; 是给左侧设置内边距。

结果如下:

image-20250202144713960

【设置外边距】

为了方便确定控件位置,演示外边距效果,使用代码创建⼀个按钮。修改 widget.cpp,创建按钮,并设置样式

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("按钮");button->setStyleSheet("QPushButton{border: 5px solid red; margin: 20px}");qDebug() << button->geometry();
}

运行程序如下:

image-20250202151310020

可以看到,当前按钮的边框被外边距挤的缩小了,但是获取到的按钮的 Geometry 是不变的。

2. 控件样式示例(Box Model)

① 自定义按钮

A. 界面上创建一个按钮

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

img

C. 执行程序,点击 “按钮”:

img

属性说明
font-size设置文字大小。
border-radius设置圆角矩形。 数值设置的越大,角就越圆。
background-color设置背景颜色。
② 自定义复选框

A. 创建一个 resource.qrc 文件,并导入以下图片

image-20250202152808327image-20250202152837718image-20250202152850132image-20250202152857961image-20250202152904725image-20250202152912954

  • 使用黑色作为默认形态
  • 使用蓝色作为 hover 形态
  • 使用红色作为 pressed 形态

⚽ 使用阿里矢量图标库,可以下载到上述图片,下载的时候可以手动选择颜色

注意这里的文件命名。

image-20250202153026411

B. 创建一个复选框,并且用样式表来编辑复选框的样式

QCheckBox {font-size: 20px;
}QCheckBox::indicator {width: 20px;height: 20px;
}QCheckBox::indicator:unchecked {image: url(:/checkbox-unchecked.png);
}QCheckBox::indicator:unchecked:hover {image: url(:/checkbox-unchecked_hover.png);
}QCheckBox::indicator:unchecked:pressed {image: url(:/checkbox-unchecked_pressed.png);
}QCheckBox::indicator:checked {image: url(:/checkbox-checked.png);
}QCheckBox::indicator:checked:hover {image: url(:/checkbox-checked_hover.png);
}QCheckBox::indicator:checked:pressed {image: url(:/checkbox-checked_pressed.png);
}
  • 鼠标点击选中,再取消的过程,可以看到此时的复选框就变得丰富起来了:

img

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

A. 创建 resource.qrc 文件,并导入以下图片

img

  • 使用黑色作为默认形态
  • 使用蓝色作为 hover 形态
  • 使用红色作为 pressed 形态

B. 在界面上创建两个单选按钮,并且在 Qt Designer 中编写样式

  • 此处为了让所有 QRadioButton 都能生效,把样式设置在 Widget 上了,并且使用后代选择器选中了 QWidget 里面的 QRadioButton。
 QWidget QRadioButton {font-size: 20px;
}QWidget QRadioButton::indicator {width: 20px;height: 20px;
}QWidget QRadioButton::indicator:unchecked {image: url(:/radio-unchecked.png);
}QWidget QRadioButton::indicator:unchecked:hover {image: url(:/radio-unchecked_hover.png);
}QWidget QRadioButton::indicator:unchecked:pressed {image: url(:/radio-unchecked_pressed.png);
}QWidget QRadioButton::indicator:checked {image: url(:/radio-checked.png);
}QWidget QRadioButton::indicator:checked:hover {image: url(:/radio-checked_hover.png);
}QWidget QRadioButton::indicator:checked:pressed {image: url(:/radio-checked_pressed.png);
}

运行程序,如下:

img

注意 :

  • QSS 中有些属性,子元素能继承父元素(例如 font-size、color 等),但是也有很多属性是不能继承的。
  • 具体哪些能继承哪些不能继承,规则比较复杂,我们不去具体研究,实践中我们编写更精准的选择器是上策。
④ 自定义输入框

A. 在界面上创建一个单行编辑框,在 Qt Designer 中编写样式

QLineEdit {border-width: 2px; border-radius: 20px;border-color: rgb(170, 170, 255);border-style: solid;padding: 0 8px;color: rgb(170, 85, 127);background:rgb(220, 220, 225);selection-background-color: rgb(0, 180, 0);selection-color: rgb(180, 0, 0);
}

B. 执行程序,输入文本,并且选中 :

image-20250202161340303

属性说明
border-width设置边框宽度。
border-radius设置边框圆角。
border-color设置边框颜色。
border-style设置边框风格。
padding设置内边距。
color设置文字颜色。
background设置背景颜色。
selection-background-color设置选中文字的背景颜色。
selection-color设置选中文字的文本颜色。
  • 这里 background 其实也可以用 background-color 的
⑤ 自定义列表

A. 在界面上创建一个 ListView,然后在样式表中编写代码,如下:

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);
}

B. 然后将其变形成为 ListWidget,然后右击进行编辑项目,如下:

image-20250202155239152

C. 执行程序,结果如下:

image-20250202155328463

要点说明
::item选中 QListView 中的具体条目。
:hover选中鼠标悬停的条目
:selected选中某个被选中的条目。
background设置背景颜色
border设置边框。
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 进行渐变的。

举个例子:

现在新建一个项目,界面不创建任何任何控件,编写样式,如下:

QWidget {background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop: 0 #fff, stop: 1 #000);
}

修改代码》让其当前按照水平从左往右从白色过渡到黑色

QWidget {background-color: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop: 0 #fff, stop: 1 #000);
}


⑥ 自定义菜单栏

A. 创建菜单栏,创建若干菜单项和一个分隔符:

B. 编写样式表,如下:

QMenuBar {background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 lightgray, stop:1 darkgray);spacing: 3px;
}QMenuBar::item {padding: 1px 4px;background: transparent;border-radius: 4px;
}QMenuBar::item:selected {background: #a8a8a8;
}QMenuBar::item:pressed {background: #888888;
}QMenu {background-color: white;margin: 0 2px;
}QMenu::item {padding: 2px 25px 2px 20px;border: 3px solid transparent;
}QMenu::item:selected {border-color: darkblue;background: rgba(100, 100, 100, 150);
}QMenu::separator {height: 2px;background: lightblue;margin-left: 10px;margin-right: 5px;
}

执行代码,结果如下:

img

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

⑦ 登录界面
  • 【基于上述学习的 QSS 样式,来制作一个美化版本的登录界面】

A. 在界面上创建元素,并使用 布局管理器 把相关元素包裹一下

image-20250202162524988

  • 使用 QVBoxLayout 来管理上述控件。

  • 两个输入框和按钮的 minimumHeight 均设置为 30(元素在布局管理器中无法直接设置 width 和 height,使用 minimumWidthminimumHeight 代替,此时垂直方向的 sizePolicy 要设为 fixed)。

image-20250202162517629

  • 右键 QCheckBox,选择 Layout Alignment 可以设置 checkbox 的对齐方式(左对齐,居中对齐,右对齐)。

设置背景图片

创建 qrc 文件,导入背景图

image-20250202162949777

  • 第一想法:直接给 QWidget 顶层窗口设置背景图,但是 Qt 中存在限制 QWidget的顶层窗口 无法设置背景图片
  • 因此可以再套上一层 QFrame,背景图片就设置到 QFrame 上即可。

image-20250202163259469

编写 QFrame 的 QSS 样式:

QFrame{border-image:url(:/ship.jpg);
}
  • 使用 border-image 设置背景图片,而不是 background-image
  • 主要是因为 border-image 是可以自动缩放的,这一点在窗口大小发生改变时是非常有意义的。

效果如下:

image-20250202163538677

编写 CSS 代码(样式表):

  • 背景色使用 transparent 表示完全透明(应用父元素的背景)。
QFrame{border-image:url(:/ship.jpg);
}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: #5555;border-style: outset;border-radius: 10px;
}QPushButton:pressed {color: black;background-color: #ced1db;border-style: inset;
}

然后还需要对两个输入框的 ui 做点改变,如下:

img

运行程序,最终效果如下:

image-20250202164239999

  • 最终完整样式代码,这些代码设置到 QFrame 的属性中即可。通常我们建议把样式代码集中放置,方便调整和排查。

六、小结

QSS 本身给 Qt 提供了更丰富的样式设置的能力,但是整体来说 QSS 的功能是不如 CSS 的。

  • 在 CSS 中,整个网页的样式都是 CSS 一手负责,CSS 功能更强大,并且也更可控。
  • 相比之下,Qt 中是以原生 API 为主,来控制控件之间的尺寸、位置等,QSS 只是起到辅助的作用。
  • 而且 Qt 中提供的一些 “组合控件”(像 QComboBox、QSpinBox 等)内部的结构是不透明的,此时进行一些样式设置也会存在一定的局限性。

另外,做出好看的界面,光靠 QSS 是不够的,更重要的是需要 专业美工做出设计稿

在这里插入图片描述

相关文章:

【QT】QT界面的美容院 -- QSS

一、背景介绍 &#x1f525; 在网页前端开发领域中&#xff0c;CSS 是一个至关重要的部分&#xff0c;描述了一个网页的 “样式”&#xff0c;从而起到对网页 美化 的作用。 所谓 样式 &#xff0c;包括不限于大小、位置、颜色、背景、间距、字体等等。现在的网页很难找到没有…...

【AI】使用Huggingface模型实现文本内容摘要器

【AI】使用Huggingface模型实现文本内容摘要器 推荐超级课程: 本地离线DeepSeek AI方案部署实战教程【完全版】Docker快速入门到精通Kubernetes入门到大师通关课AWS云服务快速入门实战目录 【AI】使用Huggingface模型实现文本内容摘要器什么是摘要器?摘要器的应用场景什么是…...

centOS 安装和配置docker

以下是在 CentOS 系统上安装和配置 Docker 的详细步骤&#xff1a; 一、安装 Docker 1. 卸载旧版本&#xff08;如有&#xff09; sudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate …...

现在AI大模型能帮做数据分析吗?

实际上&#xff0c;有了AI大模型的支持才使得AI数据分析的功能得到更好地应用&#xff0c;比如小浣熊AI支持数据交互功能&#xff0c;只要你输入具体的数据分析要求&#xff0c;它就能自动帮你完成数据清洗、分析、可视化全流程&#xff0c;而且还能生成数据分析报告。 当然&a…...

qt中,父类中有Q_OBJECT,子类中还需要加Q_OBJECT吗

在 Qt 中&#xff0c;关于子类是否需要添加 Q_OBJECT 宏的问题&#xff0c;可以总结如下&#xff1a; 1. 需要添加 Q_OBJECT 的情况 如果子类满足以下任一条件&#xff0c;必须显式添加 Q_OBJECT 宏&#xff1a; 定义了新的信号或槽&#xff1a;即使父类已有 Q_OBJECT&#…...

vue2实现在屏幕中有一个小机器人可以随意移动

第一步&#xff1a;创建store目录结构 src/ ├── store/ │ ├── modules/ │ │ └── robot.js # 机器人专用状态模块 │ └── index.js # Vuex 主配置文件第二步&#xff1a;创建机器人状态模块 创建 src/store/modules/robot.js 文件&#xff…...

MCP协议实战指南:在VS Code中实现PostgreSQL到Excel的自动化迁移

作者&#xff1a;后端小肥肠 &#x1f34a; 有疑问可私信或评论区联系我。 &#x1f951; 创作不易未经允许严禁转载。 姊妹篇&#xff1a; 从PDF到精准答案&#xff1a;Coze助力RAGFlow框架提升数据召回率_提升ragflow-CSDN博客 CozeTreeMind实测&#xff1a;秒出ISO标准流程图…...

Before After:SQL整容级优化

首先说明这个优化有一定提升&#xff0c;但不是我所期望的 我接到一个涉及优化的SQL&#xff0c;具体内容实在太长。而且可能也不利于阅读。于是我脱敏以及简化一下。SQL中间大量的充斥着 (select 列名1 from t1 where t1.id t2.id ) A, (select 列名2 from t1 where t1.id …...

hash.

Redis 自身就是键值对结构 Redis 自身的键值对结构就是通过 哈希 的方式来组织的 哈希类型中的映射关系通常称为 field-value&#xff0c;用于区分 Redis 整体的键值对&#xff08;key-value&#xff09;&#xff0c; 注意这里的 value 是指 field 对应的值&#xff0c;不是键…...

JMeter重要的是什么

重要特性 支持多种协议&#xff1a; JMeter支持对多种协议进行性能测试&#xff0c;包括HTTP、HTTPS、FTP、JDBC&#xff08;数据库&#xff09;、LDAP、JMS、SOAP、REST等。这使得它能够适应各种不同的测试场景。强大的负载模拟能力&#xff1a; JMeter能够模拟大量的虚拟用户…...

Java研学-Activiti7工作流(二)

三 Activiti7 搭建环境 1 开发环境 Jdk1.8或以上版本&#xff1b;Mysql 5及以上的版本&#xff1b;Tomcat8.5&#xff1b;IDEA&#xff1b;Activiti 7.0.0.SR1(流程设计器) 2 安装Activiti流程设计器 ① 在线安装&#xff1a;在Plugins中搜索actiBPM&#xff0c;然后点击Sear…...

鸿蒙开发-编译器使用

15.2编译器使用-目录详解 15.3 编辑器使用-切换工程模块 15.3 编辑器使用-多设备预览 15.1 编辑器使用-编辑中英文...

HTML5+CSS前端开发【保姆级教学】+图像标签附路径问题

引入&#xff1a; Hello&#xff01;&#xff0c;各位编程猿们&#xff01;我们知道在网页文档中合理地加入图像&#xff0c;会使文档变得更加生动活泼和引人入胜&#xff0c;而且看上去更加专业、更具有信息性且易于浏览。本期主要介绍图像标签以及超链接标签 一、设置图像标…...

快速启动 Rust + WebAssembly 项目

一、 模板一&#xff1a;wasm-pack-template 适合目标&#xff1a;构建一个 Rust 写的 WebAssembly npm 包 这是最常用、也是最官方推荐的起点模板。它提供了&#xff1a; Cargo.toml 配置好 WebAssembly 的 crate 类型&#xff08;cdylib&#xff09;已设置 wee_alloc 和 pan…...

Linux命令-vim编辑

用vi或vim命令进入vim编辑器。 基础: u -- 撤销上一次操作。 x -- 剪切当前光标所在处的字符。 yy -- 复制当前行。 dd -- 剪切当前行。 p -- 粘贴剪贴板内容到光标下方。 i -- 切换到输入模式&#xff0c;在光标当前位置开始输入文本。 :wq -- 保存并退出Vim 编辑器。…...

Windows单机模拟MySQL主从复制

这里写自定义目录标题 下载MySQL ZIP压缩包安装主库1、创建配置文件2、安装服务3、初始化数据库4、启动服务5、配置主库 安装从库1、配置ini文件2、安装服务3、初始化数据库4、启动服务5、配置从库6、验证从库状态 操作主库验证 下载MySQL ZIP压缩包 https://dev.mysql.com/do…...

【区块链+ 人才服务】广州理工学院区块链教学平台 | FISCO BCOS 应用案例

深圳市火链文化传播有限公司与广州理工学院合作&#xff0c;共同建设了“区块链教学平台”。该平台旨在探索区块链技术在教 育领域的应用&#xff0c;特别是在混合式教学模式中的创新实践。 “区块链教学平台”运用了区块链技术的核心优势&#xff0c;包括分布式、信息防篡改以…...

Java Stream深度解析 高阶技巧与性能优化实战

文章目录 一、Stream底层机制揭秘1.1 Stream流水线架构1.2 Spliterator探秘 二、自定义收集器高级实现2.1 实现高性能统计收集器2.2 多级分组优化技巧 三、并行流深度优化3.1 并行度控制策略3.2 工作窃取(Work-Stealing)优化 四、无限流与短路操作4.1 生成无限质数流4.2 短路操…...

【JavaEE初阶】多线程重点知识以及常考的面试题-多线程进阶(一)

本篇博客给大家带来的是多线程的知识点, . &#x1f40e;文章专栏: JavaEE初阶 &#x1f680;若有问题 评论区见 ❤ 欢迎大家点赞 评论 收藏 分享 如果你不知道分享给谁,那就分享给薯条. 你们的支持是我不断创作的动力 . 王子,公主请阅&#x1f680; 要开心要快乐顺便进步 1. 常…...

计算机视觉图像分割入门:阈值、区域生长与分水岭算法

计算机视觉图像分割入门:阈值、区域生长与分水岭算法 一、前言二、图像分割基础概念大揭秘​2.1 图像分割的定义​2.2 图像分割的目的与作用​2.3 图像分割的应用领域​三、阈值算法:最简单的图像分割法​3.1 原理剖析​3.2 手动阈值分割及代码示例​3.3 自动阈值分割方法及代…...

android​​弱网环境数据丢失解决方案(3万字长文)

在移动互联网时代&#xff0c;Android 应用已经成为人们日常生活中不可或缺的一部分。从社交媒体到在线购物&#xff0c;从移动办公到娱乐游戏&#xff0c;用户对应用的依赖程度与日俱增。然而&#xff0c;尽管网络基础设施在全球范围内得到了显著改善&#xff0c;弱网环境依然…...

设计模式:迪米特法则 - 最少依赖,实现高内聚低耦合

一、迪米特法则简介 迪米特法则&#xff08;Law of Demeter&#xff0c;简称 LoD&#xff09;&#xff0c;也称为“最少知识法则”&#xff0c;核心思想是&#xff1a;一个对象应当对其他对象有最少的了解&#xff0c;仅与直接相关的对象交互。通过减少对象之间的耦合度&#…...

React 把一系列 state 更新加入队列

把一系列 state 更新加入队列 设置组件 state 会把一次重新渲染加入队列。但有时你可能会希望在下次渲染加入队列之前对 state 的值执行多次操作。为此&#xff0c;了解 React 如何批量更新 state 会很有帮助。 开发环境&#xff1a;Reacttsantd 学习内容 什么是“批处理”以…...

AntVG2可视化学习与开发笔记-React19(持续更新)

目录 开始工作 第一步&#xff1a;创建画布空间 第二步&#xff1a;获取画布空间并挂载AntVG2 第三步&#xff1a;进行画布设计配置与数据挂载 第四步&#xff1a;完整代码 实际效果如下 参数理解 一、scale 1. 归一化range:[0,1] 2.nice、domainMin 开始工作 第一…...

从PPT到DeepSeek开启信息可视化的全新之旅

在当今信息爆炸的时代&#xff0c;如何高效、生动地展示信息成为了个人与企业在沟通、汇报、推广等场景中面临的关键挑战。传统的演示工具&#xff0c;如PPT&#xff0c;虽然曾经是展示信息的主力军&#xff0c;但随着技术的发展和人们审美、交互需求的提升&#xff0c;其局限性…...

spark-sql学习内容总结

SparkSession 定义与功能‌&#xff1a;SparkSession是Spark SQL的入口&#xff0c;封装SparkContext&#xff0c;提供了创建DataFrame和执行SQL的能力。它实质上是SQLContext和HiveContext的组合&#xff0c;因此兼容这两者的API。 创建方式‌&#xff1a;在使用spark-shell…...

Spring-AI-alibaba 结构化输出

1、将模型响应转换为 ActorsFilms 对象实例&#xff1a; ActorsFilms package com.alibaba.cloud.ai.example.chat.openai.entity;import java.util.List;public record ActorsFilms(String actor, List<String> movies) { } GetMapping("/toBean")public Ac…...

ffmpeg实现视频转码

ffmpeg 实现视频转码 什么是视频编码 视频上传成功后需要对视频进行转码处理。 什么是视频编码&#xff1f; 查阅百度百科如下&#xff1a; 所谓视频编码方式就是指通过压缩技术&#xff0c;将原始视频格式的文件转换成另一种视频格式文件的方式。视频流传输中最为重要的编解…...

【Java学习笔记】Java初级阶段代码规范

Java 初级阶段代码规范 1. 类、方法的注释&#xff0c;要以 javadoc 的方式来写。 2. 非 Java Doc 的注释&#xff0c;往往是给代码的维护者看的&#xff0c;着重告读者为什么这样写&#xff0c;如何修改&#xff0c;注重什么问题等 3. 使用 tab 操作&#xff0c;实现缩进&am…...

适应 AI 时代的软件开发流程:用 AI + TDD 构建可维护项目

🧠 适应 AI 时代的软件开发流程:用 AI + TDD 构建可维护项目 本文面向有系统开发经验的工程师,分享如何结合 Git 管理、AI 协作、YAML 驱动与 TDD 开发方式,高效构建一个可维护、可协作、可交付的嵌入式或通用工程项目。适合 BLE 模块、协议栈组件、物联网控制系统等项目落…...

EasyCVR视频汇聚系统:AIoT+视频智能分析赋能食品安全生产全流程监管

近年来,随着食品安全问题频发,消费者对食品加工企业的信任度逐渐下降,企业生产监管难度加大,市场监管也面临诸多挑战。在这样的背景下,食品加工企业迫切需要通过智能化手段提升生产管理水平,满足消费者和监管部门的要求,同时实现自身业绩的提升。 本文将结合EasyCVR与智…...

ASP.NET Core 性能优化:分布式缓存

文章目录 前言一、分布式缓存的核心概念作用&#xff1a;与内存缓存的区别&#xff1a; 二、ASP.NET Core 中的 IDistributedCache三、常用分布式缓存实现1&#xff09;Redis&#xff08;最常用&#xff09;2&#xff09;SQL Server3&#xff09;NCache&#xff08;企业级方案&…...

一款安全好用的企业即时通讯平台,支持统一门户

在数字化转型的浪潮中&#xff0c;企业面临着信息孤岛、系统分散、协作低效等诸多挑战。BeeWorks作为一款专为企业打造的数字化底座平台&#xff0c;凭借其强大的企业内部应用集成能力和单点登录功能&#xff0c;正在成为企业数字化转型的有力推手。 数字化底座平台&#xff1…...

C语言 栈 的 描述 和 详解

什么是栈&#xff1f; 栈是一种特殊的线性数据结构。 定义及特点 - 栈是一种只能在一端进行插入和删除操作的特殊线性表。它按照后进先出&#xff08;Last In First Out&#xff0c;LIFO&#xff09;的原则存储数据&#xff0c;就像一个只能从顶部取放物品的箱子&#xff0c;…...

.NET MCP 示例

服务器端示例 基础服务器 以下是一个基础的 MCP 服务器示例&#xff0c;它使用标准输入输出&#xff08;stdio&#xff09;作为传输方式&#xff0c;并实现了一个简单的回显工具&#xff1a; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.H…...

【论文阅读】MOE奠基论文《Adaptive Mixtures of Local Experts》

《Adaptive Mixtures of Local Experts》 前言一、让协同学习竞争1.1 方案1.2 方案演变的由来 二、让竞争学习协同2.1 竞争学习2.2 竞争学习协同 三、案例验证3.1 任务背景3.2 实验结果3.3 后续工作 (Future Work) 前言 论文提出了一个基于多个分离网络的有监督学习方案,该方案…...

MATLAB中replace函数用法

目录 语法 说明 示例 替换字符串数组中的子字符串 替换匹配模式的子字符串 替换多个子字符串 replace函数的功能是查找并替换一个或多个子字符串。 语法 newStr replace(str,old,new) 说明 newStr replace(str,old,new) 将所有出现的子字符串 old 替换为 new。如果 …...

MATLAB基本数据类型

1. ‌数值类型‌ ‌整数类型‌&#xff1a; 有符号整数&#xff08;如 int8, int16, int32, int64&#xff09;和无符号整数&#xff08;如 uint8, uint16, uint32, uint64&#xff09;。 这些类型分别占用 1、2、4、8 个字节&#xff0c;表示不同范围的整数值。 ‌浮点数类…...

LeetCode 热题 100_单词拆分(86_139_中等_C++)(动态规划)

LeetCode 热题 100_单词拆分&#xff08;86_139&#xff09; 题目描述&#xff1a;输入输出样例&#xff1a;题解&#xff1a;解题思路&#xff1a;思路一&#xff08;动态规划&#xff09;&#xff1a; 代码实现代码实现&#xff08;思路一&#xff08;动态规划&#xff09;&a…...

Spring Boot 集成spring-boot-starter-data-elasticsearch

第一步&#xff0c;添加Maven依赖 <!--es--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-elasticsearch</artifactId></dependency> 第二步&#xff0c;配置yml spring:elastic…...

【Linux】Linux下的gcc/g++编译器与动静态库

目录 对程序的认知&&初识gcc逐步分析程序的编译步骤预编译【进行宏替换】gcc需要执行的指令 ⭐ 编译【C语言——>汇编语言】gcc要执行的指令⭐ 汇编【汇编语言——>可重定位目标二进制文件】gcc需要执行的指令 链接【生成可执行文件或库文件】gcc需要执行的指令 …...

从暴力到动态规划再到双指针:使用 Java 探索接雨水问题的不同解法

文章目录 一、问题描述二、暴力法&#xff08;Brute Force&#xff09;思路实现代码 三、动态规划法&#xff08;Dynamic Programming&#xff09;思路实现代码 四、双指针法&#xff08;Two Pointers&#xff09;思路实现代码 五、方法对比 在本文中&#xff0c;我们将探讨经典…...

CI/CD(十) Jenkins共享库与k8s集成

一、创建k8skey&#xff08;v1.28.2版本&#xff09; 1、查看k8s集群名称 rootk8s-master:~# kubectl config get-contexts CURRENT NAME CLUSTER AUTHINFO NAMESPACE * kubernetes-adminkubernetes kubernetes kuber…...

5.Elasticsearch - Spring Data 框架

一、Kibana 介绍 Kibana 是一个免费且开放的用户界面&#xff0c;能够让你对 Elasticsearch 数据进行可视化&#xff0c;并让你在 Elastic Stack 中进行导航。你可以进行各种操作&#xff0c;从跟踪查询负载&#xff0c;到理解请求如何流经你的整个应用&#xff0c;都能轻松完…...

如何通过技术手段降低开发成本

通过技术手段降低开发成本的关键在于&#xff1a; 自动化工具的使用、优化开发流程、云计算资源的利用、开发技术栈的精简与创新、团队协作平台的高效管理。 其中&#xff0c;自动化工具的使用是最为有效的技术手段之一。自动化工具通过减少人工干预和重复性工作&#xff0c;大…...

java android持久化数据

1. SQLite 数据库&#xff08;Android 内置&#xff09; 1.1 创建数据库帮助类 public class DatabaseHelper extends SQLiteOpenHelper {private static final String DATABASE_NAME "MyDatabase.db";private static final int DATABASE_VERSION 1;// 表名和列名…...

Chromium 134 编译指南 macOS篇:系统环境准备(一)

1. 引言 在当今浏览器领域&#xff0c;开源项目Chromium的地位举足轻重。作为众多现代浏览器的技术基础&#xff0c;Chromium不仅驱动着Google Chrome&#xff0c;还为Microsoft Edge、Opera等众多知名浏览器提供了核心引擎。对于热衷于浏览器技术研究&#xff0c;或希望开发自…...

性能优化-Spring参数配置、数据库连接参数配置、JVM调优

SpringBoot配置参数 server:tomcat:#线程池配置max-threads: 200 # 最大工作线程数&#xff08;建议&#xff1a;2~4倍CPU核心数&#xff0c;如16核设200-400&#xff09;min-spare-threads: 20 # 最小空闲线程&#xff08;应对突发流量&#xff0c;…...

【2025年泰迪杯数据挖掘挑战赛】B题 数据预处理+问题建模与求解

目录 2025年泰迪杯数据挖掘挑战赛 B题数据预处理 问题一、二建模与求解三、数据预处理3.1 基于多核并行的协同处理方法的数据读取3.2 基于多核并行协同处理的数据聚合 四、问题一五、问题一技术文档与matlab代码 2025年泰迪杯数据挖掘挑战赛 B题 数据预处理 问题一、二建模与求…...

git怎么使远程分支回退到指定的节点处

git使远程分支回退到指定的节点 引言场景描述步骤 引言 最近提交代码的时候&#xff0c;总将分支合并错&#xff0c;原本要合到A分支&#xff0c;结果合并到了B分支&#xff0c;这样就导致b分支需要回退到我没有合并之前的节点处。 本文记录下怎么将远程分支回退到指定的节点。…...