GUI学习之二十七——布局管理学习总结

Posted yinsedeyinse

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了GUI学习之二十七——布局管理学习总结相关的知识,希望对你有一定的参考价值。

今天讲一个大的内容——布局管理。

一.布局管理的诞生背景

在前面所讲的所有案例中,我们都是用采用手动布局的方式来布局的。结合个案例来说明一下:在一个界面上放三个label,三个label纵向排列

技术图片
from PyQt5.Qt import *
import sys

class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.resize(600,400)
        self.UI_test()


    def UI_test(self):
        label1 = QLabel(第一个,self)
        label1.setStyleSheet(background-color:yellow)
        label1.resize(self.width(),self.height()/3)

        label2 = QLabel(第二个, self)
        label2.setStyleSheet(background-color:red)
        label2.resize(self.width(), self.height() / 3)
        label2.move(0,label1.height())


        label3 = QLabel(第三个, self)
        label3.setStyleSheet(background-color:green)
        label3.resize(self.width(), self.height() / 3)
        label3.move(0,label1.height()*2)
        pass
if __name__ == __main__:
    app = QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec_())
手动布局

效果图

技术图片

这种方法是我们前面一直在用的,但是存在一些问题,

1.如果我们通过拖拽改变了界面的尺寸,label的尺寸是不会跟着变的

技术图片

 2.给子控件加一个定时器,让内容不停刷新内容,就会发现label的尺寸限制了内容的显示

技术图片
from PyQt5.Qt import *
import sys

class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.resize(600,400)
        self.UI_test()


    def UI_test(self):
        label1 = QLabel(1,self)
        label1.setStyleSheet(background-color:yellow)
        label1.resize(self.width(),self.height()/3)

        label2 = QLabel(第二个, self)
        label2.setStyleSheet(background-color:red)
        label2.resize(self.width(), self.height() / 3)
        label2.move(0,label1.height())


        label3 = QLabel(第三个, self)
        label3.setStyleSheet(background-color:green)
        label3.resize(self.width(), self.height() / 3)
        label3.move(0,label1.height()*2)

        timer = QTimer(self)
        # timer.timeout.connect(lambda :label1.setText(str(int(label1.text()+1))+‘\\n‘))
        timer.timeout.connect(lambda :label1.setText(label1.text()+123\\n))
        timer.start(1000)
        pass
if __name__ == __main__:
    app = QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec_())
View Code

技术图片

注意第一个label,内容已经被限制住了。

3.有些情况下我们想实现把label2这个控件隐藏,然后把label3顶上去,这个方法也是不行的。(很简单的代码,加个label2.hide()就可以了,就不演示了)

这个时候就可以引入布局管理了

技术图片
from PyQt5.Qt import *
import sys

class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.resize(600,400)
        self.UI_test()


    def UI_test(self):
        label1 = QLabel(1,self)
        label2 = QLabel(2,self)
        label3 = QLabel(3,self)
        label1.setStyleSheet(background-color:yellow)
        label2.setStyleSheet(background-color:red)
        label3.setStyleSheet(background-color:green)

        v_layout = QVBoxLayout()
        v_layout.addWidget(label1)
        v_layout.addWidget(label2)
        v_layout.addWidget(label3)

        btn = QPushButton(click,label1)
        btn.clicked.connect(self.fun)

        self.setLayout(v_layout)

        timer = QTimer(self)
        timer.timeout.connect(lambda :label1.setText(label1.text()+123\\n))
        timer.start(1000)

        self.label2 = label2

    def fun(self):   #隐藏label2
        if self.label2.isVisible():
            self.label2.setVisible(False)
        else:self.label2.setVisible(True)
if __name__ == __main__:
    app = QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec_())
布局管理器效果演示

我们用布局管理器的布局方式就改善了上面说的问题。

二.布局管理器的概念

  1.布局管理器是Qt里一个包含布局管理器类的集合,他们被用来描述控件如何在应用程序的用户界面中呈现。

  2.当可用空间发生变化时,布局管理器会自动调整控件的大小和位置。

  3.布局管理器不是界面控件(不是QWidget的子类),二十界面控件的‘定位策略’。

  3.所有的QWidget类别及其子控件都可以用布局管理器来管理他们的子控件(布置子控件、调整大小、当内容发生变化时自动更新等)。

三.布局管理器的继承关系

技术图片

就是这种布局策略,比如水平布局、垂直布局等。

四.布局管理器使用方式

大概的使用方法可以归纳为下面几条

  1.创建布局对象:不用设置父对象,直接定义

layout = QHboxLayout()

  2.设置布局对象参数:包括内边距、外边距、对齐方式等。

  3.把管理器设置给需要布局的父控件,还可一调整方向(从左向右/从右向左)

self.setLayout(layout)

  4.把需要布局的控件添加在布局管理器中。

五.基类——QLayout

  我们先看一下各种layout的基类——QLayout,它抽象了各种类的共性,先看看它常用的功能:

  1.内部控件之间的间距

#设置间距
QLayout.setSpacing(self, a0: int)
#获取间距
QLayout.spacing()-> int

间距的设定是和layout的形式有关的,当 layout为水平布局的时候就是水平间距,如果是垂直布局就是垂直间距。

  2.外边距

#设置
QLayout.setContentsMargins(self, left: int, top: int, right: int, bottom: int)
#获取
QLayout.contentsMargins()-> QtCore.QMargins

  获取的外边距是一个对象,可以调用它的方法函数来获取值

  3.添加子控件

QLayout.addWidget(self, w: QWidget)

  4.替换子控件

QLayout.replaceWidget(self, from_: QWidget, to: QWidget, options: typing.Union[QtCore.Qt.FindChildOptions, QtCore.Qt.FindChildOption] = ...)
#QLayout.replaceWidget(被替换对象,替换对象)

  这里要注意一点:被替换的控件不会被布局管理器管理,有些时候会被父控件引用。这时候我们有三种方式可以用

    a.隐藏——QWidget.hide()

    b.删除

    c.添加到新的布局中

  如果仅仅被隐藏是不会被内存释放的,如果要删除是需要让他没有被引用就行了,需要这么设定

QWidget.setParent(None)

 

  把他的父控件设为空,就会被删除了。

  5.添加子布局

  布局也是可以嵌套的,我们把上面演示的label中间在插入一个布局,达到下面的结果

技术图片

代码就是利用了布局管理器的嵌套

技术图片
from PyQt5.Qt import *
import sys

class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.UI_test()


    def UI_test(self):

        layout = QBoxLayout(QBoxLayout.LeftToRight)
        self.setLayout(layout)

        label1 = QLabel(1)
        label2 = QLabel(2)
        label3 = QLabel(3)
        label4 = QLabel(4)
        label5 = QLabel(5)
        label6 = QLabel(6)
        label7 = QLabel(7)

        label1.setStyleSheet(background-color:yellow)
        label2.setStyleSheet(background-color:red)
        label3.setStyleSheet(background-color:green)
        label4.setStyleSheet(background-color:orange)
        label5.setStyleSheet(background-color:blue)
        label6.setStyleSheet(background-color:pink)
        label7.setStyleSheet(background-color:cyan)

        layout_h = QVBoxLayout()  #子布局

        layout_h.addWidget(label5)
        layout_h.addWidget(label6)
        layout_h.addWidget(label7)

        layout.addWidget(label1)
        layout.addWidget(label2)
        layout.addLayout(layout_h)  #把子布局传给父布局
        layout.addWidget(label3)
        layout.addWidget(label4)

        pass
if __name__ == __main__:
    app = QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec_())
布局管理器嵌套

这里就是一个要注意的点:不管是父级的还是子一级的,都是按照添加的顺序在界面上排列的。

  6.能用性

  没什么要说的,可以屏蔽管理器的效果

QLayout.setEnabled(self, a0: bool
QLayout.isEnabled()-> bool

六.盒子布局(QBoxLayout)

  QBoxLayout已经为我们封装好了两个子类:水平布局(QHBoxLayout)和垂直布局(QVBoxLayout),当然我们也可以直接用QBoxLyaout然后指定一个方向也是可以的。

#QboxLayout的方向和枚举值  
LeftToRight = ... # type: ‘QBoxLayout.Direction‘——0
RightToLeft = ... # type: ‘QBoxLayout.Direction‘——1
TopToBottom = ... # type: ‘QBoxLayout.Direction‘——2
BottomToTop = ... # type: ‘QBoxLayout.Direction‘——3
Down = ... # type: ‘QBoxLayout.Direction‘——2
Up = ... # type: ‘QBoxLayout.Direction‘——3

看一看他其他的功能作用

  1.修改方向

  在指定方向并添加元素以后也是可以对方向进行修改的

QBoxLayout.setDirection(self, a0: QBoxLayout.Direction)
QBoxLayout.direction()-> QBoxLayout.Direction(int)

我们试一下,每一秒换一个方向

技术图片
from PyQt5.Qt import *
import sys

class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.resize(300,300)
        self.UI_test()


    def UI_test(self):

        layout = QBoxLayout(QBoxLayout.LeftToRight)
        self.setLayout(layout)
        label1 = QLabel(1)
        label2 = QLabel(2)
        label3 = QLabel(3)
        label4 = QLabel(4)
        label5 = QLabel(5)
        label6 = QLabel(6)
        label7 = QLabel(7)

        label1.setStyleSheet(background-color:yellow)
        label2.setStyleSheet(background-color:red)
        label3.setStyleSheet(background-color:green)
        label4.setStyleSheet(background-color:orange)
        label5.setStyleSheet(background-color:blue)
        label6.setStyleSheet(background-color:pink)
        label7.setStyleSheet(background-color:cyan)

        layout.addWidget(label1)
        layout.addWidget(label2)
        layout.addWidget(label3)
        layout.addWidget(label4)

        def fun():
            layout.setDirection((layout.direction()+1)%4)   #因为方向的枚举值为0-3,我们取值后加1对4求模就可以了!

        timer = QTimer(self)
        timer.timeout.connect(fun)
        timer.start(1000)

        pass


if __name__ == __main__:
    app = QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec_())
改方向案例

  2.元素操作

  a.添加控件

   添加控件有两种:追加和插入,先指定插入的位置的索引值,在给定待插入的控件就行。如果所引值大于现有的控件数就和直接添加一样了。

QBoxLayout.addWidget(self, a0: QWidget, stretch: int = ..., alignment: typing.Union[QtCore.Qt.Alignment, QtCore.Qt.AlignmentFlag] = ...)
QBoxLayout.insertWidget(self, index: int, widget: QWidget, stretch: int = ..., alignment: typing.Union[QtCore.Qt.Alignment, QtCore.Qt.AlignmentFlag] = ...)

  b.添加子布局、替换子布局是继承父类的用法,没什么好说的

  c.移除控件

QBoxLayout.removeWidget(self, w: QWidget)

  控件被移除以后会被推送给父控件上,和替换控件一样,还是需要把他隐藏或删除。如果使用hide方法隐藏,在再次用show方法后会直接参与到布局内。

  d.添加空白

  我们在布局里添加一段空白,就要用到下面的代码

QBoxLayout.addSpacing(self, size: int)

  添加的空白的尺寸是固定的,是不随着界面的拖拽改变尺寸而变化。

  空白也可以插入,但要注意的是,如果插入空白的位置前面有空白,前面的空白是不占用索引的

layout.insertSpacing(self, index: int, size: int)

   3.伸缩因子

  首先来看一下占位比例,我们前面添加的控件都是将图层等分的,其实我们还可以在添加控件的时候指定一个比例,

layout.addWidget(label1,1)
layout.addWidget(label2,2)
layout.addWidget(label3,1)

出来的效果

技术图片

  看到了吧,是按照控件后面指定的比例分布的。

  返回来,我们在添加空白的时候,添加的空白区域是不会随着界面尺寸而变化的,这里可以添加一个伸缩因子

QBoxLayout.addStretch(self, stretch: int = ...)

  通过这个方法添加的空白是可以随着界面的变化而变化的。但是有个前提,在界面尺寸过小的时候有些时候是要保证控件里内容能够完全显示的,这时候弹簧因子就会被压缩,就像下面的图一样(标签1和2之间是有个空白的)

技术图片

如果这样,我们不定义拉伸的量

layout.addWidget(label1)
layout.addStretch()
layout.addWidget(label2)
layout.addStretch()
layout.addWidget(label3)

  弹性因子的优先级会比较高,在界面尺寸变化时,控件的尺寸不会变化,只有两个弹性因子的尺寸变化。

  那么如果我们需要哪个控件可以跟着伸缩,就可以用下面的代码把他设置为可伸缩的,或者把某个控件改成不可伸缩的。

layout.setStretchFactor(self, w: QWidget, stretch: int) -> bool: ...

  注意这个方法是有返回值的,如果控件在这个图层中就会返回1,否则返回0。

七.表单布局(QFormLayout)

 1.基础概念

  表单布局主要用于管理输入控件及其关联标签的形式,他以两列的形式列出其子元素,左侧由标签组成,右列由"字段"小部件组成(行编辑器、按钮等)。就像下面的效果

技术图片

如图所示,界面左边都是对右面控件描述的label,这就是表单布局的作用让我们看一看用法

技术图片
from PyQt5.Qt import *
import sys

class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.UI_test()


    def UI_test(self):
        layout = QFormLayout()
        label_name = QLabel(name)
        label_age = QLabel(age)
        label_sex = QLabel(sex)

        name_input = QLineEdit()
        age_input = QSpinBox()

        male_btn = QRadioButton()
        female_btn = QRadioButton()

        sex_layout = QHBoxLayout()
        sex_layout.addWidget(male_btn)
        sex_layout.addWidget(female_btn)


        btn = QPushButton(提交)

        self.setLayout(layout)
        layout.addWidget(label_name)
        layout.addWidget(name_input)
        layout.addRow(label_name,name_input)
        layout.addRow(label_age,age_input)
        layout.addRow(label_sex,sex_layout)    #添加对象为布局
        layout.addRow(btn)

        pass
if __name__ == __main__:
    app = QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec_())
表单布局

我们看一下addRow的构造函数,发现用法有这么多:

def addRow(self, label: QWidget, field: QWidget) -> None: ...
def addRow(self, label: QWidget, field: QLayout) -> None: ...
def addRow(self, labelText: str, field: QWidget) -> None: ...
def addRow(self, labelText: str, field: QLayout) -> None: ...
def addRow(self, widget: QWidget) -> None: ...
def addRow(self, layout: QLayout) -> None: ...

仔细看看其中有一个方法是可以直接传一个字符串的(第三个),我们把添加行的代码换成这样的

layout.addRow(姓名&n:,name_input)
layout.addRow(年龄&a:,age_input)
layout.addRow(性别:,sex_layout)

发现也是可以的,并且可以直接用快捷键了。

  插入行的方法和前面的方法一样,就是多了一个索引,如果设置的索引超出现有的行数就加在最后一行。

def insertRow(self, row: int, label: QWidget, field: QWidget) -> None: ...
def insertRow(self, row: int, label: QWidget, field: QLayout) -> None: ...
def insertRow(self, row: int, labelText: str, field: QWidget) -> None: ...
def insertRow(self, row: int, labelText: str, field: QLayout) -> None: ...
def insertRow(self, row: int, widget: QWidget) -> None: ...
def insertRow(self, row: int, layout: QLayout) -> None: ...

 所以,在每行都有一个角色的定义

QFormLayout.ItemRole
LabelRole = ...  # type: ‘QFormLayout.ItemRole‘(左边的)
FieldRole = ...  # type: ‘QFormLayout.ItemRole‘(右边的)
SpanningRole = ...  # type: ‘QFormLayout.ItemRole‘占据整行

  2.获取行的信息

QFormLayout.rowCount()->int    #获取总行数
QFormLayout.getWidgetPosition(self, widget: QWidget)-> typing.Tuple[int, QFormLayout.ItemRole]#获取控件位置
QFormLayout.getLayoutPosition(self, layout: QLayout)-> typing.Tuple[int, QFormLayout.ItemRole]#获取图层位置

返回的控件位置是一个元组,第一位是行数,第二位是角色的枚举值。

有了获取行的信息以后就可以对指定的行进行设置

QFormLayout.setWidget(self, row: int, role: QFormLayout.ItemRole, widget: QWidget)
QFormLayout.setLayout(self, row: int, role: QFormLayout.ItemRole, layout: QLayout)

这个看字面的含义就可以了,不需要在说什么了,但是要注意一点:如果被设置的位置是有控件的(控件没有占一整行),就会出现错误,如果原先位置的控件是占了一整行,可以加在该控件左边。

  3.移除行

  移除行有两种:

    a.移除时删除子控件(不知道为移除行的方法可以用,但是需要手敲出来,不会联想,ctrl键也查不到内容)

QFormLayout.removeRow(self,int)
QFormLayout.removeRow(self,QWidget)
QFormLayout.removeRow(self,QLayout)

    b.移除时保留控件。这种方式要注意移除后的操作,应该对移除行的控件有对应的操作否则整体布局会混乱。

QFormLayout.takeRow(self,int)
QFormLayout.takeRow(self,QWidget)
QFormLayout.takeRow(self,QLayout)

  移除的时候时移除一整行,即便传递参数为控件或图层,那么也是移除该控件所占的行。

   4.标签操作

  因为在上面的构造函数里面讲了,在创建图层的时候可以用str代替一个QLabel,那么如果我想把这个字符串修改一下,那要怎么办呢?我们可以以QLabel的形式获取这个控件

QFormLayout.labelForField(self, field: QWidget)-> QWidget

  截一段代码看看这是怎么用的

layout.addRow(name, name_input)   #获取控件
layout.labelForField(name_input).setText(姓名)   #设置控件

   5.行的包装策略

  有些时候会出现标签里内容过长,可以依靠行的包装策略来改变显示效果

QFormLayout.setRowWrapPolicy(QFormLayout.WrapAllRows)
# 枚举值 type: ‘QFormLayout.RowWrapPolicy‘
# DontWrapRows = ...  # 字段在标签旁
# WrapLongRows = ...  # 变迁被赋予足够的水平空间以适合最宽的标签,其余的空间被赋予字段
                      # 如果字段的最小大小比可用空间宽,则该字段换到下一行
# WrapAllRows = ...  #  字段在标签下方

  第二个方法主要意思是当如果水平空间够的话是水平放置,在竖直空间够且水平空间不足的时候会把自动字段放在标签下面

  6.对齐方式

  默认的对齐方式是水平方向的左对齐,竖直方向的上对齐,如果想改的话可以这么做

QFormLayout.setAlignment(self, w: QWidget, alignment: typing.Union[QtCore.Qt.Alignment, QtCore.Qt.AlignmentFlag])
#枚举值
AlignLeft = ...  # type: ‘Qt.AlignmentFlag‘
AlignLeading = ...  # type: ‘Qt.AlignmentFlag‘
AlignRight = ...  # type: ‘Qt.AlignmentFlag‘
AlignTrailing = ...  # type: ‘Qt.AlignmentFlag‘
AlignHCenter = ...  # type: ‘Qt.AlignmentFlag‘
AlignJustify = ...  # type: ‘Qt.AlignmentFlag‘
AlignAbsolute = ...  # type: ‘Qt.AlignmentFlag‘
AlignHorizontal_Mask = ...  # type: ‘Qt.AlignmentFlag‘
AlignTop = ...  # type: ‘Qt.AlignmentFlag‘
AlignBottom = ...  # type: ‘Qt.AlignmentFlag‘
AlignVCenter = ...  # type: ‘Qt.AlignmentFlag‘
AlignVertical_Mask = ...  # type: ‘Qt.AlignmentFlag‘
AlignCenter = ...  # type: ‘Qt.AlignmentFlag‘
AlignBaseline = ...  # type: ‘Qt.AlignmentFlag‘

  7.间距设置

QFormLayout.setVerticalSpacing(self, spacing: int)  #垂直间距
QFormLayout.setHorizontalSpacing(self, spacing: int)#水平间距

  8.字段拉伸策略

  说实话我这个实在是没搞明白是什么效果,以后用到的时候再来补充吧!

QFormLayout.FieldGrowthPolicy(QFormLayout.FieldsStayAtSizeHint)
#枚举值
FieldsStayAtSizeHint = ...  # type: ‘QFormLayout.FieldGrowthPolicy‘
ExpandingFieldsGrow = ...  # type: ‘QFormLayout.FieldGrowthPolicy‘
AllNonFixedFieldsGrow = ...  # type: ‘QFormLayout.FieldGrowthPolicy‘

 八.网格布局QGridLayout

  1.基础概念,添加控件

layout = QGridLayout()
layout.addWidget(self, w: QWidget)
layout.addWidget(self, a0: QWidget, row: int, column: int, rowSpan: int, columnSpan: int, alignment: typing.Union[QtCore.Qt.Alignment, QtCore.Qt.AlignmentFlag] = ...)

如果我们直接插入控件的话,效果就和QBoxLayout一样的。但是,如果在添加控件的时候加上行列号,就能出现这样的效果

layout.addWidget(label1,0,0)
layout.addWidget(label2,0,1)
layout.addWidget(label3,1,0)
layout.addWidget(label4,1,1)

技术图片

第三种添加控件的方法是可以实现类似表格合并单元格的效果在行列号后跟的是该控件所占的行数和列数。

layout.addWidget(label1,0,0)
layout.addWidget(label2,0,1)
layout.addWidget(label3,1,0,2,3)   #控件所在位置为第1行第0列,占了2行3列的位置

效果如下

技术图片

注意第0行,由于只有两个控件,但label3占了3列的位置,第0行就空出来了一个位置

  2.列高、行宽和拉伸系数

QGridLayout.setColumnMinimumWidth(self, column: int, minSize: int)    #设置最小列宽
QGridLayout.setColumnStretch(self, column: int, stretch: int)        #设置行拉伸系数
QGridLayout.setRowMinimumHeight(self, row: int, minSize: int)       #设置最小列宽
QGridLayout.setRowStretch(self, row: int, minSize: int)             #设置列拉伸系数

设置拉伸系数时应该把行或列全部设置了,如果某一行或列没设置而其他的行或列设置了这行就会被压缩至最小(默认拉伸系数为0)

   3.间距设置

QGridLayout.setVerticalSpacing(self, spacing: int)          #设置垂直间距
QGridLayout.setHorizontalSpacing(self, spacing: int)        #设置水平间距
QGridLayout.verticalSpacing() -> int: ...                   #获取垂直间距
QGridLayout.horizontalSpacing() -> int: ...                 #获取水平间距

  要注意的一点,如果间距不一致,则返回值为-1。

  4.信息获取

  a.获取行列数

QGridLayout.rowCount()      #获取行数
QGridLayout.columnCount()      #获取列数

  行数和列数的获取没什么好讲的,返回值就是个int

  b.获取指定控件

QGridLayout.itemAt(self, a0: int)   -> QLayoutItem  #获取指定item
QGridLayout.itemAtPosition(self, row: int, column: int)   -> QLayoutItem  #获取指定行列除的item

  获取指定的item,获取的控件可以加widget()方法来操作

  c.获取指定控件的尺寸

QGridLayout.cellRect(self, row: int, column: int)   self, row: int, column: int

  但这个函数有个问题,在现在使用的PyQt5这个版本中,必须等父控件完全显示完后使用才能获得控件的尺寸。否则返回的就是个空的值。所以要在这里用

if __name__ == __main__:
    app = QApplication(sys.argv)
    window = Window()
    window.show()
    layout = window.layout()
    print(layout.cellRect(0,1))
    sys.exit(app.exec_())

 九.堆叠布局(QStackedLayout)

  堆叠布局的效果主要实现了图层切换的效果。但是要注意一点,在定义了布局以后要直接设置图层,而不要在添加控件以后再设置

#方法1
layout = QStackedLayout()
self.setLayout(layout)
#方法2
layout = QStackedLayout(self)

  a.添加控件

QStackedLayout.addWidget(self, w: QWidget)                   -> int
QStackedLayout.insertWidget(self, index: int, w: QWidget)    -> int

  注意,堆叠布局的添加或插入控件时是会返回一个索引值的。如果是插入控件的话会把移动的原有控件的索引值加1。并且如果插入的位置是0,默认显示的也是原先索引值为0的控件。

  b.显示不同的控件

QStackedLayout.setCurrentIndex(self, index: int)  #按索引要求显示
QStackedLayout.setCurrentWidget(self, w: QWidget) #指定控件显示
技术图片
from PyQt5.Qt import *
import sys

class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.UI_test()


    def UI_test(self):
        label1 = QLabel(标签1)
        label2 = QLabel(标签2)
        label3 = QLabel(标签3)
        label4 = QLabel(标签4)
        label5 = QLabel(标签5)
        label6 = QLabel(标签6)
        label7 = QLabel(标签7)

        label1.setStyleSheet(background-color:yellow)
        label2.setStyleSheet(background-color:red)
        label3.setStyleSheet(background-color:green)
        label4.setStyleSheet(background-color:orange)
        label5.setStyleSheet(background-color:blue)
        label6.setStyleSheet(background-color:pink)
        label7.setStyleSheet(background-color:cyan)

        layout = QStackedLayout()
        self.setLayout(layout)

        layout.addWidget(label1)
        layout.addWidget(label2)
        layout.addWidget(label3)
        layout.addWidget(label4)

        timer = QTimer(self)
        timer.timeout.connect(lambda :layout.setCurrentIndex(((layout.currentIndex()+1)%layout.count())))
        timer.start(1000)


        pass
if __name__ == __main__:
    app = QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec_())
切换显示控件案例

  c.信号

  常用的信号:

QStackedLayout.currentChanged(self, index: int)     #控件切换
QGridLayout.widgetRemoved(self, index: int)         #控件移除

  信号传递了当前显示控件的索引值。

  d.展示模式

  展示模式限定了控件的展示模式。如果我们把上面案例的label1隐藏以后,可以发现所有的控件都不显示了。所以默认情况下布局的展示模式是只有当前的控件可见

QStackedLayout.setStackingMode(self, stackingMode: QStackedLayout.StackingMode)
QStackedLayout.stackingMode()-> int
# type: ‘QStackedLayout.StackingMode‘
StackOne = ...  # 只有当前显示的控件可见,默认情况(0)
StackAll = ...  # 所有控件都是可见的,当前显示的控件显示在最前(1)

 十.控件尺寸策略

  在掌握了上面所讲的各种布局方式,我们就可以结合各种布局方式完成界面的布局。那么还要补充一点就是控件尺寸的策略。

  我们首先要了解这样一个用法

QWidget.sizeHint() -> QtCore.QSize       #合适的建议大小
QWidget.minminiimumSizeHint() -> QtCore.QSize   #最小的建议大小

  这个是合适的建议大小(注意,和size())是不冲突的两个用法,到要注意,layout是永远不会把一个控件的尺寸是指的比minminiimumSizeHint指定的尺寸还要小,除非设置了最小从尺寸和尺寸策略设置为Ignored

技术图片
from PyQt5.Qt import *
import sys
class Label(QLabel):
    def minimumSizeHint(self):
        return QSize(100,100)
class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.UI_test()


    def UI_test(self):
        label1 = Label(标签1)   #注意这里用的是Label,对QLabel重构了一下
        label2 = QLabel(标签2)
        label3 = QLabel(标签3)


        label1.setStyleSheet(background-color:yellow)
        label2.setStyleSheet(background-color:red)
        label3.setStyleSheet(background-color:green)


        layout = QHBoxLayout(self)
        self.setLayout(layout)

        layout.addWidget(label1)
        layout.addWidget(label2)
        layout.addWidget(label3)

        print(label1.sizeHint())
        print(label1.size())

        pass
if __name__ == __main__:
    app = QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec_())
建议最小尺寸

  在定义了最小尺寸以后,可以看一下

技术图片

  标签的最小尺寸就是100*100,但还是可以随着界面的拉伸相应变化的。而建议尺寸就是启动时的显示状态(没有最小限制)。

  其次我们要了解一下控件的拉伸策略。我们前面所演示的控件都是QLabel,它是在水平方向和垂直方向都可以拉伸的,可以试一下QPushButton或者QLineedit,它们默认都只能水平拉伸。但是Qt对每种控件的拉伸策略都给出了设定的方法

QWidget.setSizePolicy()
# type: ‘QSizePolicy.Policy‘
Fixed = ... # 不拉伸,尺寸值参考sizeHint()
Minimum = ... # 可以伸展收缩,但sizeHint()的返回值回定了控件的最小尺寸
Maximum = ... # 可以伸展收缩,但sizeHint()的返回值回定了控件的最大尺寸
Preferred = ... # 可以伸缩,但没有获取更大控件的优势
Expanding = ... # 可以伸缩,并会尽可能多的去获取额外的控件
MinimumExpanding = ... # 和Expanding差不多,sizeHint()的返回值规定了控件的最小尺寸
Ignored = ... # 忽略建议尺寸大小

注意,策略的设置可以统一一起设置,还可以分开设置

QWidget.setSizePolicy(self, a0: QSizePolicy)  #统一设置
QWidget.setSizePolicy(self, hor: QSizePolicy.Policy, ver: QSizePolicy.Policy)    #分别设置了水平/垂直策略

还有一些其他的策略是用下面的方式定义的

QSizePolicy()

具体的用法可以用Ctrl键联想。但是要注意一点,有些方法是要在设置以前使用的

sp = QSizePolicy(QSizePolicy.Fixed,QSizePolicy.Fixed)
sp.setRetainSizeWhenHidden(True)
label1.setSizePolicy(sp)

label1.hide()

上面的方法就是在控件被隐藏的时候还占据位置。方法在设置以前就要被设置,否则是没用的。

以上是关于GUI学习之二十七——布局管理学习总结的主要内容,如果未能解决你的问题,请参考以下文章

GUI学习之二十三——QDialog学习总结

GUI学习之三——QObject学习总结

GUI学习之十九——QFontComboBox学习总结

GUI学习之十四——QAbstractSpinBox学习总结

GUI学习之四——QWidget控件学习总结

Linux学习总结(二十七)任务计划,系统服务管理