Qt——界面美化 QSS

文章目录

界面美化 QSS

类似于网页前端的CSS,可以对界面的各种样式进行设置

需要注意,如果QSS和C++代码同时对同一个控件的同一个属性进行了设置,那么QSS的优先级更高

QSS 基本格式:

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

例如,将窗口内所有的QPushBUtton的文本设置成红色:

css 复制代码
#include "widget.h"

#include "./ui_widget.h"

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

    ui->pushButton->setStyleSheet("QPushButton {color : rgb(200, 0, 100);}");
}

Widget::~Widget() { delete ui; }

设置样式的时候,是可以指定某个控件来设置的。指定控件之后,此时的样式就会针对这个指定的控件和这个控件的子控件同时生效

例如:

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 : rgb(200, 0, 100);}");
}

Widget::~Widget() { delete ui; }

全局样式

通过main函数内部构造的QApplication类,进行全局样式设置

  • 如果设置了全局样式,然后在某个控件里面又设置了新的样式(不冲突),那么这个控件的样式就会叠加 。即层叠性
  • 如果设置了全局样式,然后在某个控件里面又设置了同样的的样式,那么局部样式就会覆盖全局样式。即局部样式优先级高于全局样式
  • 所以,实际开发中,就可以设置通用的全局样式,来统一风格 。如果需要对界面的某个控件进行微调,就可以使用局部样式

例如:

cpp 复制代码
#include <QApplication>

#include "widget.h"

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

    a.setStyleSheet("QPushButton {color : green;}");

    w.show();
    return a.exec();
}

// widget.cpp
#include "widget.h"

#include "./ui_widget.h"

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

    ui->pushButton_1->setStyleSheet("background-color : white;");		// 叠加性
    ui->pushButton_2->setStyleSheet("color : rgb(200, 0, 100);}");		// 局部样式优先级高于全局样式
}

Widget::~Widget() { delete ui; }

样式和代码分离

如果涉及到的样式比较多,那么将其和C++代码混合明显是不合适的。

我们可以把样式代码拎出来,放到单独的文件中,后续可以直接让C++代码来读取并加载文件内容

Qt Designer中直接集成了上述功能,允许我们把样式直接写到 **.ui**文件里,并支持实时预览


选择器

选择器 示例 说明
全局选择器 * 选择所有的 widget.
类型选择器 (type selector) QPushButton 选择所有的 QPushButton 和 其子类 的控件.
类选择器 (class selector) .QPushButton 选择所有的 QPushButton 的控件. 不会选择子类.
ID 选择器 #pushButton_2 选择 objectNamepushButton_2 的控件.
后代选择器 QDialog QPushButton 选择 QDialog 的所有后代(子控件, 孙子控件等等) 中的 QPushButton.
子选择器 QDialog > QPushButton 选择 QDialog 的所有子控件中的 QPushButton.
并集选择器 QPushButton, QLineEdit, QComboBox 选择 QPushButton, QLineEdit, QComboBox 这三种控件. (即接下来的样式会针对这三种控件都生效).

类型选择器

例如:

cpp 复制代码
Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) {
    ui->setupUi(this);

    QString style = "QWidget {color : rgb(200, 50, 100);}";
    ui->layoutWidget->setStyleSheet(style);
}

通过类型选择器,将layoutWidget下的,所有QWidget及其子类的控件的文本设置颜色

效果:


类选择器

类选择器和类型选择器的区别是,类选择器不会选择指定控件类型的子类

例如:

cpp 复制代码
Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) {
    ui->setupUi(this);

    QString style = ".QWidget {color : rgb(200, 50, 100);}";
    ui->layoutWidget->setStyleSheet(style);
}

通过类选择器,将layoutWidget下的QWidget类型的控件的文本设置颜色

效果:


ID选择器

ID选择器只针**对传入的****objectName**的空间生效

例如:

cpp 复制代码
Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) {
    ui->setupUi(this);

    QString style = "#pushButton_1 {color : rgb(200, 50, 100);}";
    ui->layoutWidget->setStyleSheet(style);
}

使用ID选择器,对objectNamepushButton_1的控件,设置字体颜色

效果:


并集选择器

通过传入多个类型,样式会对传入的所有类型生效

例如:

cpp 复制代码
Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) {
    ui->setupUi(this);

    QString style = "QPushButton, QLineEdit {color : rgb(200, 50, 100);}";
    ui->layoutWidget->setStyleSheet(style);
}

通过并集选择器,将layoutWidget下的QPushButtonQLineEdit类型的控件设置字体颜色

效果:


子控件选择器

有些控件内部包含了多个"子控件"。比如QComboBox的下拉后的面板

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

哪些样式有哪些子控件,可通过下面的方式搜索:

Qt Assistant -> Qt Style Sheet Reference -> List of Sub-Controls

例如:

cpp 复制代码
Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) {
    ui->setupUi(this);

    QString style = "#comboBox::down-arrow{image : url(:/down)}";
    ui->comboBox->setStyleSheet(style);
}

利用ID选择器和子控件选择器,对objectNamecomboBox的下拉按钮设置图标

效果:


伪类选择器

并非选择一个控件,而是选择一个控件的状态

  • 当状态具备时,控件被选中,样式生效
  • 当状态不具备时,空间不被选中,样式不生效
伪类选择器 说明
:hover 鼠标放到控件上
:pressed 鼠标左键按下时
:focus 获取输入焦点时
:enabled 元素处于可用状态时
:checked 被勾选时
:read-only 元素为只读状态时

注:可以通过 ! ,的方式,对状态取反

可以通过:Qt Assistant -> Qt Style Sheet Reference -> List of Pseudo-States。进行更详细的查看

例如:

cpp 复制代码
Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) {
    ui->setupUi(this);

    QString style =
        "#pushButton:hover {background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 1, stop: 0 "
        "rgb(255, 100, 100), "
        "stop: 1 "
        "rgb(100, 255, 100));}";

    ui->pushButton->setStyleSheet(style);
}

利用伪类选择器,当**光标进入****pushButton**时,其背景颜色就会发生改变

效果:


渐变色

上面我们给pushButton设置背景颜色时,用到了渐变色。下面来介绍渐变色的设置方法

线性渐变

所谓线性渐变,就是沿着从左往右、从上往下、对角线的方向进行颜色渐变

可以对左上、右上、左下、右下分别标点为 (0, 0), (0, 1), (1, 0), (1, 1)

用到的方法为:qlineargradient

例如:

qss 复制代码
QPushButton {
    background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop: 0 red, stop: 1 yellow);
}
  • x1y1,即渐变起点的位置
  • x2y2,即渐变终点的位置
  • stop:渐变 stops 点,stop:0 对应起始颜色,stop:1 对应结束颜色,也可插入多个 stops 实现多色渐变,比如 stop:0 red, stop:0.5 green, stop:1 blue

径向渐变

即从中心向外围扩散的渐变方式

用到的方法为:qradialgradient

例如:

qss 复制代码
QPushButton {
    /* 圆心在控件中心(0.5, 0.5),半径 0.5(控件尺寸一半),颜色从中心白色渐变到边缘黑色 */
    background: qradialgradient(cx: 0.5, cy: 0.5, radius: 0.5, stop: 0 white, stop: 1 black);
}
  • cx, cy:渐变圆心坐标(0~1 范围,相对控件自身),(0.5, 0.5) 即控件中心。
  • radius:渐变半径(0~1 范围,相对于控件尺寸),0.5 表示以控件一半大小为半径。
  • stop:同线性渐变,控制颜色分布。

例如:

qss 复制代码
#dial::chunk {
    background : qradialgradient(cx: 0.5, cy: 0.5, radius: 0.5, stop: 0 green, stop: 1 blue);
}

盒子模型 Box Model

Qt中,每个Widget都是一个矩形,那么这个矩形,从里到外都可以分为四个部分:

  • CONTENT:内容

  • PADDING:内边距。内容区与边框(BORDER)之间的空白区域,用于控制内容与边框的间距,会影响元素整体占位大小 (padding 会让元素 "变大" ),且背景色会覆盖内边距区域

  • BORDER:边距。包裹内容和内边距的线条 ,可设置样式border-style(如实线、虚线)、宽度border-width、颜色border-color,是元素视觉边界

  • MARGIN:外边距。最外层,用于控制当前元素与其他元素之间的空白间距 ,不影响元素自身大小,但会影响与相邻元素的布局关系(如上下元素的间距由 margin-top/margin-bottom 决定 ),且 外边距区域无背景色。 其可拆分为四个属性

    • margin-left
    • margin-right
    • margin-top
    • margin-bottom
    • 使用时,可以写为margin : 10px,即上下左右分别10px
    • 也可以写为margin: 10px, 20px,即上下10px,左右20px
    • 也可以写为margin: 10px, 20px, 30px, 40px,以上右下左的顺序(顺时针)进行设置
    • padding属性同理
  • 默认情况下marginpaddingborder-width为0

    例如:

cpp 复制代码
Widget::Widget(QWidget* parent) : QWidget(parent), ui(new Ui::Widget) {
    ui->setupUi(this);

    QPushButton* button = new QPushButton(this);
    button->setText("这是一个按钮");
    button->setGeometry(0, 0, 300, 300);
    button->setObjectName("button");

    QString style =
        "#button {"
        "border : 20px solid rgb(200, 100, 100);"
        "padding : 50px;"
        "margin : 30px;"
        "text-align: left;"  // 改为左对齐
        "}";

    button->setStyleSheet(style);
}

效果:


样例

修改复选框样式

cpp 复制代码
#checkBox {
	font-size : 20px;
	color : blue;
}

#checkBox::indicator:unchecked {
	image : url(:/source/white-no.png);
}

#checkBox:indicator:unchecked:hover {
	image : url(:/source/blue-no.png);
}

#checkBox:indicator:unchecked:pressed {
	image : url(:/source/red-yes.png);
}

#checkBox:indicator:checked {
	image : url(:/source/white-yes.png);
}

#checkBox:indicator:checked:hover {
	image : url(:/source/blue-yes.png);
}

#checkBox:indicator:checked:pressed {
	image : url(:/source/red-yes.png);
}

效果:


修改单行编辑框样式

cpp 复制代码
#lineEdit {
    border-radius: 10px;
    border : 3px solid rgb(110, 110, 110);
    padding : 5px;
    font-size: 15px;
    background-color: rgb(197, 197, 197);
    color : rgba(1, 40, 49, 0.523);
    selection-background-color: rgb(116, 1, 1);		// 修改被选中文本的背景色
    selection-color: rgb(148, 243, 152);			// 修改被选中文本的文本色
}

效果:


实现一个简单的登陆界面

实现一个简单的登录页,包含以下功能:

  • 设置背景图片
  • 设置各个控件的样式
  • 可以控制用户密码的显示与隐藏
  • 可以根据窗口大小的变化,自动调节控件布局

注意:Qt不允许直接给主窗口QWidget设置背景图片,只能通过向主窗口添加一个QFrame控件,对这个frame进行背景图设置

QFrame 基本功能
  • 提供边框样式QFrame 可以设置多种边框样式,如 Box(矩形框)、Panel(面板样式)、Sunken(凹陷样式)、Raised(凸起样式) 等。通过设置边框样式,能让界面元素更具层次感和视觉吸引力。例如,在设计登录界面时,可以使用 QFrame 作为输入框区域的容器,并设置为凹陷样式,使其看起来像是一个独立的输入面板。
  • 背景和填充 :除了边框,QFrame 还可以设置背景颜色或背景图片,从而进一步定制其外观。这对于创建具有特定风格的界面组件非常有用,比如为一个包含多个按钮的区域添加一个带有颜色或图案的背景。

Qt Desginer展示:

代码:

cpp 复制代码
// widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QResizeEvent>
#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE

class Widget : public QWidget {
    Q_OBJECT

   public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

    void resizeEvent(QResizeEvent *event);

   private slots:
    void on_toolButton_clicked();

   private:
    Ui::Widget *ui;
};
#endif  // WIDGET_H

// widget.cpp
#include "widget.h"

#include <QLineEdit>
#include <QPushButton>

#include "./ui_widget.h"

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

    ui->label->setAlignment(Qt::AlignCenter);
    
    // 设置背景图
    QString style_frame =
        "#frame {"
        "border-image : url(:/background);"
        "}";
    ui->frame->setStyleSheet(style_frame);
    
    // 设置输入框样式
    QString style_edit =
        ".QLineEdit {"
        "background-color : rgba(50, 50, 50, 0.7);"
        "border : 1px solid rgb(160, 100, 200);"
        "border-radius : 10px;"
        "font-size : 15px;"
        "padding : 5px;"
        "}";
    ui->lineEdit->setStyleSheet(style_edit);
    ui->lineEdit_2->setStyleSheet(style_edit);

    ui->lineEdit->setEchoMode(QLineEdit::Normal);
    ui->lineEdit_2->setEchoMode(QLineEdit::Password);
    ui->lineEdit->setClearButtonEnabled(true);
    ui->lineEdit_2->setClearButtonEnabled(true);
    ui->lineEdit->setPlaceholderText("请输入用户uid");
    ui->lineEdit_2->setPlaceholderText("请输入密码");
    
    // 设置 ToolButton样式
    QString style_tool =
        "QToolButton {"
        "    background-color: white !important;"
        "    padding: 5px;"
        "}";
    ui->toolButton->setStyleSheet(style_tool);
    ui->toolButton->setCheckable(true);
    ui->toolButton->setChecked(false);
    ui->toolButton->setIcon(QIcon(":/closeEyes"));
    
    // 设置pushButton样式
    QString style_push =
        "#pushButton {"
        "   background: rgba(0, 0, 0, 0.6); "
        "   border-radius: 10px; "
        "   border: 1px solid rgba(255, 255, 255, 0.5); "
        "   color: white; "
        "   padding: 8px 20px; "
        "}"
        "#pushButton:hover {"
        "    background: rgba(255, 255, 255, 0.3); "
        "}"
        "#pushButton:pressed {"
        "   padding: 7px 3px 3px 7px;"
        "   border: 1px solid darkgray;"
        "}";
    ui->pushButton->setStyleSheet(style_push);
}

Widget::~Widget() { delete ui; }

void Widget::resizeEvent(QResizeEvent* event) {
    ui->frame->setGeometry(0, 0, event->size().width(), event->size().height());
}

// 控制密码显示
void Widget::on_toolButton_clicked() {
    if (ui->toolButton->isCheckable() == false) {
        ui->toolButton->setIcon(QIcon(":/openEyes"));

        ui->toolButton->setCheckable(true);
        ui->lineEdit_2->setEchoMode(QLineEdit::Normal);
    } else {
        ui->toolButton->setIcon(QIcon(":/closeEyes"));

        ui->toolButton->setCheckable(false);
        ui->lineEdit_2->setEchoMode(QLineEdit::Password);
    }
}

绘图

有时候Qt已有的控件无法满足我们的需求,为了应对这种情况,就需要用到绘图,自己实现满足要求的控件

核心类:

说明
QPainter "绘画者" 或者 "画家"。 用来绘图的对象,提供了一系列 drawXXX 方法,可以允许我们绘制各种图形。
QPaintDevice "画板"。 描述了 QPainter 把图形画到哪个对象上。像咱们之前用过的 QWidget 也是一种 QPaintDevice(QWidget 是 QPaintDevice 的子类)。
QPen "画笔"。 描述了 QPainter 画出来的线是什么样的。
QBrush "画刷"。 描述了 QPainter 填充一个区域是什么样的。

画图的相关操作,一般不放到狗杂函数中进行。

Qt提供了一个**paintEvent**事件处理函数,在这里进行绘图调用 。当QpaintEvent事件触发的时候,事件函数就会执行

  • 控件首次创建的时候
  • 空间被遮挡,然后被解除遮挡的时候
  • 窗口被最小化,然后被还原的时候
  • 控件大小发生改变的时候
  • 主动在代码中调用repaint或者update的时候

绘制形状

线段

cpp 复制代码
void QPainter::drawLine(int x1, int y1, int x2, int y2)
void QPainter::drawLine(const QPoint &p1, const QPoint &p2)

指定起点和终点的横纵坐标,绘制线段

矩形

cpp 复制代码
void QPainter::drawRect(int x, int y, int width, int height)

指定左上角坐标,指定长宽

cpp 复制代码
void QPainter::drawEllipse(int x, int y, int width, int height)

指定圆心横纵坐标,并确定外接矩形的高和宽

文字

cpp 复制代码
void QPainter::drawText(int x, int y, const QString &text)
void QPainter::drawText(const QPoint &position, const QString &text)
  • x:是文字的最左侧位置
  • y:是文字的基线位置。四线格的第三根线

例如:

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

#include <QBrush>
#include <QPainter>
#include <QPen>

#include "./ui_widget.h"

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

Widget::~Widget() { delete ui; }

void Widget::paintEvent(QPaintEvent *evnet) {
    QPainter painter(this);

    QPen pen;
    pen.setColor("red");                   // 设置画笔颜色
    pen.setWidth(2);                       // 设置画笔宽度
    pen.setStyle(Qt::PenStyle::DashLine);  // 设置画笔类型

    painter.setPen(pen);  // 讲画笔设置给画家

    painter.drawLine(10, 10, 88, 88);  // 画线段

    painter.setFont(QFont("微软雅黑", 20));   // 设置画家字体
    painter.drawText(300, 50, "HELLO WORD");  // 写字

    // 设置径向渐变画刷
    QLinearGradient gradient(100, 100, 500, 500);
    gradient.setColorAt(0, Qt::red);
    gradient.setColorAt(1, Qt::blue);
    QBrush brush(gradient);  // 直接用渐变初始化画刷(无需额外设置样式)

    painter.setBrush(brush);  // 讲画刷设置给画家

    painter.drawEllipse(100, 100, 300, 300);  // 以 (100, 100)半径500px画圆

    // 设置
    brush.setColor("white");
    brush.setStyle(Qt::HorPattern);
    painter.setBrush(brush);  // 讲画刷设置给画家

    // 画矩形
    painter.drawRect(400, 350, 200, 200);
}

效果:


图片

Qt 提供了四个类来处理图像数据:QImage、QPixmap、QBitmap 和 QPicture,它们都是常用的绘图设备。

  • QImage 主要用来进行 I/O 处理 ,它对 I/O 处理操作进行了优化,而且可以用来直接访问和操作像素

  • QPixmap 主要用来在屏幕上显示图像,它对在屏幕上显示图像进行了优化;

  • QBitmap 是 QPixmap 的子类,用来处理颜色深度为 1 的图像,即只能显示黑白两种颜色

  • QPicture 用来记录并重演 QPainter 命令

这里主要介绍QPixmap

cpp 复制代码
void QPainter::drawPixmap(int x, int y, const QPixmap &pixmap)
void QPainter::drawPixmap(int x, int y, int width, int height, const QPixmap &pixmap)
  • xy开始,绘制图片
  • 并将图片绘制成大小width * height

例如:

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

#include <QBrush>
#include <QPixmap>

#include "./ui_widget.h"

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

Widget::~Widget() { delete ui; }

void Widget::paintEvent(QPaintEvent *evnet) {
    QPainter painter(this);

    QPixmap map(":/photo");

    painter.drawPixmap(20, 20, 800, 600, map);
}

效果:

如果想要旋转图像,以旋转180度为例,操作步骤如下:

  1. 保存当前坐标系状态

    cpp 复制代码
    painter.save();
  2. 旋转坐标轴(旋转画家)

    cpp 复制代码
    painter.rotate(180);
  3. 移动坐标原点(将坐标原点从左上角移动到右下角)

    cpp 复制代码
    painter.translate(-this->geometry().width(), -this->geometry().height());
  4. 绘制图像

    cpp 复制代码
    painter.drawPixmap(20, 20, 800, 600, map);
  5. 恢复坐标系状态

    cpp 复制代码
    painter.restore();
相关推荐
ajassi20002 小时前
开源 C++ QT Widget 开发(七)线程--多线程及通讯
linux·c++·qt·开源
bdgtd881782 小时前
动态修补C扩展模块的函数指针有哪些风险?安全的修补方案是什么?
c语言·开发语言·安全
mit6.8242 小时前
8.27 网格memo
c++·算法
jeffery8923 小时前
4056:【GESP2403八级】接竹竿
数据结构·c++·算法
luquinn3 小时前
实现统一门户登录跳转免登录
开发语言·前端·javascript
快乐的划水a5 小时前
解释器模式及优化
c++·设计模式·解释器模式
##学无止境##5 小时前
解锁Java分布式魔法:CAP与BASE的奇幻冒险
java·开发语言·分布式
岁忧5 小时前
(LeetCode 每日一题) 498. 对角线遍历 (矩阵、模拟)
java·c++·算法·leetcode·矩阵·go
做一位快乐的码农5 小时前
基于Spring Boot的旅行足迹分享社区的设计与实现/基于java的在线论坛系统
java·开发语言·spring boot