GUI学习之三十四——QSS样式表

今天是一个大课题: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_())
原文地址:https://www.cnblogs.com/yinsedeyinse/p/11701466.html