今天是一個大課題:QSS樣式表
一.概念:
QSS是Qt Style Sheet——Qt樣式表,是用來自定義控件外觀的一種機制;可以把他類比成CSS,但是不及其功能強大。
二.使用:
我們做一個模板,可以在后面來演示

from PyQt5.Qt import * import sys class Window(QWidget): def __init__(self): super().__init__() self.UI_test() self.resize(800,600) def UI_test(self): box1 = QWidget(self) box2 = QWidget(self) layout = QVBoxLayout() layout.addWidget(box1) layout.addWidget(box2) self.setLayout(layout) label1 = QLabel('標簽1',box1) btn1 = QPushButton('click1',box1) btn1.move(150,50) label2 = QLabel('標簽1', box2) btn2 = QPushButton('click1', box2) btn2.move(150, 50) pass if __name__ == '__main__': app = QApplication(sys.argv) window = Window() window.show() sys.exit(app.exec_())
mainwindow-- | | |box1----------- | | -label1 | | | | -btn1 | |box1----------- | -label1 | | -btn1
1.局部設置
局部設置是指定需要設置外觀的控件,直接調用控件的方法就好了
QWidget.setStyleSheet()
局部設置的作用域是控件本身和子控件
box1.setStyleSheet('background-color:red') box2.setStyleSheet('background-color:orange')
可以發現整個控件內部的子控件都被設置了。
還有一種方式,在定義的時候加上選擇器
box1.setStyleSheet("QPushButton {background-color:yellow};") box2.setStyleSheet("QLabel {background-color:red};")
這樣就可以把指定類型的控件的樣式設定
2.全局設置
比方我們定義一個新的btn3,但是父控件不是主窗口
self.btn3 = QPushButton('btn3') self.btn3.show() #注意:1btn3不屬於self,要主動被show()才可以 # 2btn要被調用,如果只被定義在show()以后會直接被釋放掉
運行的效果是這樣的,btn3是個獨立的控件
這個時候,如果用父控件的設置效果
self.setStyleSheet("QPushButton {background-color:yellow};")
只有self內的控件會變化
如果需要全局設置的話,要指定全局的QApplication對象,調用其對應的setStyleSheet方法。
app = QApplication(sys.argv) window = Window() window.show() app.setStyleSheet("QPushButton {background-color:yellow};")#調用全局的控件效果設置 sys.exit(app.exec_())
這樣就是應用程序內的所有控件都被設置了。
3.全局定位設置
還有一種情況,在全局設置的時候我們可以定位一個控件進行設置,但第一步是要將該空間定義一個objectname
label1.setObjectName('l1') self.setStyleSheet("QPushButton {background-color:yellow;} " "QLabel#l1 {background-color:red;}")
控件類型加了個ID選擇器
4.常規使用方法
由於一般情況下qss的字符串都比較長,為了方便使用我們一般把字符串另存在一個文件里(常規情況可以把文件后綴名定為.qss)
with open('test.qss','r') as f: qss = f.read() self.setStyleSheet(qss) #test.qss QLabel#l1 { background-color:yellow } QPushButton#b2{ background-color:red }
就把樣式和頁面的邏輯分開了。如果更改只要改qss文件就好了,比較便於修改。還有一點要注意的基礎知識:不同控件的objectname可以是一樣的!
三.QSS語法
QSS組成是由下面幾部分組成
QSS選擇器——用來再一次選擇控件,可以進行二次篩選
QSS偽狀態——指定不同的狀態
QSS聲明——指定樣式的字符串
我們下面一個個來講
四.QSS選擇器
1.作用:QSS選擇器主要用來指明哪些控件會收到樣式的作用,分為下面幾類,我們通過改上面的qss文件來分別演示
通配符選擇器
類型選擇器
類選擇器
ID選擇器
屬性選擇器
后代選擇器
字選擇器
子控件選擇器
2.通配符選擇器是匹配所有的控件,用星號表示
* { background-color:yellow }
3.類型選擇器是通過控件類型來匹配控件的(包括子類)
QWidget { background-color:yellow }
在這個例子里,因為QWIdget里有QLabel和QPushbutton兩個子類,所以所有的控件都會生效。(注意是父子類繼承的關系而不是從屬關系)
4.類選擇器也是通過控件類型來匹配控件的,但不同的是不包含子類,語法是在類前面加了個.(是個點)
.QWidget { background-color:yellow } 注意類前面有個點
這樣就只對QWidget生效,btn和label是不會變化的。
5.ID選擇器是和結合控件的objectname來匹配控件的,qss里objectname前加個井號來表示,不同控件的objectname是可以相同的
btn2.setObjectName('blue') btn1.setObjectName('blue')
qss的內容
#blue { background-color:blue }
6.屬性選擇器是結合控件的屬性值來匹配控件的,,首先要設定控件的屬性,qss里屬性用[proterty = attitude]來限制
label1.setProperty('notice_level','error') label2.setProperty('notice_level','warning')
然后定義qss文件
.QLabel { background-color:pink; } .QLabel[notice_level='warning'] { border:5px solid yellow; } .QLabel[notice_level='error'] { border:5px solid red; }
這里還有個用法,就是qss內只定義屬性值,只要有這個屬性的控件就可以被選中
.QLabel [notice_level]{ background-color:pink; } .QLabel[notice_level='warning'] { border:5px solid yellow; } .QLabel[notice_level='error'] { border:5px solid red; }
第一個qss定義了只要有novice_level這個屬性的控價都是生效的。
上圖中的標簽3是直接創建的,並沒有加屬性,所以只有標簽1和2有背景色。注意語法:控件和中括號之間是不能有空格的
7.后代選擇器是通過父控件(直接或間接)子控件來篩選控件,前面的代碼里主界面和box1,box1和label1之間都是直接包含的,而主界面和label1是間接包含的關系。
例如我們想把box2里的標簽設置個背景色,要怎么做?
首先給box2設置好objectname
box2.setObjectName('box2')
然后定義qss文件,就可以了,注意語法(空格的位置)
QWidget#box2 QLabel { background-color:pink; }
如果我們新建一個box3,一個label3放在box3里
box3 = QWidget(box2)
box3.move(300,0) label3 = QLabel('label3',box3)
因為box2是label3的間接父關系,索引上面的qss對label3也是生效的。
8.子選擇器是通過父控件的直接子控件來篩選控件的。語法是父控件后跟一個大於號>,還是上面那個帶box3的例子,qss文件是這樣的
QWidget#box2>QLabel { background-color:pink; }
這樣就只對box2里的QLabel控件生效,label3作為box的間接子控件是不變化的
9.子控件選擇器用來選擇一個復合控件上的子控件,語法是兩個冒號::

同一行中是有相同子控件的控件 QCheckBox,QRadioButton QCombBox, QSpinBox,QDateEdit,QTimeEdit,QDateTimeEdit QSlider QProgressBar QScrollBar QGroupBox QTableView 等待
我們可以通過特定的控件選擇空間的子控件比如我們創建個checkbox控件,把復選框的框框可以改下樣式(加了個圖片)
QCheckBox::indicator { image:url(../open.png); width:20px; height:20px; }
看下效果
但是會發現用鼠標點擊是不是沒效果了,如果改這種效果,我們就需要用到下一節的內容:qss偽裝態,我們后面再講。
10補充
上面講的各種選擇器是可以疊加使用的,用逗號分隔就可以了
QWidget#btn1,#btn2 { background-color:pink; }
五.QSS偽裝態
我們在上面一節第9條提到了偽裝態這個概念,他是限制控制只能在某種狀態下,被樣式表作用,語法是冒號:后面加關鍵字
選擇器:偽裝態
下面是常見的偽裝態

:checked button部件被選中 :unchecked button部件未被選中 :disabled 部件被禁用 :enabled 部件被啟用 :focus 部件獲得焦點 :hover 鼠標位於部件上 :pressed 部件被鼠標按下 :indeterminate checkbox或radiobutton被部分選中 :off 部件可以切換,且處於off狀態 :on 部件可以切換,且處於on狀態
這個就不演示了。但是要有注意的地方:
1.不同的控件會有某種特定的狀態,具體的要看官方文檔
2.可以用!否定,比如!checked表明沒被選中
3可以連接使用:
:hover:checked 表明鼠標指向並且選中時 :hover:!checked鼠標經過但沒有選中
六.QSS聲明
QSS聲明指明了控件會作用什么樣的樣式,以類似於字典的形式存在(分號分隔,而字典是逗號)
{
key:value; key:value; }
一般控件都符合名字叫做盒子模型的布局樣式,我們先看看這個樣式的概念
其中margin——外邊框
border——內邊框
content——內容
一個控件的尺寸定義好以后,就是上面圖中虛線的尺寸,而定義了外邊距‘、邊框和內邊距以后內容矩形是不停的被壓縮的,整體的尺寸是不會變的
1.邊框相關
針對邊框來說也有三個參數:樣式,寬度,顏色。而每個參數的設定可以一次設定四個參數(上右下左,以空格分割),也可以獨自設置
QLabel { background-color:yellow; border-width:5px; border-right-style:dotted; }
上面的代碼就是同時設置四個方向的邊框樣式
下面的就是樣式的代碼
none : 無邊框。與任何指定的border-width值無關 hidden : 隱藏邊框。IE不支持 dotted : 在MAC平台上IE4+與WINDOWS和UNIX平台上IE5.5+為點線。否則為實線(常用) dashed : 在MAC平台上IE4+與WINDOWS和UNIX平台上IE5.5+為虛線。否則為實線(常用) solid : 實線邊框(常用) double : 雙線邊框。兩條單線與其間隔的和等於指定的border-width值 groove : 根據border-color的值畫3D凹槽 ridge : 根據border-color的值畫菱形邊框 inset : 根據border-color的值畫3D凹邊 outset : 根據border-color的值畫3D凸邊
而如果想獨立設置,也可以分別按下面的方法指定
border-top-style border-right-style border-bottom-style border-left-style
而寬度的設定也可以直接指定,也可以指定邊框
border-top-width border-right-width border-bottom-width border-left-width
這里還有個要點,px(像素值) 和em(相對長度單位)
其中還有個換算單位
1em=16px
最后的顏色和前面的都一樣,可以統一設置也可以獨立設置,但是還有一點,除了直接指定顏色還可以直接指定rgb的值
QLabel{ border-left-color:reb(255,255,0) }
當然也可以設置16進制的值
#00ff00
最后看一下漸變色的設定。
a.線性漸變
線性漸變的思路是按下面的坐標系變化的
在定義坐標的時候,我們通常把x1,y1的值定為0,x2和y2的值定成1,然后可以指定好中間的顏色就可以了
QLabel { background-color:qlineargradient(x1:0, y1:0, x2:1, y2:1,stop:0 red,stop:0.4 gray,stop:0.8 orange,stop:1 green); }
比方上面的qss文件,出來的效果就是這樣的從0到1中間分了0.4和0.8兩個點,顏色定義了會和橙色。
這里有個隱藏用法:如果我們定義了x2或y2的值為0就成了垂直或水平漸變色的效果
b.輻射漸變
輻射漸變的思路是這樣的
其中XcYc是圓心的坐標點,r為漸變的半徑,XfYf為焦距點(可以以為是顏色最濃的地方)
QLabel { background-color:qradialgradient(cx:0.1,cy:0.1, radius:0.5,fx:0.9,fy:0.9,stop:0 red,stop:0.5 yellow,stop:1 orange); }
出來的效果
如果我們把cxcy都設為0.5,則是從中間開始變色的
c.角度漸變
角度漸變是先確認個中心點(Xc,Yc),再確定出起始角度(0度為水平向右,逆時針旋轉),然后圍着原點轉一圈開始漸變
QLabel { background-color:qconicalgradient(cx:0.5,cy:0.5, angle:90,stop:0 red,stop:0.5 yellow,stop:1 orange); }
然后我們在看一看邊框圓角
因為默認情況控件的邊框顯示的都是矩形,但有些時候我們希望把空間的角變成圓弧,那有什么參數呢?
從上面的圖可以看出來,我們以不同的半徑畫控件邊框的內切圓,由於是內切,三個圓的圓心都在角平分線上,要想要不同的弧度,只需指定好半徑即可(也可以指定角設定)
border-radius
border-top-left-radius
border-top-right-radius
border-bottom-right-radius
border-bottom-left-radius
如果把半徑設為控件的一般,效果就是圓了。
QLabel { border-style:groove; border-width:22px; border-radius:150px }
因為label的尺寸為300*300,我們把半徑設成150出來的效果就是圓形了
邊框圖片
然而有些時候邊框效果不太容易通過效果設置,比如這個效果
那么我們就可以直接設定邊框圖片
QLabel { background-color:yellow; border-image:url(無標題.png); }
但是要注意一下qss里是有個背景色的,由於圖片背景不是透明的,就會把背景色遮蓋
還有一點,如果設置的圖片如果尺寸比較小的話填滿控件后是會被拉伸的,例如我們把下面的圖片(20*20像素),添加在上面的label控件里結果就是被拉伸了
然后我們可以定義一個裁剪值和重復策略,意義是這樣的:
裁剪值:上——距離上邊的線(像素值)
下——距離下邊的線(像素值)
左——距離左邊的線(像素值)
右——距離右邊的線(像素值)
重復 :round——平鋪
repeat——重復
stretch——拉伸
我們來結合一個圖來看吧
我們用上面定義的分割線把一副圖分割成了9份,然后四個角上的圖是不會被拉伸的,剩下的2,4,6,8是會按照定義的拉伸策略被拉伸。這個沒有比較好的例子就不演示了。
QLabel { background-color:yellow; border-image:url(無標題.png)30px 30px 30px 30px round; border-width:35px }
2.外邊距(Margin)
外邊距和前面說的一樣,可以統一設置也可以分開設置
統一設置
margin (可以定義不同值,上、右、下、左)
分開設置
margin-top
margin-right
margin-bottom
margin-left
QLabel { background-color:yellow; border-style:solid; border-width:10px; margin:20px 40px 80px 160px }
可以看出來控件已經被截成矩形的了
3.內邊距
內邊距限制了控件內容顯示的區域范圍,設置的方法和上面的外邊距是一樣的
統一設置
padding (可以定義不同值,上、右、下、左)
分開設置
padding-top
padding-right
padding-bottom
padding-left
4.背景
背景(background)分下面幾條
background
background-color #顏色
background-image #圖片
background-repeat #重復
background-position #位置
background-origin
background-clip
background-attachment
我們一點點來搞清楚
a.background-color是沒什么好講的,可以直接加顏色,也可以加個rgb定義顏色
b.圖片和下面幾個可以一起來講,比方我們設定了個背景圖片
QLabel { background-image:url(test.png) }
效果如下:
是不是都重復了,這時候就需要用到重復的定義了(默認情況是xy軸方向都重復的)
background-repeat:no-repeat #只顯示一張(默認位置在左上角)
repeat-x #只重復x軸方向
repeat-y #只重復y軸方向
repeat-xy #重復xy軸兩個方向
因為如果我們用不重復的時候圖片顯示的位置是左上角(為了直觀我們加一個背景色來表明控件的位置和外觀)
QLabel {background-color:yellow; background-image:url(test.png); background-repeat:no-repeat }
顯示效果:
如果我們想要改變圖片的位置就可以通過下面的方法
background-position:
right
top
bottom
left
middle
用下面的qss代碼設置
QLabel {background-color:yellow; background-image:url(test.png); background-repeat:no-repeat; background-position:left middle }
出來的效果
注意看紅框框,因為我們設置的是背景,是不會遮蓋前面層的文本的。
下面看一下參照位置的用法
background-origin:
border
padding #默認值
content
我們把邊框顯示出來以后加上上面的圖
QLabel {background-color:yellow; border:20px double red; background-image:url(test.png); background-repeat:no-repeat; }
可以看出來,圖片是貼着邊框內部的
最后一個
background-attachment:
scroll
fixed
表示背景是否跟隨控件多余的部分滾動而滾動。如果我們的控件是一個可以滾動的控件(類似於QTextEdit)默認的scroll是背景隨着控件的滾動而滾動的
5.字體設置和前景色
字體設置的的方法和setfont()方法差不多,qss文件
#統一設置
font
#獨立設置
font-family
font-size
font-style
font-weight
其中字體形式分下面幾種
font-style:
normal
italic #斜體
oblique #傾斜
而字體的粗細除了可以用下面的單詞表示,也可以直接定義數值
font-weight:
bold #粗體
bolder #更粗
lighter #更細
100
200
300
400
。
。
。
由粗到細,400為normal,700等同於bold
還可以直接指定前景色
color
因為大部分的控件前景都為字符,設置了前景色就相當於改變了字符的顏色
看一下效果
QLabel {font-family:隸書; font-size:30px; font-style:oblique; font-weight:bold; color:red }
效果圖就不放了,自己腦補一個紅色的Label字符串好了。
6.最大最小
最大最小和直接調用設置最大最小尺寸的方法一樣
min-width:
max-width:
min-height:
max-height:
7.子控件控制
整體來看子控件控制Subcontrol有下面幾種模式
Subcontrol-Origin Subcontrol-Position 還可以細微調整 Top Bottom Left Right
我們用一個spinbox來演示上面的效果
from PyQt5.Qt import * import sys class Window(QWidget): def __init__(self): super().__init__() self.UI_test() def UI_test(self): spin = QSpinBox(self) spin.resize(300,300) spin.move(50,50) spin.setStyleSheet(""" """) if __name__ == '__main__': app = QApplication(sys.argv) window = Window() window.show() sys.exit(app.exec_())
然后我們通過改變qss代碼來改變他的效果,正常情況
QSpinBox { font-size:20px; color:red; border:10px double green; border-radius:10px }
效果如下
因為上下按鈕是屬於前景的,在改變了前景色的時候箭頭的顏色也變了。
如果我們想改變上箭頭的效果可以使用前面講到的子控件選擇器
QSpinBox { font-size:20px; color:red; border:10px double green; border-radius:10px } QSpinBox::up-button{ width:50px; height:50px; }
效果:
那我們想改變一下他們的位置就要用到這里的子控件控制了
QSpinBox { font-size:20px; color:red; border:10px double green; border-radius:10px } QSpinBox::up-button{ width:50px; height:50px; subcontrol-position:left middle } QSpinBox::down-button{ width:50px; height:50px; subcontrol-position:right middle }
效果:
而這一節說的微調主要是和偽裝態聯系用的,比方鼠標指向后bottom 10px意思就是鼠標指向控件后控件向下移動10個像素(這里還有個設定的東西,position:relative或者position:absolute),其中relative是相對原先控件的位置變化的像素值,而absolute就結合了origin的位置了。
QSpinBox::down-button:hover { position:absolute; top:100px; width:50px; height:50px; subcontrol-position:right middle }
上面就是語法結構的參考形式。
上面講的就是qss語法的基本結構,具體的偽裝態,子控件形式什么的可以看下Qt的官方文檔。
七.注意事項
1.級聯:
QSS可以在QApplication、父控件、子控件中設置,這個我們在上面已經說過了。而一個控件的最終樣式,會收到父控件以及QApplication的影響。
2.沖突
如果一個控件作為后代控件,被多個控件影響,則會不同屬性相互疊加,相同屬相產生覆蓋。這里還要考慮到特異性:同時設置的話要看那個優先級比較高:比如下面兩個按鈕
btn = QPushButton('b1',self) btn2 = QPushButton('b2',self) btn2.move(100,0) btn2.setObjectName('btn2') self.setStyleSheet(""" QPushButton { background-color:red } QPushButton#btn2 { background-color:green } """)
由於btn2是有特異性的,所以優先級比較高。出來的效果就是btn1為紅色,btn2為綠色。
而如果一樣的特異性,那就取最后一個。
八.三方包
有些時候我們可以通過導入三方包來使用別人已經做好的qss代碼,這里我在命令行可以安裝一個包
pip install qdarkgraystyle
然后在最后就可以直接使用(這里就放出來最后一段的代碼)
if __name__ == '__main__': app = QApplication(sys.argv) import qdarkgraystyle app.setStyleSheet(qdarkgraystyle.load_stylesheet_pyqt5()) window = Window() window.show() sys.exit(app.exec_())