QSS獨門秘籍:subcontrol


QSS是C++ Qt中的界面美化神器,其語法和CSS區別不大,但是QSS有一個獨有的功能——subcontrol,這是CSS所沒有的,一個widget往往由多個子部件構成,利用subcontrol可以對窗口部件的某些子部件做精細處理,從而使得界面美化達到定制最大化。

 

以下內容來源於http://qtdebug.com/QSS-Subcontrol.html

http://qtdebug.com/index.html上有很多不錯的教程

 

Subcontrol 的繪制位置由 subcontrol-origin、subcontrol-position, top, left 來指定,就先從這幾個屬性開始入手。

Subcontrol-Origin

subcontrol-origin 定義在 parent widget 中繪制 subcontrol 的參考矩形,默認在 padding 的矩形中繪制。

The origin rectangle of the subcontrol within the parent element. If this property is not specified, the default is padding.

subcontrol-origin 有 4 個值可選:
  • margin
  • border
  • padding
  • content

下圖展示了 subcontrol-origin 的值不同時,在 parent widget 的不同位置進行繪制 subcontrol:

Subcontrol-Position

已經知道 subcontrol 要在 parent widget 的某個矩形區域里繪制,如 padding rectangle,這個矩形這么大,具體要在這個矩形的哪個位置繪制呢?使用 subcontrol-position 來指定,不同的 subcontrol 的 subcontrol-position 默認值不同,例如 QSlider 的 handle 的默認值是 center center,QSpinBox 的 up-button 的默認值是 right top。

The alignment of the subcontrol within the origin rectangle specified by subcontrol-origin. If this property is not specified, it defaults to a value that depends on the subcontrol.

subcontrol-position 水平方向有 3 個值可選:
  • left
  • center
  • right
subcontrol-position 垂直方向有 3 個值可選:
  • top
  • center
  • bottom

用 Top 和 Left 微調 Subcontrol 的位置

Top 和 left 的主要作用是 :hover,:pressed 等發生時,用 top 和 left 偏移一點 subcontrol,這樣就看到 subcontrol 的鼠標動作了,偏移是相對於 subcontrol-orign 和 subcontrol-position 確定的位置,top 和 left 的默認值是 0。

用下面的 QSS 總結一下 subcontrol-origin, subcontrol-position, top, left:
  • QSpinBox 的 up-button 放置在 QSpinBox 的左邊垂直劇中
  • 當鼠標放到 up-button 上時,將其向右下角偏移 1px
  • 當鼠標離開 up-button 后,up-button 移回到原來的位置
QSpinBox::up-button { subcontrol-origin: margin; subcontrol-position: left center; } QSpinBox::up-button:hover { top: 1px; left: 1px; }




接下來就具體的介紹每一個 Widget 有哪些 subcontrol,怎么 QSS 它們。

QCheckBox

QCheckBox 的 subcontrol 有 ::indicator,比較有意思的是,text 總是顯示在 indicator 右邊,所以如果 indicator 靠右邊顯示的話,text 很可能就看不到了。

QRadioButton 的 QSS 和 QCheckBox 的一樣,所以就不在重復介紹。

下面 QSS 的效果如圖:

 

QCheckBox { color: lightgray; background: rgb(44, 44, 44); border: 10px solid rgb(76, 76, 76); spacing: 10px; /* indicator 和 text 的間隔 */ padding: 10px; } QCheckBox::indicator { subcontrol-origin: border; subcontrol-position: left center; background: white; border: 2px solid rgb(170, 170, 170); } QCheckBox::indicator:checked { background: rgb(76, 76, 76); }



 

修改 subcontrol-origin 和 subcontrol-position 為不同的值看看效果是什么。

QComboBox

QComboBox 的 subcontrol 有 drop-down

下面 QSS 的效果如圖:

 

QComboBox { color: lightgray; background: rgb(44, 44, 44); border: 10px solid rgb(76, 76, 76); spacing: 10px; /* indicator 和 text 的間隔 */ padding: 10px; } QComboBox::drop-down { width: 15px; height: 10px; subcontrol-origin: border; subcontrol-position: right center; background: white; border: 2px solid rgb(170, 170, 170); border-radius: 3px; } QComboBox::drop-down:hover { background: rgb(76, 76, 76); } QComboBox::drop-down:on { background: black; top: 1px; left: 1px; }



 

QSpinBox, QDateEdit, QTimeEdit, QDateTimeEdit

QSpinBox 的 subcontrol 有 ::up-button::down-button::up-arrow::down-arrow

  • up-button 顯示在 QSpinBox 里,它的 subcontrol-origin 是相對於 QComboBox 的
  • down-button 顯示在 QSpinBox 里,它的 subcontrol-origin 是相對於 QComboBox 的
  • up-arrow 顯示在 up-button 里,它的 subcontrol-origin 是相對於 up-button 的
  • down-arrwo 顯示在 down-button 里,它的 subcontrol-origin 是相對於 down-button 的

QDateEdit, QTimeEdit, QDateTimeEdit 的 subcontrol 和 QSpinBox 是一樣的,只需要把下面 QSS 里的 QSpinBox 換成 QDateEdit,QTimeEdit 或 QDateTimeEdit 即可。

下面 QSS 的效果如圖,down-button 靠左垂直居中,up-button 靠右垂直居中:

 

QSpinBox { color: lightgray; background: rgb(44, 44, 44); border: 10px solid rgb(76, 76, 76); padding: 5px; } QSpinBox::down-button, QSpinBox::up-button { subcontrol-origin: border; width: 16px; height: 10px; background: white; border: 2px solid rgb(170, 170, 170); } QSpinBox::down-button { subcontrol-position: center left; } QSpinBox::up-button { subcontrol-position: center right; } QSpinBox::up-arrow, QSpinBox::down-arrow { subcontrol-origin: content; subcontrol-position: center center; width: 6px; height: 6px; background: rgb(76, 76, 76); }



 

QSlider

QSlider 的 subcontrol 有 ::groove(槽),::handle::add-page 和 ::sub-page

  • groove 顯示在 QSlider 里,它的 subcontrol-origin 是相對於 QSlider 的
  • handle 顯示在 groove 里,它的 subcontrol-origin 是相對於 groove 的
  • sub-page 顯示在 groove 里,它的 subcontrol-origin 是相對於 groove 的
  • add-page 顯示在 groove 里,它的 subcontrol-origin 是相對於 groove 的
  • handle, sub-page, add-page 雖然都顯示在 groove 里,但是都可以把它們擴展到 groove 外

下面 QSS 的效果如圖:

 

QSlider { background: rgb(170, 170, 170); padding: 2px; height: 40px; } QSlider::groove:horizontal { subcontrol-origin: content; background: rgb(76, 76, 76); /* the groove expands to the size of the slider by default. by giving it a height, it has a fixed size */ height: 20px; } QSlider::handle:horizontal { background-color: rgb(50, 50, 50); width: 40px; border-radius: 20px; /* handle is placed by default on the contents rect of the groove. Expand outside the groove */ margin: -10px 0; } QSlider::sub-page:horizontal { background: #999; margin: 5px; border-radius: 5px; } QSlider::add-page:horizontal { background: #666; margin: 5px; border-radius: 5px; }



 

Groove 的默認高度和 QSlider content rectangle 的高度一樣,給它一個高度值就可以讓他有固定的高度了,把 groove 的 height 去掉試試。

Handle 的默認高度和 groove content rectangle 的高度一樣,為了讓起顯示超出 groove,需要設置 margin 為負值,如果這個負值太小,顯示超出 QSlider 的部分將看不到。Handle 的 subcontrol-position 沒有作用,因為 handle 不是固定在一個地方的,而是根據 QSlider 的值動態計算顯示的位置。

QProgressBar

QProgressBar 的 subcontrol 有 ::chunk

對於 QProgressBar 的 QSS,大多數都是想把 chunk 定義為圓角矩形的樣子,但是當它的 value 比較小時,chunk 的圓角會變成直角,即使使用圖片都不行,效果很不理想,所以如果要修改 QProgressBar 的外觀的話,推薦繼承 QProgressBar 自己繪制或者使用 QStyle。

QGroupBox

QGroupBox 的 subcontrol 有 ::title 和 ::indicator

  • title 相對於 QGroupBox
  • indicator 的 subcontrol-origin 和 subcontrol-position 自定義無效

下面 QSS 的效果如圖:

 

QGroupBox { background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #E0E0E0, stop: 1 #EEEEEE); border: 2px solid gray; border-radius: 5px; margin-top: 10px; /* leave space at the top for the title */ } QGroupBox::title { subcontrol-origin: margin; subcontrol-position: top center; /* position at the top center */ padding: 2px 3px; color: white; margin-top: 2px; background-color: gray; border-radius: 3px; spacing: 5px; } QGroupBox::indicator { width: 13px; height: 13px; border: 1px solid black; background: white; } QGroupBox::indicator:checked { background: yellow; }



 

QTableView

 

QTableView 相關的 subcontrol 有 QTableView 的 ::item,QHeaderView 的 ::section 和 左上角的 QTableCornerButton 的 ::section

QTableView 的 QSS 對於 QTableWidget 也是生效的。

下面 QSS 的效果如圖:

  1.  
    QTableView, QHeaderView, QTableView::item {
  2.  
    background: white;
  3.  
    }
  4.  
     
  5.  
    QTableView::item:alternate {
  6.  
    background: rgb(209, 231, 254);
  7.  
    }
  8.  
     
  9.  
    QTableView::item:selected { /*被選中的index*/
  10.  
    color: black;
  11.  
    background: qlineargradient(
  12.  
    x1: 0, y1: 0, x2: 0, y2: 1,
  13.  
    stop: 0 #FAFBFE,
  14.  
    stop: 1 #DCDEF1);
  15.  
    }
  16.  
     
  17.  
    QHeaderView::section:horizontal, QTableCornerButton::section {
  18.  
    background-color: qlineargradient(spread:reflect,
  19.  
    x1:0, y1:0, x2:0, y2:1,
  20.  
    stop:0 rgba(255, 255, 255, 255),
  21.  
    stop: 1 rgba(164, 164, 164, 255));
  22.  
    border: 1px solid rgb(153, 153, 153);
  23.  
    border-width: 0 1px 1px 0;
  24.  
    }
  25.  
     
  26.  
    QHeaderView::section:vertical {
  27.  
    background: #DDD;
  28.  
    border: 1px solid rgb(153, 153, 153);
  29.  
    border-width: 0 1px 1px 0;
  30.  
    }



QTreeView

QTreeView 相關的 subcontrol 有 QHeaderView::section(和上面 QTableView 的 header view 的 QSS 一樣,其實就是一個東西),QTreeView::item 和 QTreeView::branch

QTreeView 的 QSS 對於 QTreeWidget 也是生效的。

QTreeView 的 subcontrol 並不多,但是 branch 有很多種不同的狀態,關鍵就是理解這些狀態,不同狀態時使用不同的背景,下面 QSS 的效果如圖:

 

 

QHeaderView::section:horizontal { background-color: qlineargradient(spread:reflect, x1:0, y1:0, x2:0, y2:1, stop:0 rgba(255, 255, 255, 255), stop:1 rgba(164, 164, 164, 255)); border: 1px solid rgb(153, 153, 153); border-width: 0 1px 1px 0; } QTreeView::item { border-bottom: 1px solid lightgray; selection-color: black; } QTreeView::item:selected { /*被選中的index*/ background: qlineargradient( x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #FAFBFE, stop: 1 #DCDEF1); } QTreeView::branch { background: yellow; } QTreeView::branch:has-siblings:!adjoins-item { background: cyan; } QTreeView::branch:has-siblings:adjoins-item { background: red; } QTreeView::branch:!has-children:!has-siblings:adjoins-item { background: blue; } QTreeView::branch:closed:has-children:has-siblings { background: black; } QTreeView::branch:has-children:!has-siblings:closed { background: gray; } QTreeView::branch:open:has-children:has-siblings { background: magenta; } QTreeView::branch:open:has-children:!has-siblings { background: green; }



 

上面的 QSS 是不實用的,直接這么用到項目里,肯定被咔嚓,但是把各種狀態的 branch 標記為不同的顏色,這樣在做設計的時候就能很容易的分辨出各種 branch,然后根據需求設計出不同的圖片放到對應的 branch。其實 Qt 的幫助文檔里對 QTreeView 的 QSS 已經有一個很完善的例子,在 QtCreator 的幫助里搜索 style sheets examples,定位到 Customizing QTreeView 就可以看到這個例子了,開發的時候,照葫蘆畫瓢的做就可以了:

QTabWidget

QTabWidget 相關的 subcontrol 有:
  • QTabWidget::pane
  • QTabWidget::tab-bar
  • QTabBar::tab
  • QTabBar::close-button
  • QTabBar::tear
  • QTabBar::scroller
  • QTabBar QToolButton::left-arrow
  • QTabBar QToolButton::right-arrow

不幸的是,在我看來,QTabWidget 還有一些 bug,例如:
  • QTabBar::close-button 只能定位在 tab 的左邊或者右邊,不能調整和文本之間的間隔
  • Tab 互相遮蓋的情況下,如我們的例子的樣式,拖動 tab 時某些 tab 會被截斷
  • QTabBar::scroller 的定位也是個問題,不能自由定位到需要的位置
  • QTabBar 和 QTabWidget 不一樣寬


上圖是 SublimeText 的截圖,很多應用中的 tab widget 也差不多是上面這個樣,至少這幾個 bug 決定了 QTabWidget 使用 QSS 我還做不到這么好,其實用性大受限制,所以就不對 QTabWidget 做太多介紹,只給出一個小例子,展示一下簡單的定制 QTabWidget,滿足一般的需求,復雜的需要我們自己實現一個 tab widget。

下面 QSS 的效果如圖:

 

QTabWidget::pane { /* The tab widget frame */ border: 2px solid rgb(69, 69, 69); margin-top: -2px; } QTabWidget::tab-bar { left: 5px; /* move to the right by 5px */ } QTabBar::tab { color: gray; min-width: 40px; height: 28px; border-width: 0 18px 0 18px; border-image: url(:/resources/tab-inactive.png) 0 18 0 18 stretch stretch; } QTabBar::tab:selected { color: #DDD; height: 28px; border-width: 0 18px 0 18px; border-image: url(:/resources/tab-active.png) 0 18 0 18 stretch stretch; } QTabBar::tab:!first { margin-left: -20px; } QTabBar::tab:hover { color: #DDD; }



 

tab-active.png tab-inactive.png

QScrollBar

QScrollBar 相關的 subcontrol 挺多的,仔細觀察的話,有點像 QSpinBox 和 QSlider 的合體:

  • ::sub-line, ::add-line
  • ::sub-page, ::add-page
  • ::up-arrow, ::down-arrow
  • ::left-arrow, ::right-arrow

很多時候並不是每一個 subcontrol 都需要處理的,例如下面的 QSS 中,arrow 沒有處理,直接繪制在 ::sub-line 和 ::add-line 的背景里,也沒有處理 ::sub-page 和 ::add-page,效果如圖:

 

QScrollBar:horizontal { height: 16px; border-width: 0px 10px 0px 10px; border-image: url(:/resources/horizontal-track.png) 0 10 0 10 repeat stretch; margin-left: 6px; margin-right: 16px; padding-right: 4px; } QScrollBar::handle:horizontal { min-width: 40px; border-width: 0 17px 0 17px; border-image: url(:/resources/horizontal-handle.png) 0 17 0 17 repeat repeat; } QScrollBar::sub-line:horizontal { width: 20px; height: 17px; subcontrol-position: left; subcontrol-origin: margin; background-image: url(:/resources/horizontal-sub-line.png) } QScrollBar::add-line:horizontal { width: 20px; height: 17px; subcontrol-position: right; subcontrol-origin: border; background-image: url(:/resources/horizontal-add-line.png) } QScrollBar:vertical { width: 16px; border-width: 10px 0px 10px 0px; border-image: url(:/resources/vertical-track.png) 10 0 10 0 repeat repeat; margin-top: 6px; margin-bottom: 16px; padding-bottom: 6px; } QScrollBar::handle:vertical { min-height: 40px; border-width: 17px 0px 17px 0px; border-image: url(:/resources/vertical-handle.png) 17 0 17 0 repeat repeat; } QScrollBar::sub-line:vertical { width: 17px; height: 22px; subcontrol-position: top left; subcontrol-origin: margin; background-image: url(:/resources/vertical-sub-line.png) } QScrollBar::add-line:vertical { width: 17px; height: 22px; subcontrol-position: bottom; subcontrol-origin: border; background-image: url(:/resources/vertical-add-line.png) }



 

QTableView 的 QSS 這里沒有給出,請參考上面 QTableView 的 subcontrol 的內容。

QScrollBar 使用的背景圖:
       


 

有 subcontrol 的常用 widget 基本上介紹完了,相關的 QSS 還有更多的細節都可以在 Qt 的幫助文檔里找到。

 

https://blog.csdn.net/hechao3225/article/details/53914390


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM