QSS Subcontrol

普通的 QSS 和 CSS 沒什麼區別,難度不大,但除此之外,想要使用好 QSS,還必須得掌握好 subcontrol,這個在 CSS 裏沒有,是 Qt 獨有的。

什麼是 subcontrol?一個複雜的 widget 由多個部分組成,它們可以是一個 widget,也可以是邏輯上的部件,例如 QCheckBox 由 icon 和 text 兩個部分組成,不僅可以定義 text 的樣式,還可以定義 icon 相關的樣式,icon 部分就是 QCheckBox 的 subcontrol ::indicator

在 Qt 的幫助文檔裏有所有 subcontrol 的說明,但是相信很多人看了還是不明白每個 subcontrol 具體是什麼,這一節將使用可視化的方式標記出 subcontrol,介紹使用 QSS 自定義有 subcontrol 的常用 widget,這裏的重心是怎麼去 QSS subcontrol 而不是樣式效果,複雜漂亮的界面需要大量的圖片和更多的 QSS,這裏不作介紹,以免陷入細節,掩蓋本節主題。只要知道了原理,結合已經掌握的 QSS,找美工提供一套界面切圖,就能很容易實現出來很專業效果了。

Subcontrol 的 QSS 和大多數 widget 的差不多,也支持盒子模型,可以自定義 color, background, background-color, background-image, border, padding, margin, width, height 等,也支持 Pseudo-States。

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-origin。不同的 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 移回到原來的位置
      
      
1
2
3
4
5
6
7
8
9
      
      
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 的效果如圖:

      
      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
      
      
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 的效果如圖:

      
      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
      
      
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 靠右垂直居中:

      
      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
      
      
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 的效果如圖:

      
      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
      
      
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 的效果如圖:

      
      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
      
      
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
      
      
QTableView, QHeaderView, QTableView ::item {
background: white;
}
QTableView ::item :alternate {
background: rgb(209, 231, 254);
}
QTableView ::item :selected { /*被選中的index*/
color: black;
background: qlineargradient(
x1: 0, y1: 0, x2: 0, y2: 1,
stop: 0 #FAFBFE,
stop: 1 #DCDEF1);
}
QHeaderView ::section :horizontal, QTableCornerButton ::section {
background-color: qlineargradient(spread:reflect,
}
QTableView ::item :selected { /*被選中的index*/
color: black;
background: qlineargradient(
x1: 0, y1: 0, x2: 0, y2: 1,
stop: 0 #FAFBFE,
stop: 1 #DCDEF1);
}
QHeaderView ::section :horizontal, QTableCornerButton ::section {
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;
}
相關文章
相關標籤/搜索