動態

詳情 返回 返回

Qt 中實現系統主題感知 - 動態 詳情

【寫在前面】

在現代桌面應用程序開發中,系統主題感知是一項重要的功能,它使得應用程序能夠根據用户的系統主題設置(如深色模式或淺色模式)自動調整其外觀。

Qt 作為一個跨平台的C++圖形用户界面應用程序開發框架,提供了豐富的工具和類來實現這一功能。


【正文開始】

一、使用效果

image

二、系統主題感知助手類(SystemThemeHelper)

SystemThemeHelper類是一個封裝了系統主題感知功能的Qt對象。它主要通過讀取系統設置和監聽系統主題變化來更新應用程序的主題顏色和顏色方案。

  1. 類定義與屬性

    systemthemehelper.h中,SystemThemeHelper類繼承自QObject,並定義了兩個屬性:themeColorcolorScheme。這兩個屬性分別表示當前的主題顏色和顏色方案(深色、淺色或無)。

    class SystemThemeHelper : public QObject
    {
        Q_OBJECT
        Q_PROPERTY(QColor themeColor READ themeColor NOTIFY themeColorChanged)
        Q_PROPERTY(SystemThemeHelper::ColorScheme colorScheme READ colorScheme NOTIFY colorSchemeChanged)
        // ...
    };

    ColorScheme是一個枚舉類,定義了三種顏色方案:NoneDarkLight

  2. 構造函數與析構函數

    SystemThemeHelper的構造函數初始化了一些私有成員變量,並啓動了一個定時器,用於定期更新主題顏色和顏色方案。析構函數則負責清理資源。

    SystemThemeHelper::SystemThemeHelper(QObject *parent)
        : QObject{parent}, d_ptr(new SystemThemeHelperPrivate(this))
    {
        Q_D(SystemThemeHelper);
        d->m_themeColor = getThemeColor();
        d->m_colorScheme = getColorScheme();
        d->m_timer.start(200, this);
        #ifdef Q_OS_WIN
        initializeFunctionPointers();
        #endif
    }
    
    SystemThemeHelper::~SystemThemeHelper()
    {
        // 清理資源
    }
  3. 獲取主題顏色和顏色方案

    getThemeColorgetColorScheme是兩個不可用於綁定的方法,它們立即返回當前的主題顏色和顏色方案,但不會觸發任何更新通知。這兩個方法主要用於快速獲取當前設置,而不關心後續的變化。

    QColor SystemThemeHelper::getThemeColor() const
    {
        Q_D(const SystemThemeHelper);
        #ifdef Q_OS_WIN
        return QColor::fromRgb(d->m_themeColorSettings.value("ColorizationColor").toUInt());
        #endif
    }
    
    SystemThemeHelper::ColorScheme SystemThemeHelper::getColorScheme() const
    {
        Q_D(const SystemThemeHelper);
        #if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
        const auto scheme = QGuiApplication::styleHints()->colorScheme();
        return scheme == Qt::ColorScheme::Dark ? ColorScheme::Dark : ColorScheme::Light;
        #else
        #ifdef Q_OS_WIN
        return !d->m_colorSchemeSettings.value("AppsUseLightTheme").toBool() ? ColorScheme::Dark : ColorScheme::Light;
        #else //linux
        const QPalette defaultPalette;
        const auto text = defaultPalette.color(QPalette::WindowText);
        const auto window = defaultPalette.color(QPalette::Window);
        return text.lightness() > window.lightness() ? ColorScheme::Dark : ColorScheme::Light;
        #endif // Q_OS_WIN
        #endif // QT_VERSION
    }
  4. 更新主題顏色和顏色方案

    themeColorcolorScheme是兩個可用於綁定的方法,它們返回當前的主題顏色和顏色方案,並在值發生變化時發出通知。這兩個方法內部調用了私有成員函數的更新邏輯。

    QColor SystemThemeHelper::themeColor()
    {
        Q_D(SystemThemeHelper);
        d->_updateThemeColor();
        return d->m_themeColor;
    }
    
    SystemThemeHelper::ColorScheme SystemThemeHelper::colorScheme()
    {
        Q_D(SystemThemeHelper);
        d->_updateColorScheme();
        return d->m_colorScheme;
    }
  5. 設置窗口標題欄模式

    setWindowTitleBarMode方法允許設置窗口標題欄的模式(深色或淺色)。這個方法在Windows平台上通過調用 DWM API 實現,而在其他平台上則不支持。

    bool SystemThemeHelper::setWindowTitleBarMode(QWindow *window, bool isDark)
    {
        #ifdef Q_OS_WIN
        return bool(pDwmSetWindowAttribute ? !pDwmSetWindowAttribute(HWND(window->winId()), 20, &isDark, sizeof(BOOL)) : false);
        #else
        return false;
        #endif //Q_OS_WIN
    }
  6. 定時器事件處理

    timerEvent方法是一個虛函數,用於處理定時器事件。它定期調用更新函數來檢查主題顏色和顏色方案是否發生變化,並在變化時發出通知。

    void SystemThemeHelper::timerEvent(QTimerEvent *)
    {
        Q_D(SystemThemeHelper);
        d->_updateThemeColor();
        d->_updateColorScheme();
    }

三、實現細節

SystemThemeHelperPrivateSystemThemeHelper的私有實現類,它封裝了所有的實現細節和狀態變量。這個類主要負責讀取系統設置、更新主題顏色和顏色方案,併發出通知。

  1. 構造函數與成員變量

    SystemThemeHelperPrivate的構造函數接收一個指向SystemThemeHelper的指針,並初始化成員變量。成員變量包括主題顏色、顏色方案、定時器和一些平台特定的設置對象。

    SystemThemeHelperPrivate::SystemThemeHelperPrivate(SystemThemeHelper *q)
        : q_ptr(q)
    {
        // 初始化成員變量
    }
  2. 更新函數

    _updateThemeColor_updateColorScheme是兩個更新函數,它們檢查當前的主題顏色和顏色方案是否發生變化,並在變化時更新成員變量併發出通知。

    void SystemThemeHelperPrivate::_updateThemeColor()
    {
        Q_Q(SystemThemeHelper);
        auto nowThemeColor = q->getThemeColor();
        if (nowThemeColor != m_themeColor) {
            m_themeColor = nowThemeColor;
            emit q->themeColorChanged();
        }
    }
    
    void SystemThemeHelperPrivate::_updateColorScheme()
    {
        Q_Q(SystemThemeHelper);
        auto nowColorScheme = q->getColorScheme();
        if (nowColorScheme != m_colorScheme) {
            m_colorScheme = nowColorScheme;
            emit q->colorSchemeChanged();
        }
    }
  3. 平台特定的實現

    在Windows平台上,SystemThemeHelperPrivate使用QSettings來讀取系統主題設置,並使用DWM API來設置窗口標題欄的模式。這些實現細節被封裝在條件編譯塊中,以確保跨平台的兼容性。

    #ifdef Q_OS_WIN
    QSettings m_themeColorSettings{QSettings::UserScope, "Microsoft", "Windows\\DWM"};
    QSettings m_colorSchemeSettings{QSettings::UserScope, "Microsoft", "Windows\\CurrentVersion\\Themes\\Personalize"};
    static DwmSetWindowAttributeFunc pDwmSetWindowAttribute = nullptr;
    // ...
    static inline bool initializeFunctionPointers()
    {
        // 初始化DWM API函數指針
    }
    #endif //Q_OS_WIN

四、如何使用

C++:

    SystemThemeHelper *helper = new SystemThemeHelper;
    QObject::connect(helper, &SystemThemeHelper::themeColorChanged, [helper]{
        qDebug() << helper->getThemeColor();
    });
    QObject::connect(helper, &SystemThemeHelper::colorSchemeChanged, [helper]{
        qDebug() << helper->getColorScheme();
    });

Qml:

import QtQuick 2.15
import QtQuick.Window 2.15

import DelegateUI.Utils 1.0

Window {
    id: window
    width: 640
    height: 480
    visible: true
    title: qsTr("SystemThemeHelper Test - ") + (themeHelper.colorScheme == SystemThemeHelper.Dark ? "Dark" : "Light")
    color: themeHelper.colorScheme == SystemThemeHelper.Dark ? "black" : "white"

    Behavior on color { ColorAnimation { } }

    SystemThemeHelper {
        id: themeHelper
        onThemeColorChanged: {
            console.log("onThemeColorChanged:", themeColor);
        }
        onColorSchemeChanged: {
            setWindowTitleBarMode(window, themeHelper.colorScheme == SystemThemeHelper.Dark)
            console.log("onColorSchemeChanged:", colorScheme);
        }
        Component.onCompleted: {
            console.log("onColorSchemeChanged:", colorScheme);
            setWindowTitleBarMode(window, themeHelper.colorScheme == SystemThemeHelper.Dark)
        }
    }

    Text {
        anchors.centerIn: parent
        text: qsTr("主題顏色")
        font.family: "微軟雅黑"
        font.pointSize: 32
        color: themeHelper.themeColor
    }
}

---

【結語】

通過SystemThemeHelper類,我們可以在 Qt 應用程序中實現系統主題感知功能。

這個類封裝了讀取系統設置、更新主題顏色和顏色方案以及發出通知的邏輯,使得我們可以輕鬆地根據系統主題變化來調整應用程序的外觀。

此外,通過條件編譯和平台特定的實現,還確保了跨平台的兼容性。

最後:項目鏈接(多多star呀..⭐_⭐):

Github: https://github.com/mengps/QmlControls

Gitee: https://gitee.com/MenPenS/QmlControls

user avatar jiuliangxiaodeshuanggang 頭像
點贊 1 用戶, 點贊了這篇動態!
點贊

Add a new 評論

Some HTML is okay.