CMake实战篇1-主题管理库

CMake实战篇1-主题管理库


《宿建德江》· 孟浩然

移舟泊烟渚,日暮客愁新。
野旷天低树,江清月近人。

33

在多 Qt 项目开发场景中,重复开发主题切换功能不仅耗时,还易导致代码不一致。为此,我们基于 CMake 构建了一款可直接复用的 Qt 主题管理器动态库,无需重复造轮子,让多个项目快速具备动态主题切换能力。

本文由 豆包AI 协助完成

一、核心用法:开箱即用的主题管理

这款动态库的设计核心是 “极简接入、灵活使用”,无需深入理解底层实现,仅需两步即可集成到任意 Qt 项目中。

1.1 快速集成

将编译好的动态库文件(Windows 下为.dll/.lib,Linux 下为.so,macOS 下为.dylib)和头文件拷贝到项目指定目录,在项目的 CMakeLists.txt 中仅需几行配置即可完成依赖引入:

find_package(UThemeManager REQUIRED)
target_link_libraries(${PROJECT_NAME} PRIVATE Hakuon::UThemeManager)

1.2 简单调用

在项目代码中,通过全局单例即可实现主题切换,无需额外初始化,核心调用仅需一行代码:

void UThemeManager::applyTheme(UThemeManager::ETheme theme)
{
    QString typeName;
    switch (theme)
    {
    case ET_DarkBlue:
        typeName = "darkblue";
        break;
    case ET_Light:
        typeName = "lightblue";
        break;
    default:
        break;
    }

    applyTheme(QString(":/res/%1.qss").arg(typeName));
}

1.3 效果预览

t_1

二、核心优势:适配多场景的复用价值

2.1 跨项目复用,提效降本

将主题管理逻辑抽离为独立动态库,所有 Qt 项目无需重复编写样式表加载、主题切换代码,接入成本仅需几分钟,大幅减少重复开发工作量,且能保证所有项目的主题切换逻辑一致。

2.2 跨平台兼容,适配性强

基于 CMake 构建,天然支持 Windows、Linux、macOS 三大平台编译,编译产物可直接在对应平台使用,无需针对不同系统做额外适配,降低跨平台开发复杂度。

2.3 接口简洁,低学习成本

仅暴露核心的主题切换、自定义样式加载接口,无需掌握复杂的配置规则,即使是新手也能快速上手,且单例模式设计符合 Qt 项目的全局使用习惯。

2.4 灵活扩展,适配个性化需求

支持加载自定义 QSS 文件,不仅局限于内置的亮色 / 暗色主题,可根据不同项目的视觉风格,灵活接入专属主题样式,满足个性化需求。

三、使用注意事项与不足

3.1 运行时依赖

动态库需与项目运行环境匹配(如 Qt 版本、编译器、系统架构),若项目更换 Qt 版本或编译环境,需重新编译动态库,否则可能出现兼容性问题。

3.2 样式表冲突风险

若项目自身已设置局部样式表,可能与主题管理器的全局样式表产生冲突,需在使用时做好样式表的层级规划,避免样式覆盖异常。

3.3 调试难度略增

相较于项目内的本地代码,动态库的调试需要依赖调试符号文件(如 Windows 下的 PDB 文件),若缺失则难以定位库内的问题,增加调试成本。

3.4 功能轻量化,适配场景有限

当前版本仅聚焦核心的主题切换能力,未支持主题变量、多主题预览、样式缓存等进阶功能,若项目有复杂的主题管理需求,需在此基础上扩展。

四、总结

这款基于 CMake 的 Qt 主题管理器动态库,以 “复用” 为核心,解决了多 Qt 项目主题切换逻辑冗余的问题,凭借简洁的接入方式和跨平台特性,能快速落地到各类 Qt 项目中。虽然存在少量使用限制,但通过规范使用流程(如统一 Qt 编译环境、规划样式表层级)可有效规避,是提升多 Qt 项目开发效率的实用工具。

五.源码实现

#ifndef UTHEMEMANAGER_H
#define UTHEMEMANAGER_H

/*! ===================================================================
   @class      UThemeManager;
   @brief      用于主题管理, 支持不同的主题皮肤;
   @note       note;
   @author     Hakuon
   @date       2025-11-20
======================================================================*/

#include <QObject>
#include <QtCore/qglobal.h>


#ifndef BUILD_STATIC
# if defined(UTHEMEMANAGER_LIBRARY)
#  define UTHEMEMANAGER_EXPORT Q_DECL_EXPORT
# else
#  define UTHEMEMANAGER_EXPORT Q_DECL_IMPORT
# endif
#else
# define HAKUWIDGETS_EXPORT
#endif

class UTHEMEMANAGER_EXPORT UThemeManager
{
public:
    enum ETheme {
        ET_DarkBlue = 0,
        ET_Light,

        ET_None     = 100,
    };
    ~UThemeManager();

    static UThemeManager* instance();

    void applyTheme(ETheme theme = ET_DarkBlue);
    void applyTheme(const QString& path);

public:
    explicit UThemeManager();
};

#endif // UTHEMEMANAGER_H
#include "UThemeManager.h"

#include <QApplication>
#include <QDebug>
#include <QFile>


UThemeManager::UThemeManager()
{
}

UThemeManager::~UThemeManager()
{
}

UThemeManager *UThemeManager::instance()
{
    static UThemeManager s_utm_inst;
    return &s_utm_inst;
}

void UThemeManager::applyTheme(UThemeManager::ETheme theme)
{
    QString typeName;
    switch (theme)
    {
    case ET_DarkBlue:
        typeName = "darkblue";
        break;
    case ET_Light:
        typeName = "lightblue";
        break;
    default:
        break;
    }

    applyTheme(QString(":/res/%1.qss").arg(typeName));
}

void UThemeManager::applyTheme(const QString &path)
{
    QFile file(path);
    if (file.open(QIODevice::Text | QIODevice::ReadOnly))
    {
        qApp->setStyleSheet(file.readAll());
    }
    file.close();

    qDebug() << ((file.exists()) ? u8"加载主题管理器成功. ^_^" : u8"主题文件未找到. 加载失败 >_<");
}

CMakeLists.txt文件编写

cmake_minimum_required(VERSION 3.15)

# ------------------------------------
# 设置项目的基本信息
# ------------------------------------
project(UThemeManager
    VERSION     1.0.0
    LANGUAGES   CXX
    DESCRIPTION "A Custom Theme Manager"
)

# -------------------------------------------------
function(group_files_by_folder out_var folder_path file_glob_pattern group_name)
    file(GLOB FILE_LIST "${folder_path}/${file_glob_pattern}")

    if(FILE_LIST)
        source_group("${group_name}" FILES ${FILE_LIST})
        set(${out_var} ${FILE_LIST} PARENT_SCOPE)
    endif()
endfunction()
# -------------------------------------------------


# ------------------------------------
# 文件分组
# ------------------------------------
group_files_by_folder(HEADERS ${CMAKE_CURRENT_SOURCE_DIR} "*.h"   "Include")
group_files_by_folder(SOURCES ${CMAKE_CURRENT_SOURCE_DIR} "*.cpp" "Src")
group_files_by_folder(RES     ${CMAKE_CURRENT_SOURCE_DIR} "*.qrc" "res")

set(EXPORT_HEADERS
    UThemeManager.h
)


# ------------------------------------
# 生成库
# ------------------------------------
add_library(${PROJECT_NAME} SHARED ${HEADERS} ${SOURCES} ${RES})

# ------------------------------------
# 区分构建和install后的头文件引用
# ------------------------------------
target_include_directories(${PROJECT_NAME}
    PUBLIC
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
    $<INSTALL_INTERFACE:include/${PROJECT_NAME}>
)

set_target_properties(${PROJECT_NAME} PROPERTIES
    DEBUG_POSTFIX               "d"
    PUBLIC_HEADER               "${EXPORT_HEADERS}"

    AUTORCC                     ON
    AUTOUIC                     ON
    AUTOMOC                     ON
)

target_compile_definitions(${PROJECT_NAME} PRIVATE UTHEMEMANAGER_LIBRARY)

find_package(Qt5 COMPONENTS Core Widgets REQUIRED)
target_link_libraries(${PROJECT_NAME} PUBLIC Qt5::Core Qt5::Widgets)


# ------------------------------------
# 安装属性
# ------------------------------------
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)

# 安装目标文件
install(TARGETS ${PROJECT_NAME}
        EXPORT ${PROJECT_NAME}Targets
        PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}
        RUNTIME       DESTINATION ${CMAKE_INSTALL_BINDIR}
        LIBRARY       DESTINATION ${CMAKE_INSTALL_LIBDIR}
        ARCHIVE       DESTINATION ${CMAKE_INSTALL_LIBDIR}
)

# 导出目标信息
install(EXPORT ${PROJECT_NAME}Targets
        FILE ${PROJECT_NAME}Targets.cmake
        NAMESPACE Hakuon::
        DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}
)

# 生成配置文件
configure_package_config_file(
        "${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}Config.cmake.in"
        "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
        INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}
)

write_basic_package_version_file(
        "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
        VERSION ${PROJECT_VERSION}
        COMPATIBILITY SameMajorVersion
)

# 安装 config 文件
install(FILES
        "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
        "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
        DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}
)

UThemeManagerConfig.cmake.in编写

# @PACKAGE_INIT@ 必须放在开头
@PACKAGE_INIT@

# 设置版本变量(可选但推荐), set(MyMath_VERSION 2.3.1)
set(@PROJECT_NAME@_VERSION @PROJECT_VERSION@)

# 包含目标文件(核心), 一般值得是 lib\cmake\MyMath
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")

# 提供传统变量(向后兼容),set(MyMath_LIBRARIES MyMath::MyMath)
set(@PROJECT_NAME@_LIBRARIES @PROJECT_NAME@::@PROJECT_NAME@)

# 检查必需组件(如果有)
check_required_components(@PROJECT_NAME@)
posted on 2025-12-14 23:16  Hakuon  阅读(1)  评论(0)    收藏  举报