今天是一个大课题: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); 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-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-
max-
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_())