Qt加载.css/.qss文件设置控件的QSS样式(支持程序运行时修改且立即生效类似换肤效果)

初学Qt时要想通过QSS修改控件QWidget,QPushButton等原生基础控件的样式,一般都是直接在.ui文件中直接添加qss,或者在代码中通过setStyleSheet(QString qss)来设置。当程序很大时,很多地方需要复用样式时会非常麻烦,qss写的到处都是,极难维护(如下图所示两种常见初学方式)。

为了更好地管理样式,提高复用率,应该把QSS样式写在一个个文件中(文件后缀是.css或者.qss都可以,但是建议保存为.css文件好点,因为Notepad++可以进行语法识别高亮提醒,另外样式相关的文件编码最好是UTF-8带BOM),程序初始化时统一加载到主程序中,这样所有控件都会自动继承,且通过属性过滤器决定哪个控件生效(如下图)。

这样通过读取 “StyleList.txt” 文件可知有多少个css文件可以加载,读取所有文件内容拼接成一个超长QString再使用setStyleSheet(QString qss)来加载。期间也可以监控这些文件的内容变化,一旦有内容更新马上重新加载。

举例,我一个测试程序的exe文件在bin目录下,bin同级目录下有res/QSS来存放QSS样式相关文件

创建一个类 “QssLoadTool” 用于加载,监控这些文件:

qssloadtool.h

#ifndef QSSLOADTOOL_H
#define QSSLOADTOOL_H

#include <QObject>
#include <QFile>
#include <QFileSystemWatcher>


class QssLoadTool : public QObject
{
    Q_OBJECT
public:
    explicit QssLoadTool(QObject *parent = nullptr);

    // 设置qss样式文件的根文件(根文件记录了需要加载的所有qss样式文件名)
    static void setQssFileListRootFile(const QString &QssRootFile);
    static QString getQssFileListRootFile();

    // 加载所有qss文件刷新程序控件样式
    static void LoadQss2RefreshStyle();

    // 监控qss相关文件,发送修改就重新加载(在main调用一次即可)
    static void WatchQSSFileChange(QFileSystemWatcher *FileWatcher);

private :

    // 本程序所需qss样式文件的根文件
    static QString m_QssRootFile;

    // 程序运行位置
    static QString m_currentPath;
};


#endif // QSSLOADTOOL_H

qssloadtool.cpp

#include "qssloadtool.h"
#include <QDebug>
#include <QApplication>

QString QssLoadTool::m_QssRootFile = "";

QssLoadTool::QssLoadTool(QObject *parent) : QObject(parent)
{
    // 获取应用程序当前路径
    m_currentPath = QCoreApplication::applicationDirPath();
}

void QssLoadTool::setQssFileListRootFile(const QString &QssRootFile)
{
    m_QssRootFile = QssRootFile;
}

QString QssLoadTool::getQssFileListRootFile()
{
    return m_QssRootFile;
}

void QssLoadTool::LoadQss2RefreshStyle()
{
    if(m_QssRootFile.isEmpty())
    {
        qDebug() << "未设置qss样式文件的根文件:" << m_QssRootFile;
        return;
    }

    qDebug() << __FUNCTION__ << "qss样式发送变更,正在重新加载...";

    QFile file(m_QssRootFile);
    if (file.open(QIODevice::ReadOnly))
    {

       QString style = file.readAll();
       file.close();

       QStringList styleList = style.split("\n");
       style.clear();
       QString path = "";
       for(const QString &qssfile : styleList)
       {
           path = m_currentPath + "/../res/QSS/" + qssfile;

           file.setFileName(path.trimmed());
           if(file.open(QIODevice::ReadOnly))
           {
               style = style + file.readAll().trimmed();
               file.close();
           }
           else
           {
               qDebug() << "打开文件失败! ---> " << path;
           }
       }

       qobject_cast<QApplication*>(QApplication::instance())->setStyleSheet(style);

    }
}

void QssLoadTool::WatchQSSFileChange(QFileSystemWatcher *FileWatcher)
{
    if(m_QssRootFile.isEmpty())
    {
        qDebug() << "未设置qss样式文件的根文件:" << m_QssRootFile;
        return;
    }

    FileWatcher->addPath(m_QssRootFile);

    qDebug() << "监控qss样式文件的根文件:" << m_QssRootFile;

    QFile file(m_QssRootFile);
    if (file.open(QIODevice::ReadOnly))
    {
       QString files = file.readAll();
       file.close();
       QStringList fileList = files.split("\n");
       QString path = "";
       for(const QString &qssfile : fileList)
       {
           path = m_currentPath + "/../res/QSS/" + qssfile;
           FileWatcher->addPath(path.trimmed());

           qDebug() <<"监控qss样式文件 :" << path.trimmed();
       }
    }

    // 被监控的qss文件发生修改时,马上重新加载所有qss样式文件
    QObject::connect(FileWatcher, &QFileSystemWatcher::fileChanged, [](){
        QssLoadTool::LoadQss2RefreshStyle();
    });
}

main.cpp中使用方式:

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
        
    // exe所在位置
    QString currentPath = QCoreApplication::applicationDirPath();
    
    // 读取所有的qss样式加载到程序中
    QssLoadTool::setQssFileListRootFile(currentPath + "/../res/QSS/StyleList.txt");
    QssLoadTool::LoadQss2RefreshStyle();
    
    // 监控所有样式文件的内容变化(也可以不监控,即接下来的这两句代码不写)
    QFileSystemWatcher fileWatcher;
    QssLoadTool::WatchQSSFileChange(&fileWatcher);

    // 主界面启动
    ProjectMainWindow w;
    w.show();

    return a.exec();
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/509071.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

QA测试开发工程师面试题满分问答3: python的深拷贝和浅拷贝问题

在 Python 中&#xff0c;深拷贝&#xff08;deep copy&#xff09;和浅拷贝&#xff08;shallow copy&#xff09;是用于创建对象副本的两种不同方式。 浅拷贝是创建一个新的对象&#xff0c;该对象与原始对象的内容相同&#xff08;包括内部嵌套对象的引用&#xff09;&…

SBCFormer:能够在单板计算机上以每秒1帧的速度进行全尺寸ImageNet分类的轻量级网络

摘要 https://arxiv.org/ftp/arxiv/papers/2311/2311.03747.pdf 计算机视觉在解决包括智能农业、渔业和畜牧业管理等不同领域的实际问题中变得越来越普遍。这些应用可能不需要每秒处理许多图像帧&#xff0c;因此从业者倾向于使用单板计算机&#xff08;SBCs&#xff09;。尽管…

Pytorch数据结构:Tensor

文章目录 Tensor基础1.1、Tensor的维度&#xff08;Dimensions&#xff09;1.1.1、举例说明1.1.2、高维Tensor 1.2、.dim()和.size()方法1.2.1、.dim()方法1.2.2、.size()方法1.2.3、.shape属性1.2.3、示例代码1.2.3.1、一维Tensor1.2.3.2、二维Tensor1.2.3.3、三维Tensor 1.3、…

【Go】十五、接口、多态、断言

文章目录 1、接口的引入2、接口3、接口的注意点4、多态5、断言6、Type Switch 1、接口的引入 以日常生活中打招呼为例&#xff0c;定义接口规范&#xff0c;各国人为打招呼为具体的实现 package main import "fmt"//接口的定义&#xff1a;定义规则、定义规范&…

存内领域前沿,基于忆阻器的存内计算----浅析忆阻存内计算

目录 一.概念浅析 二.忆阻器的分类 三.基于忆阻器的存内计算原理 四.存内计算的实验研究 一.概念浅析 1.存内计算 存内计算&#xff08;In-Memory Computing&#xff0c;简称 IMC&#xff09;是一种将数据处理和存储紧密结合在一起的计算方式。它的主要思想是在存储设备中直…

Navicat设置mysql权限

新建用户&#xff1a; 注意&#xff1a;如果不生效执行刷新命令:FLUSH PRIVILEGES; 执行后再重新打开查看&#xff1b; 查询权限命令&#xff1a;1234为新建的用户名&#xff0c;localhost为访问的地址 SHOW GRANTS FOR 1234localhost;如果服务器设置服务器权限后可能会出现权…

某东手势验证

地球上最强版&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 成功率99 基本不失败&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 识别速…

ZCMU操作系统课程实验 - 实验1-Linux的使用

登录 1. 打开这个东西 2. 在 文件 - > 打开 中打卡机房里VMOS文件里的这个东东 3. 然后依次操作下去好了&#xff0c;有红色的选项&#xff0c;我都是选的"Do nothing"。完成后就会出现这样一个黑框框。 4. 让你登录。输入&#xff1a;root。密码&…

Excel制作甘特图

使用Excel表格制作甘特图&#xff0c;可根据任务开始时间和结束时间自动计算工时&#xff0c;并自动用指定颜色填充横道图。 1.新建Excel文档&#xff0c;先设置项目基本信息&#xff0c;包括表格名称&#xff0c;这里设置为“**项目甘特图”&#xff1b;然后添加任务序号列&a…

前后端实现下拉框带条件查询(Vue+Java)

目录 前言1. 前端2. 后端 前言 对于该基本知识熟门熟路&#xff0c;对此总结一套自身的模版如下 总体逻辑如下&#xff1a;通过前端下拉框或者其他组合框&#xff08;多选勾选查询&#xff09;&#xff0c;将条件返回给后端处理&#xff0c;后端查询数据库&#xff0c;查询得…

【Servlet】Servlet入门

文章目录 一、介绍二、入门案例导入servlet-api的解决办法 一、介绍 概念&#xff1a;server applet&#xff0c;即&#xff1a;运行在服务器端的小程序 Servlet就是一个接口&#xff0c;定义了Java类被浏览器访问到&#xff08;tomcat识别&#xff09;的规则。 将来我们定义…

bugku-web-速度要快

发现phpsessid 从上述提示 提示发送post请求&#xff0c;并且带有参数margin 发送后发现报文头部有一个字段叫flag&#xff0c;但好像每一次flag都不一样 构建Python脚本 request requests.Session()data {margin:find, } for i in range(50):html request.post(urlhttp:/…

JS详解-class-类的核心语法关于ES6与ES5

class基本核心语法&#xff1a; //定义类class Person{//公有属性(推荐此处定义)name// 定义属性并设置默认值price 100//构造函数constructor(name,age){// 构造函数内部的this实例化对象this.name namethis.age age// 动态添加属性(不推荐&#xff0c;查找麻烦)this.rank …

位运算算法(1)

目录 一、算法简述 191. 位1的个数 一、题目描述 二、思路解析 三、代码 338.比特位计数 一、题目描述 二、思路解析 三、代码 461.汉明距离 一、题目描述 二、思路解析 三、代码 声明&#xff1a;本博客涉及到的三道题均为一种做法的总结&#xff0c;建议先了解做…

ansible-tower安装

特别注意&#xff1a;不需要提前安装ansible&#xff0c;因为ansible tower中的setup.sh脚本会下载对应的ansible版本 ansible tower不支持Ubuntu系统,对cenos系统版本也有一定的限制&#xff0c;建议使用centos7.9。 准备一台全新的机器安装&#xff0c;因为ansible tower需要…

RISC-V/ARM mcu OpenOCD 调试架构解析

Risc-v/ARM mcu OpenOCD 调试架构解析 最近有使用到risc-v的单片机&#xff0c;所以了解了下risc-v单片机的编译与调试环境的搭建&#xff0c;面试时问到risc-v的调试可参看以下内容。 risc-v根据官方的推荐&#xff0c;调试器服务是选择OpenOCD&#xff0c;DopenOCD(开放片上…

2024多功能知识付费源码下载

多功能知识付费源码下载实现流量互导多渠道变现 源码介绍 资源变现类产品的许多优势&#xff0c;并剔除了那些无关紧要的元素&#xff0c;使得本产品在运营和变现能力方面实现了质的飞跃。多领域素材资源知识变现营销裂变独立版本。 支持&#xff1a;视频、音频、图文、文档…

【机器学习】揭秘无监督学习:机器如何自我学习发现数据奥秘

无监督学习&#xff1a;全面解析 引言 在机器学习的众多分支中&#xff0c;无监督学习因其在未标记数据上发现隐藏模式的能力而独树一帜。它不依赖于事先标记的输出&#xff0c;而是通过分析数据本身的结构和分布来揭示内在的关系和分类。本文深入探讨无监督学习的核心概念、…

2013年认证杯SPSSPRO杯数学建模C题(第一阶段)公路运输业对于国内生产总值的影响分析全过程文档及程序

2013年认证杯SPSSPRO杯数学建模 C题 公路运输业对于国内生产总值的影响分析 原题再现&#xff1a; 交通运输作为国民经济的载体&#xff0c;沟通生产和消费&#xff0c;在经济发展中扮演着极其重要的角色。纵观几百年来交通运输与经济发展的相互关系&#xff0c;生产水平越高…

学透Spring Boot 003 —— Spring 和 Spring Boot 常用注解(附面试题和思维导图)

这是 学透 Spring Boot 专栏 的第三篇&#xff0c;欢迎关注我&#xff0c;与我一起学习和探讨 Spring Boot 相关知识&#xff0c;学透 Spring Boot。 从面试题说起 今天我们通过一道和Spring Boot有关的常见面试题入手。 面试题&#xff1a;说说 Spring Boot 中有哪些常用注解…