最近想學習下Qt的自定義界面,所以花了點時間看了下QStyle,,,,結果很難受,這一塊涉及到一大塊GUI的具體實現方式,看得我很頭疼。想看第一手資料而且英語功底不錯的能夠直接上qt文檔,下面我會以易懂的方式簡單講解下。html
首先咱們要來說講GUI控件結構,這裏以QComboBox爲例:git
一個完整的控件由一種或多種GUI元素構成:github
Complex control elements contain sub controls. Complex controls behave differently depending on where the user handles them with the mouse and which keyboard keys are pressed.api
Complex Control Elements(簡稱CC)包含子控件。根據用戶對鼠標和鍵盤的不一樣處理,CC控件的表現也不一樣。上圖中的QComboBox僅包含一個CC控件CC_ComboBox,該複雜控件又包含三個子控件(SC,Sub Control)SC_ComboBoxFrame、SC_ComboBoxArrow、SC_ComboBoxEditField。學習
A control element performs an action or displays information to the user.code
控件元素與用戶交互相關,例如PushButton、CheckBox等等。QComboBox只有一個CE_ComboBoxLabel用以在ComboBox左側展現當前選中或者正在編輯的文字。orm
Primitive elements are GUI elements that are common and often used by several widgets.htm
主元素表明那些公共的GUI元素,主要用於GUI控件複用。例如PE_FrameFocusRect這個主元素就進場被多種控件用來繪製輸入焦點。QComboBox包含兩個主元素PE_IndicatorArrowDown、PE_FrameFocusRect。blog
QStyle是一套抽象接口,它定義了實現界面控件外觀的一系列api而且不能用來被實例化:接口
QProxyStyle實現了QStyle全部的抽象接口,而且默認保持系統風格,在Linux、Windows、Mac系統上樣式以下:
QStyleFactory類提供了當前可應用的全部QStyle風格實現,在Windows系統上我得到以下幾種風格(具體結果見最後一小節):
咱們能夠經過QStyleFactory::keys()和QStyleFactory::create()來獲取這些可用的風格而且設置到須要的QWidget上用以改變GUI風格。
這裏咱們經過實現一個QStyle來自定義QComboBox的樣式。
這個自定義的QComboBox樣式分爲兩部分,箭頭區域和非箭頭區域。非箭頭區域包含CE_ComboBoxLabel和SC_CombBoxListBoxPopup。因爲QStyle不負責繪製下拉框(由delegate繪製),咱們只能更改下拉框的位置和大小(這裏咱們不作改變)。 箭頭區域包含背景區和PE_IndicatorArrowDown。
箭頭區域咱們用一個輻射漸變來繪製背景,而且在鼠標Hover或者按下的時候更改漸變的顏色來重繪,中間的下拉箭頭咱們複用QProxyStyle的實現來完成。
void CustomeStyle::drawArrowArea(const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget) const { QRect arrowBoxRect = option->rect; arrowBoxRect.adjust(option->rect.width() * 0.8, 0, 0, 0); auto arrowAreaColor = Qt::darkCyan; m_arrowAreaHovered = arrowBoxRect.contains(widget->mapFromGlobal(QCursor::pos())); if (option->state & State_MouseOver && m_arrowAreaHovered) arrowAreaColor = Qt::cyan; else if (option->state & State_On && m_arrowAreaHovered) arrowAreaColor = Qt::darkMagenta; QRadialGradient gradient(arrowBoxRect.center(), arrowBoxRect.width()); gradient.setColorAt(1.0, arrowAreaColor); painter->fillRect(arrowBoxRect, QBrush(gradient)); auto arrowDownOption = *option; auto adjustPixel = arrowBoxRect.width() * 0.2; arrowDownOption.rect = arrowBoxRect.adjusted(adjustPixel, adjustPixel, -adjustPixel, -adjustPixel); drawPrimitive(PE_IndicatorArrowDown, &arrowDownOption, painter, widget); }
非肩頭區域即CE_ComboBoxLabel,咱們用4種顏色的線性漸變來繪製,同箭頭區域同樣她也會根據當前的狀態更改漸變顏色來增長交互效果:
auto comboBoxOption = qstyleoption_cast<const QStyleOptionComboBox*>(option); if (comboBoxOption == nullptr) return; QColor gradientColors[] = { Qt::yellow, Qt::green, Qt::blue, Qt::red }; QColor penColor = Qt::white; if (option->state & State_MouseOver && !m_arrowAreaHovered) { for (auto& color : gradientColors) color.setAlpha(80); penColor.setAlpha(80); } else if (option->state & State_On && !m_arrowAreaHovered) { for (auto& color : gradientColors) color = color.darker(300); penColor = penColor.darker(300); } QRect labelRect = comboBoxOption->rect; labelRect.adjust(0, 0, -(labelRect.width() * 0.2), 0); QLinearGradient linearGradient(labelRect.topLeft(), labelRect.bottomRight()); for (int i = 0; i < 4; ++i) { linearGradient.setColorAt(0.25 *i, gradientColors[i]); } painter->fillRect(labelRect, QBrush(linearGradient)); painter->setPen(QPen(penColor)); painter->drawText(labelRect, comboBoxOption->currentText, QTextOption(Qt::AlignCenter));
完整代碼見連接。
QStyle優勢:
QStyle缺點:
下期咱們介紹Qt的Style Sheet。