QT開發(六十八)——QSS應用

QT開發(六十八)——QSS應用

本博文轉載自一去丶二三裏的博客:http://blog.csdn.net/liang19890820css

1、語法高亮設置

    Qt Creator中默認狀況下打開qss文件(*.qss)不會高亮顯示,須要手動配置,讓其更符合閱讀習慣,以更炫麗的方式展現代碼片斷。app

配置流程以下:ide

    A、進入:工具 -> 選項 -> 環境 -> MIME 類型。函數

    B、在【已註冊的MIME類型】處輸入「text/css」能夠快速定位,而後在【詳情】中的「模式」處添加 *.qss,即將原來的「模式」改成:*.css;*.CSSL;*.qss。工具

注意:中間用分號(;)分隔ui

wKioL1h3MLazus55AAB8TRdrkAs237.png

效果以下:this

wKiom1h3MNejeEi9AABIOKASKwM740.png

2、動態屬性

一、自定義屬性

    爲了用戶界面外觀的動態變化,屬性選擇器能夠與動態屬性組合使用。動態屬性在QT4.2中引入,容許爲編譯時不存在的QObject屬性分配屬性值。即:若是爲QObject設置一個urgent屬性爲true,該屬性將跟隨該類,但不會爲urgent屬性包含一個Q_PROPERTY宏。url

    建立樣式選擇器依賴於動態屬性,例如:urgent,能夠用一個很是動態的方式凸顯用戶界面。例如:spa

QLineEdit[urgent=true] {.net

  color: red;

}

    使用這種方式有侷限性。最主要的是當一個屬性值變化時,所引用的樣式不會自動更新。相反地,必須手動觸發更新纔會生效。

    unpolish()用於清理以前的樣式,而polish()則用於添加新的樣式。

lineEdit->setProperty("urgent", true);

lineEdit->style()->unpolish(lineEdit);

lineEdit->style()->polish(lineEdit);

    必須在組件的樣式中使用,QStyle::polish既接受QWidge也接受QApplication做爲參數。

二、實例

    自定義標題欄中的最大化/還原按鈕爲例,進行切換。

wKiom1h3MRHBOysGAAAIRnyXx3k258.pngwKioL1h3MRKw4DRuAAAIgQP_mmI528.png

void TitleBar::updateMaximize()
{
    QWidget *pWindow = this->window();
    if (pWindow->isTopLevel())
    {
        bool bMaximize = pWindow->isMaximized();
        m_pMaximizeButton->setToolTip(bMaximize ? tr("Restore") : tr("Maximize"));
        m_pMaximizeButton->setProperty("maximizeProperty", bMaximize ? "restore" : "maximize");
 
        // 手動更新樣式
        m_pMaximizeButton->style()->unpolish(m_pMaximizeButton);
        m_pMaximizeButton->style()->polish(m_pMaximizeButton);
        m_pMaximizeButton->update();
        //m_pMaximizeButton->setStyle(QApplication::style());
    }
}

QSS:

QPushButton#maximizeButton[maximizeProperty="maximize"] {
        border-radius: none;
        border-bottom-left-radius: 4px;
        border-bottom-right-radius: 4px;
        background: rgb(50, 50, 50);
        p_w_picpath: url(:/Images/maximize);
}
QPushButton#maximizeButton[maximizeProperty="maximize"]:hover {
        background: rgb(60, 60, 60);
        p_w_picpath: url(:/Images/maximizeHover);
}
QPushButton#maximizeButton[maximizeProperty="maximize"]:pressed {
        background: rgb(55, 55, 55);
        p_w_picpath: url(:/Images/maximizePressed);
}
QPushButton#maximizeButton[maximizeProperty="restore"] {
        border-radius: none;
        border-bottom-left-radius: 4px;
        border-bottom-right-radius: 4px;
        background: rgb(50, 50, 50);
        p_w_picpath: url(:/Images/restore);
}
QPushButton#maximizeButton[maximizeProperty="restore"]:hover {
        background: rgb(60, 60, 60);
        p_w_picpath: url(:/Images/restoreHover);
}
QPushButton#maximizeButton[maximizeProperty="restore"]:pressed {
        background: rgb(55, 55, 55);
        p_w_picpath: url(:/Images/restorePressed);

3、原始屬性

    任何可被識別的Q_PROPERTY均可以使用qproperty-語法設置。

    Q_PROPERTY定義的屬性經過QSS按照qproperty-<property name>語法的方式設置

    QLabel的屬性以下:

class Q_WIDGETS_EXPORT QLabel : public QFrame
{
    ...
    Q_PROPERTY(QPixmap pixmap READ pixmap WRITE setPixmap)
    Q_PROPERTY(bool scaledContents READ hasScaledContents WRITE setScaledContents)
    ...
};
class Q_WIDGETS_EXPORT QWidget : public QObject, public QPaintDevice
{
    ...
    Q_PROPERTY(QSize minimumSize READ minimumSize WRITE setMinimumSize)
    Q_PROPERTY(QSize maximumSize READ maximumSize WRITE setMaximumSize)
    ...
};

    QLabel的屬性有minimumSizemaximumSizepixmapscaledContents

QSS文件:

QLabel#customLabel {
        qproperty-minimumSize: 100px 100px;
        qproperty-maximumSize: 100px 100px;
        qproperty-pixmap: url(:/Images/logo);
        qproperty-scaledContents: true;
}
 
QPushButton#customButton {
        qproperty-text: "Click Me";
        qproperty-icon: url(:/Images/logo);
        qproperty-iconSize: 20px 20px;
}
 
QGroupBox#customGroupBox {
        qproperty-title: "GroupBox";
}

源碼:

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    QLabel *pLabel = new QLabel(this);
    QPushButton *pButton = new QPushButton(this);
    QGroupBox *pGroupBox = new QGroupBox(this);
 
    pLabel->setObjectName("customLabel");
    pButton->setObjectName("customButton");
    pGroupBox->setObjectName("customGroupBox");
 
    QVBoxLayout *pLayout = new QVBoxLayout();
    pLayout->addStretch();
    pLayout->addWidget(pLabel, 0, Qt::AlignCenter);
    pLayout->addWidget(pButton);
    pLayout->addStretch();
    pLayout->setSpacing(10);
    pLayout->setContentsMargins(10, 10, 10, 10);
 
    pGroupBox->setLayout(pLayout);
}

Main.cpp文件:

#include "Widget.h"
#include <QApplication>
#include <QFile>
 
class CommonHelper
{
public:
    static void setStyle(const QString &style)
    {
        QFile qss(style);
        qss.open(QFile::ReadOnly);
        qApp->setStyleSheet(qss.readAll());
        qss.close();
    }
};
 
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    CommonHelper::setStyle(":/style.qss");
    Widget w;
    w.show();
 
    return a.exec();
}

以上的解決方法將界面樣式與業務邏輯進行了分離,效果與以下代碼相同:

pLabel->setPixmap(QPixmap(":/Images/logo"));
pLabel->setMinimumSize(100, 100);
pLabel->setMaximumSize(100, 100);
pLabel->setScaledContents(true);
 
pButton->setIcon(QIcon(":/Images/logo"));
pButton->setIconSize(QSize(20, 20));
pButton->setText("Click Me");
 
pGroupBox->setTitle("GroupBox");

4、自定義屬性

1、自定義屬性

    QAbstractItemModel、QAbstractItemDelegate均繼承自QObject,而QSS只能用於QWidget及其子類,動態獲取樣式屬性值方法以下:

    A、建立一個從QWidget繼承的專用類StyledWidget。

    B、StyledWidget添加自定義屬性,並使用Q_PROPERTY聲明

    C、自定義QSS,使用自定義屬性,語法:qproperty-<property name>

    其中,Q_PROPERTY聲明有如下要求:

    READ getFunction
    用於讀取屬性,使用const限定,返回屬性的類型或者類型的指針或引用。

    WRITE setFunction
    用於設置屬性,參數是一個屬性的類型,或者屬性的const指針或引用,返回

二、應用實例

    建立一個從QWidget繼承的專用類StyledWidget,爲其添加自定義屬性,並使用Q_PROPERTY聲明

StyledWidget.h文件:

#ifndef STYLEDWIDGET_H
#define STYLEDWIDGET_H
 
#include <QWidget>
 
class StyledWidget : public QWidget
{
    Q_OBJECT
    Q_PROPERTY(QColor normalColor READ normalColor WRITE setNormalColor DESIGNABLE true)
    ...
public:
    explicit StyledWidget(QWidget *parent = 0);
    ~StyledWidget();
 
    QColor normalColor() const;
    void setNormalColor(QColor color);
    ...
 
private:
    QColor m_normalColor;
    ...
};
 
#endif // STYLEDWIDGET_H

StyledWidget.cpp文件:

...
QColor StyledWidget::normalColor() const
{
    return m_normalColor;
}
 
void StyledWidget::setNormalColor(QColor color)
{
    m_normalColor = color;
}
...

QSS文件:

StyledWidget {
        qproperty-normalColor: white;
        qproperty-disableColor: gray;
        qproperty-highlightColor: rgb(0, 160, 230);
        qproperty-errorColor: red;
}

使用:

    在須要設置樣式的類中聲明StyledWidget:

class TableModel : public QAbstractTableModel
{
    Q_OBJECT
public:
    ...
    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
    ...
private:
    ...
    StyledWidget m_styledWidget;
};

    使用自定義屬性設置樣式:

QVariant TableModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();
 
    switch (role)
    {
    case Qt::TextColorRole:
    {
        if (index.column() == FILE_NAME_COLUMN)
            return m_styledWidget.normalColor();
 
        if (index.column() == SIZE_COLUMN)
            return m_styledWidget.highlightColor();
 
        if (index.column() == STATUS_COLUMN)
            return m_styledWidget.errorColor();
    }
    ...
    }
    return QVariant();
}

wKioL1h3MT3QloiuAABCTpvStm8315.png

5、QSS文件加載

    QT中對於樣式表的使用,爲了下降耦合性(與邏輯代碼分離),一般會定義一個QSS文件,而後編寫各類組件(QLabel QLineEdit、QPushButton)的樣式,最後使用QApplication進行樣式加載,讓整個應用程序就共享同一個樣式。

一、建立QSS文件

    建立一個後綴名爲qss的文件,例如:style.qss,將其加入資源文件(qrc)中。

二、編寫QSS文件

QLineEdit
{
    border: 1px solid rgb(41, 57, 85);
    border-radius: 3px;
    background: white;
    selection-background-color: green;
    font-size: 14px ;
}

三、QSS文件加載

    爲了便於調用,能夠寫一個靜態加載樣式的函數

#include <QFile>
#include <QApplication>
 
class CommonHelper
{
public:
    static void setStyle(const QString &style)
    {
        QFile qss(style);
        qss.open(QFile::ReadOnly);
        qApp->setStyleSheet(qss.readAll());
        qss.close();
    }
};

    主函數中加載:

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
 
    // 加載QSS樣式
    CommonHelper::setStyle("style.qss");
 
    MainWindow window;
    window.show();
 
    return a.exec();
}

4QSS加載實現原理

    qApp是QCoreApplication的一個單例,而後,將其轉換爲QApplication。

#if defined(qApp)
#undef qApp
#endif
#define qApp (static_cast<QApplication *>(QCoreApplication::instance()))

    QApplication調用setStyleSheet()後全部的組件樣式都改變的主要緣由是調用了setStyle()

void QApplication::setStyle(QStyle *style)
{
    if (!style || style == QApplicationPrivate::app_style)
        return;
 
    QWidgetList all = allWidgets();
 
    // clean up the old style
    if (QApplicationPrivate::app_style)
    {
        if (QApplicationPrivate::is_app_running && !QApplicationPrivate::is_app_closing)
        {
            for (QWidgetList::ConstIterator it = all.constBegin(), cend = all.constEnd(); it != cend; ++it)
            {
                QWidget *w = *it;
                if (!(w->windowType() == Qt::Desktop) &&        // except desktop
                        w->testAttribute(Qt::WA_WState_Polished))
                { // has been polished
                    QApplicationPrivate::app_style->unpolish(w);
                }
            }
        }
        QApplicationPrivate::app_style->unpolish(qApp);
    }
    QStyle *old = QApplicationPrivate::app_style; // save
 
    QApplicationPrivate::overrides_native_style =
            nativeStyleClassName() == QByteArray(style->metaObject()->className());
 
#ifndef QT_NO_STYLE_STYLESHEET
    if (!QApplicationPrivate::styleSheet.isEmpty() && !qobject_cast<QStyleSheetStyle *>(style))
    {
        // we have a stylesheet already and a new style is being set
        QStyleSheetStyle *newProxy = new QStyleSheetStyle(style);
        style->setParent(newProxy);
        QApplicationPrivate::app_style = newProxy;
    }
    else
#endif // QT_NO_STYLE_STYLESHEET
    QApplicationPrivate::app_style = style;
    QApplicationPrivate::app_style->setParent(qApp); // take ownership
 
    // take care of possible palette requirements of certain gui
    // styles. Do it before polishing the application since the style
    // might call QApplication::setPalette() itself
    if (QApplicationPrivate::set_pal)
    {
        QApplication::setPalette(*QApplicationPrivate::set_pal);
    }
    else if (QApplicationPrivate::sys_pal)
    {
        clearSystemPalette();
        initSystemPalette();
        QApplicationPrivate::initializeWidgetPaletteHash();
        QApplicationPrivate::initializeWidgetFontHash();
        QApplicationPrivate::setPalette_helper(*QApplicationPrivate::sys_pal, /*className=*/0, /*clearWidgetPaletteHash=*/false);
    }
    else if (!QApplicationPrivate::sys_pal)
    {
        // Initialize the sys_pal if it hasn't happened yet...
        QApplicationPrivate::setSystemPalette(QApplicationPrivate::app_style->standardPalette());
    }
 
    // initialize the application with the new style
    QApplicationPrivate::app_style->polish(qApp);
 
    // re-polish existing widgets if necessary
    if (QApplicationPrivate::is_app_running && !QApplicationPrivate::is_app_closing)
    {
        for (QWidgetList::ConstIterator it = all.constBegin(), cend = all.constEnd(); it != cend; ++it)
        {
            QWidget *w = *it;
            if (w->windowType() != Qt::Desktop && w->testAttribute(Qt::WA_WState_Polished))
            {
                if (w->style() == QApplicationPrivate::app_style)
                    QApplicationPrivate::app_style->polish(w);                // repolish
#ifndef QT_NO_STYLE_STYLESHEET
                else
                    w->setStyleSheet(w->styleSheet()); // touch
#endif
            }
        }
 
        for (QWidgetList::ConstIterator it = all.constBegin(), cend = all.constEnd(); it != cend; ++it)
        {
            QWidget *w = *it;
            if (w->windowType() != Qt::Desktop && !w->testAttribute(Qt::WA_SetStyle))
            {
                QEvent e(QEvent::StyleChange);
                QApplication::sendEvent(w, &e);
                w->update();
            }
        }
    }
 
#ifndef QT_NO_STYLE_STYLESHEET
    if (QStyleSheetStyle *oldProxy = qobject_cast<QStyleSheetStyle *>(old))
    {
        oldProxy->deref();
    }
    else
#endif
        if (old && old->parent() == qApp)
        {
            delete old;
        }
 
    if (QApplicationPrivate::focus_widget)
    {
        QFocusEvent in(QEvent::FocusIn, Qt::OtherFocusReason);
        QApplication::sendEvent(QApplicationPrivate::focus_widget->style(), &in);
        QApplicationPrivate::focus_widget->update();
    }
}

    主要分爲4步:

    A、清理舊樣式 - unpolish()

    B、初始化新樣式 - polish()

    C、加載新樣式 - polish() + sendEvent()、update()

    D、刪除舊樣式 - delete

    經過調用QWidgetList all = allWidgets()獲取了全部控件的集合,而後利用迭代器QWidgetList::ConstIterator對每個控件進行處理,通 過QApplication::sendEvent()來發送QEvent::StyleChange事件,達到全局樣式更改。

相關文章
相關標籤/搜索