• 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_())
  • 相关阅读:
    vue 动态绑定事件
    远程操作
    es6promise
    XMLHttpRequest
    canvas 学习
    charles
    调试抓包工具
    应用程序二进制接口ABI
    vim分屏操作
    汇编中的lea指令的作用
  • 原文地址:https://www.cnblogs.com/yinsedeyinse/p/11701466.html
Copyright © 2020-2023  润新知