Qt包含一組QStyle子類,這些子類(QWindowsStyle,QMacStyle等)模擬Qt支持的不同平臺的樣式,默認情況下,這些樣式內置在Qt GUI模塊中,樣式也可以作為插件提供。
Qt的內置widgets使用QStyle來執行幾乎所有的繪圖,確保其看起來與等效的原生widgets完全相同。下面展示了九種不同樣式的QComboBox。
設置樣式
使用QApplication::setStyle()函數設置整個應用程序的樣式,也可以使用命令行選項指定。使用QWidget::setStyle()設置單個控件的樣式。如果沒有指定樣式,Qt會根據平臺或桌面環境選擇最合適的樣式。
自定義風格控件開發
開發自定義控件并且希望在所有平臺上表現一致,可以使用QStyle函數(如drawItemText()、drawItemPixmap()、drawPrimitive()、drawControl()和drawComplexControl())來執行控件繪制的一部分。
QStyle函數大部分包含4個參數:
枚舉值:指定要繪制的圖形元素的類型
QStyleOption:指定渲染元素的方式和位置
QPainter:用于繪制元素
QWidget:執行繪圖的控件
例如,如果要在小部件上繪制一個焦點矩形,可以編寫:
void MyWidget::paintEvent(QPaintEvent * /* event */)
{QPainter painter(this);QStyleOptionFocusRect option;option.initFrom(this);option.backgroundColor = palette().color(QPalette::Background);style()->drawPrimitive(QStyle::PE_FrameFocusRect, &option, &painter, this);
}
控件從QStyleOption中獲取呈現圖形元素所需的所有信息。QStyleOption有多個子類,表示可以用于繪制的圖形元素的類型。
為了方便,Qt提供了QStylePainter類,結合了QStyl、QPainter和QWidget,從而能夠使用
QStylePainter painter(this);
...
painter.drawPrimitive(QStyle::PE_FrameFocusRect, option);
代替
QPainter painter(this);
...
style()->drawPrimitive(QStyle::PE_FrameFocusRect, &option, &painter, this);
創建自定義樣式
可以通過創建自定義樣式來為應用程序創建自定義外觀。有兩種方法可以創建自定義樣式。在靜態方法中,可以對現有的QStyle類進行子類化。然后重新實現虛擬函數以提供自定義行為,或者從頭開始創建整個QStyle類。動態方法中,使用QProxyStyle,在運行時修改系統樣式的行為。
靜態方法的第一步是選擇Qt提供的樣式之一來構建自定義樣式,最常用的基類是QCommonStyle(而不是QStyle)。
根據要更改的基本樣式的部分,重新實現用于繪制界面的對應部分的函數。以修改QWindowStyle繪制的QSpinBox箭頭為例,箭頭是由drawPrimitive()函數繪制的基元元素,因此需要實現該函數。
class CustomStyle : public QProxyStyle
{Q_OBJECTpublic:CustomStyle(const QWidget *widget);~CustomStyle() {}void drawPrimitive(PrimitiveElement element, const QStyleOption *option,QPainter *painter, const QWidget *widget) const override;
};
QSpinBox使用PE_IndicatorSpinUp和PE_IndicatorSpinDown基元元素去繪制向上和向下箭頭。
void CustomStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *option,QPainter *painter, const QWidget *widget) const
{if (element == PE_IndicatorSpinUp || element == PE_IndicatorSpinDown) {QPolygon points(3);int x = option->rect.x();int y = option->rect.y();int w = option->rect.width() / 2;int h = option->rect.height() / 2;x += (option->rect.width() - w) / 2;y += (option->rect.height() - h) / 2;if (element == PE_IndicatorSpinUp) {points[0] = QPoint(x, y + h);points[1] = QPoint(x + w, y + h);points[2] = QPoint(x + w / 2, y);} else { // PE_SpinBoxDownpoints[0] = QPoint(x, y);points[1] = QPoint(x + w, y);points[2] = QPoint(x + w / 2, y + h);}if (option->state & State_Enabled) {painter->setPen(option->palette.mid().color());painter->setBrush(option->palette.buttonText());} else {painter->setPen(option->palette.buttonText().color());painter->setBrush(option->palette.mid());}painter->drawPolygon(points);} else {QProxyStyle::drawPrimitive(element, option, painter, widget);}
}
需要注意的是,不會使用參數widget,只是將其傳遞給QWindowStyle::drawPrimitive()函數。要繪制的內容和如何繪制的信息是由QStyleOption對象指定的,與參數widget無關。如果需要使用參數widget來獲取其它信息,要在使用之前確保其不為0,并且類型是正確的。在實現自定義樣式時,不能僅僅因為枚舉值為PE_IndicatorSpinUp或PE_IndicatorSpinDown而假定控件類型是QSpinBox。
const QSpinBox *spinBox = qobject_cast<const QSpinBox *>(widget);
if (spinBox) {
...
}
使用自定義樣式
在Qt應用程序中使用自定義樣式有幾種方法。最簡單的方法是在創建QApplication之前將自定義樣式傳遞給靜態函數QApplication::setStyle()。該函數可以隨時被調用,但在構造函數之前調用,可以確保遵守使用命令行選項(-style)設置的用戶首選項。
#include <QtWidgets>#include "customstyle.h"int main(int argc, char *argv[])
{QApplication::setStyle(new CustomStyle);QApplication app(argc, argv);QSpinBox spinBox;spinBox.show();return app.exec();
}
Qt插件系統支持創建樣式作為插件,在運行時加載為共享對象。從而將自定義樣式用于其它應用程序,而無需重新編譯。