QStyle
樣式(繼承自QStyle類)代表控件的繪制并封裝GUI的外觀。QStyle是一個封裝了GUI外觀的抽象基類。Qt使用QStyle去執行幾乎所有的內置控件的繪制,確保控件外觀和原生控件風格風格相同。
class Q_WIDGETS_EXPORT QStyle : public QObject{};
QProxyStyle
為了自定義現有樣式,需要繼承QProxyStyle并重新實現所需的虛函數。QProxyStyle允許指定某個基本樣式,當基本樣式未指定時,將自動使用應用程序樣式。前者提供對基本樣式的完全控制,并且在自定義需要特定樣式行為時效果最佳,而后者提供一種與平臺無關的方式來自定義默認為本機平臺樣式的應用程序樣式。
class Q_WIDGETS_EXPORT QProxyStyle : public QCommonStyle{};
QCommonStyle
QCommonStyle為完全自定義樣式實現提供了一個方便的基類。這種方式與使用QProxyStyle相同,但是繼承自QCommonStyle并重新實現相應的虛函數。
class Q_WIDGETS_EXPORT QCommonStyle: public QStyle{};
QStyle實現
QStyle的API包含繪制控件的函數,用于執行常見和困難任務的靜態函數以及用于執行繪制時所需的各種計算的函數。該樣式也輔助控件內容的布局,此外還創建一個包含QBrush的QPalette對象。
QStyle繪制圖形元素,一個元素是一個控件,或者是一個控件部分(如按鈕斜角、窗口框架或滾動條等等)。大多數繪制函數采用四個函數:
- 枚舉值指定繪制元素的類型;
- QStyleOption對象指定呈現該元素的方式和位置;
- QPainter對象表示執行繪圖的畫筆;
- QWidget對象表示執行繪圖的控件。
當控件要求樣式繪制元素時,控件會為樣式提供QStyleOption對象(包含繪制所需信息的類)。QStyleOption類使得可以在不鏈接控件代碼的情況下繪制控件,從而在任何繪畫設備上使用QStyle的繪制函數成為可能。在Qt中,Item Views的items由委托繪制。Item Views的標題任由樣式繪制。
樣式元素
樣式元素是GUI的圖形部分。一個控件由樣式元素的層次結構(或樹)組成。比如,當樣式收到繪制按鈕的請求時(例如,從QPushButton),樣式會繪制一個標簽(文本和圖標)、一個按鈕斜角和一個焦點框。而按鈕斜角由斜角周圍的框架和另外兩個元素組成。
繪制控件不一定是通過要求樣式只繪制一個元素來完成,控件可以多次調用來繪制不同元素。比如QTabWidget單獨繪制選項卡和框架。
元素類型分三種:基本元素、控制元素和復雜控制元素,元素由枚舉類型ComplexControl、ControlElement和PrimitiveElement enums定義。
- 基本元素:
基本元素是常見的GUI元素。例如,框架frame、按鈕斜角button bavels以及用于輸入框spin boxes、滑動條scroll bars和下拉框combo boxes的箭頭。基本元素不能獨立存在,而是整體結構的一部分,不參與用戶交互,而是GUI中的被動裝飾。
- 控制元素:
控制元素執行操作或者向用戶顯示信息。控制元素包括表和樹視圖中的按鈕、勾選框和標題部分。控制元素不一定是完整的小部件,也可以是控件的部分結構,比如滑動條滑塊和選項卡。控制元素與基本元素的不同之處在于其不是被動的,而是在交互中填充功能。有多個元素組成的控件通常使用樣式來計算元素的邊界矩形,可用的子元素由枚舉類型SubElement定義。此枚舉僅用于計算邊界矩形,子元素不是像基本元素、控制元素和復雜元素那樣需要繪制的圖形元素。
- 復雜的控制元素:
復雜的控制元素包含子控件。復雜控件的行為會有所不同,具體取決于用戶使用鼠標的位置和按下的鍵盤鍵。這取決于鼠標處于或按鍵按下的子控件(如果有)。復雜控件元素有滾動條和組合框。可以通過鼠標移動滑動塊或者按向上和向下按鈕來使用滑動條。可用的子控件由枚舉SubControl枚舉類型定義。
除了繪圖之外,樣式還需要為控件提供有關鼠標在哪個子控件上進行操作的信息。例如,互動條需要知道用戶是否按下滑塊、滑塊凹槽或向上、向下按鈕。
和控制元素不同,不能使用樣式繪制子控件,樣式僅計算應在其中繪制子控件的邊界矩形。然而,復雜元素通常使用控制元素和基本元素來繪制子控件。
樣式選項
QStyleOption的子類包含設置各個元素樣式所需的所有信息。樣式選項通常在棧上被實例化,并由QStyle函數的調用方填寫。根據繪制的內容,樣式期望不同的樣式選項類。例如,QStyle::PE_FrameFocusRect元素需要一個QStyleOptionFocusRect參數,并且可以創建一個自定義樣式可以使用的自定義子類。出于性能原因,樣式選項保留公共變量
控件可以處于許多不同的狀態,這些狀態由State枚舉類型定義。根據控件的不同,狀態標志具有不同的含義,但像State_Disabled這樣的狀態標志對所有控件都是通用的。
最值得注意的是,樣式選項包含要繪制的控件的調色板和邊界矩形,大多數控件都有專門的樣式選項,例如QPushButton和QCheckBox使用QStyleOptionButton用作樣式選項,其中包含文本、圖標及其圖標的大小。
在重新實現帶有QStyleOption參數的QStyle函數時,通常需要將QStyleOption對象類型轉換為子類(如QStyleFocusRect)。為了安全起見,可以使用qstyleoption_cast()確保指針類型時正確的。如果類型指針不正確,qstyleoption_cast會返回nullptr。
QStyle函數
定義了三個虛函數,用來繪制基本元素、控制元素和復雜控制元素。
- 繪制基本元素drawPrimitive
- 繪制復雜控制元素drawComplexControl
- 繪制控制元素drawControl
QStyle類還提供繪制元素時使用的輔助函數,drawItemText函數在指定的矩形內繪制文本,將調色板對象作為參數。drawItemPixmap函數在指定的邊界矩形內對齊像素圖。
其它QStyle函數為執行繪制的函數進行各種計算。如果控件繪制多個樣式元素,也使用這些函數來計算大小提示和邊界矩形。和繪制元素的函數一樣,輔助函數通常采用相同的參數。
- subElementRect函數采用SubElement枚舉值并計算子元素的邊界矩形,樣式使用此函數來得到繪制元素不同部分的位置。
- subControlRect函數用來計算復雜控制元素中的子控件的邊界矩形,實現新樣式時需要重新實現該函數并計算矩形。
- pixelMetric函數返回像素度量,該指標是以屏幕像素為單位給出的與樣式相關的大小。采用PixelMetric枚舉值并返回正確的測量值。
- hitTestComplexControl函數返回鼠標指針在復雜控制元素中的子控件上。
QStyle還定義了函數polish和unpolish函數,所有控件在顯示之前都會被發送到polish函數,在隱藏時會被發送到unpolish。可以使用這些函數在設置控件上的屬性,或者執行樣式所需的其它工作。例如,如果需要知道鼠標何時懸停在控件上,需要設置WA_Hover屬性,然后控件中的樣式選項中的State_MouseOver狀態位被設置。
QStyle也定義了一些靜態輔助函數,執行一些常見但困難的任務。例如,根據滑塊的值計算滑塊手柄的位置,并轉換矩形并繪制文本,考慮反向布局。
調色板
QPalette提供調色板,存儲不同控件狀態和顏色角色的顏色。每種樣式都提供調色板應用于控件的繪制。有一組顏色集合對應于不同的控件狀態:活動狀態(具有焦點)、非活動狀態和禁用狀態。可以通過查詢State_Enabled和State_Actived狀態標志得到當前的狀態。每一個顏色集合包含由枚舉類型QPalette::ColorRole確定的顏色職責。這些職責描述了在一種情況(繪制控件背景、文本和按鈕)下應該使用對應的顏色。
如何使用顏色職責取決于樣式,例如,如果樣式使用漸變,則可以使用使用QColor::darked或QColor::lighter使其更暗或更亮從而創建漸變。如果需要調色板沒有提供的畫布,通常應該繼承。
Java風格
實現一種類似于Java默認外觀的樣式。
設計與實現
設計樣式的第一步是選擇基類,通常選擇繼承QCommonStyle,該類實現了需要的大多數功能,而不是執行實際的繪圖。
改樣式在一個類中實現。
嘗試實現一個控件
公共的控件屬性和變量
某些狀態和變量對于所有控件來說是通用的,這些屬性通過QStyleOption::initFrom函數設置。并非所有元素都使用該函數。
狀態 | 狀態設置條件 |
---|---|
State_Enabled | 控件沒有禁用時 |
State_Focus | 控件有焦點 |
State_KeyboardFocusChange | 鍵盤焦點更改 |
State_MouseOver | 鼠標在控件上 |
State_Active | 控件是活動窗口的子窗口 |
State_HasEditFocus | 控件具有編輯焦點 |
變量 | 內容 |
---|---|
rect | 繪制元素的邊界矩形 |
direction | 布局方向 |
palette | 繪制元素使用的調色板 |
fontMetrics | 繪制文本的字體計量 |
QPushButton
按鈕的樣式結構圖下所示,通過對樹進行自上而下的遍歷,可以獲得繪制元素的順序。
按鈕的布局(和元素邊界相關)因樣式而異。元素可能有相同的邊界。例如,PE_PushButtonBevel被用于在QCommonStyle中繪制三個元素:PE_FrameDefaultButton、PE_FrameButtonBevel和PE_PanelButtonCommand,這些元素在公共樣式中具有相同的邊界。
下面給出了一個按鈕圖像,該圖像顯示了元素的邊界矩形。顏色用于分隔圖像中的邊界矩形。
QPushButton的樣式選項是QStyleOptionButton,QPushButton可以在樣式選項上設置的狀態表如下。
狀態 | 狀態設置條件 |
---|---|
State_Sunken | 按鈕按下或按下菜單顯示 |
State_On | 按鈕被選中 |
State_Raised | 按鈕沒按下也沒升起 |
成員變量 | 內容 |
---|---|
features | 描述各種按鈕屬性 |
icon | 按鈕圖標 |
iconSize | icon的大小 |
text | 按鈕文本 |
QCheckBox和QRadioButton
QCheckBox和QRadioButton的結構是相同的。
狀態 | 狀態設置條件 |
---|---|
State_Sunken | 框被按下 |
State_NoChange | 框被部分選中 |
State_On | 框被選中 |
State_Off | 按鈕沒按下也沒升起 |
Tabs選項卡
在Qt中,QTabBar使用樣式來繪制選項卡。選項卡要么存在于包含QTabBar的QTabWidget,要么作為單獨的選項卡欄存在。
QTabBar和QTabWidget的樣式結構如下。
tab bar形狀和標簽具有與CE_TabBarTab相同的邊界矩形。請注意,tabs和tab widget框架重疊,tab bar的底部是重疊的區域。
tabs的樣式QStyleOptionTab包含繪制tabs必須的信息。該樣式選項包含tab在tab bar中的位置,選擇tab的位置,tab的形狀,文本,圖標和圖標的大小。
tab bar可以在tabs上設置的狀態表如下。
狀態 | 狀態設置條件 |
---|---|
State_Sunken | 選項卡被鼠標選中 |
State_Selected | 是當前選項卡 |
State_HasFocus | 選項卡欄具有焦點且選項卡處于選中狀態 |
QStyleOptionTab的成員表如下。
成員變量 | 內容 |
---|---|
cornerWidgets | 指示tab bar是否具有角控件以及具有哪些角控件 |
icon | tab圖標 |
iconSize | icon的大小 |
text | tab文本 |
position | tab在tab bar上相對于其它tab的位置 |
row | tab所在的行 |
selectedPosition | 指示所選tab和tab相鄰還是tab |
shape | 指示tab是圓角還是三角以及tab的方向 |
tab widget的外框使用QStyleOptionTabWidgetFrame作為樣式選項,除了通用的標志外,沒有其它狀態標志。
成員變量 | 內容 |
---|---|
cornerWidgets | 指示tab bar是否具有角控件以及具有哪些角控件 |
icon | tab圖標 |
iconSize | icon的大小 |
text | tab文本 |
position | tab在tab bar上相對于其它tab的位置 |
row | tab所在的行 |
selectedPosition | 指示所選tab和tab相鄰還是tab |
shape | 指示tab是圓角還是三角以及tab的方向 |
Scroll Bars
QScrollBar只需創建樣式選項,然后繪制控件。