初学Qt时要想通过QSS修改控件QWidget,QPushButton等原生基础控件的样式,一般都是直接在.ui 文件中直接添加qss,或者在代码中通过setStyleSheet(QString qss) 来设置。当程序很大时,很多地方需要复用样式时会非常麻烦,qss写的到处都是,极难维护(如下图所示两种常见初学方式)。
为了更好地管理样式,提高复用率,应该把QSS样式写在一个个文件中(文件后缀是.css 或者.qss 都可以,但是建议保存为.css文件 好点,因为Notepad++ 可以进行语法识别高亮提醒,另外样式相关的文件编码最好是UTF-8带BOM ),程序初始化时统一加载到主程序中,这样所有控件都会自动继承,且通过属性过滤器决定哪个控件生效(如下图)。
这样通过读取样式根文件“StyleList.txt” 即可知有多少个.css/.qss样式文件可以加载,然后对这些样式文件一个个读取,然后将所有内容拼接成一个超长的QString,再使用setStyleSheet(QString qss) 来设置加载到主程序中。期间也可以选择性使用QFileSystemWatcher 来监控这些样式文件的内容变化,一旦有内容更新会发出信号,然后马上重新加载所有样式。
举例,我一个测试程序的exe文件在bin目录下 ,bin同级目录下有res/QSS 来存放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();
// 设置样式根文件
QssLoadTool::setQssFileListRootFile(currentPath + "/../res/QSS/StyleList.txt");
// 通过上一步设置的样式根文件去加载每一个.css样式文件
QssLoadTool::LoadQss2RefreshStyle();
// 监控所有样式文件的内容变化,一旦发生变化就马上刷新样式并生效
// (如果不想监控,那就接下来的这两句代码不写)
QFileSystemWatcher fileWatcher;
QssLoadTool::WatchQSSFileChange(&fileWatcher);
// 主界面启动
ProjectMainWindow w;
w.show();
return a.exec();
}