目录
一、尺寸策略
widget的尺寸策略是是widget所在的布局处理widget外观(大小和位置)的依据。
widget在布局中则由尺寸策略决定其外观,不在布局中则由setGeometry()决定其外观。
在讨论尺寸策略之前先要了解一个函数:sizeHint() ,它返回一个QSize,是布局给widget提供的首选(默认)大小,不能通过代码设置。布局会根据widget的类型、内容等给widget实时提供一个合适的sizeHint,举个简单的例子,有个QLabel,给它的text设置成1和11的时候,他的sizeHint是不一样的。
PyQt5有7种策略,他们的值都是整数,如下表:
常量 | 值 | 描述 | 收缩 | 拉伸 |
---|---|---|---|---|
QSizePolicy::Fixed | 0 | 见名知意,widget的尺寸固定就是sizeHint()的值 | 不能 | 不能 |
QSizePolicy::Minimum | GrowFlag(1) | widget的尺寸不能比sizeHint()小。widget倾向于拉伸 | 不能 | 能 |
QSizePolicy::Maximum | ShrinkFlag(4) | widget的尺寸不能比sizeHint()大。 | 能 | 不能 |
QSizePolicy::Preferred | GrowFlag | ShrinkFlag(5) | sizeHint()是widget最合理的尺寸。这个策略是很多widget的默认策略。widget倾向于拉伸 | 能 | 能 |
QSizePolicy::Expanding | GrowFlag | ShrinkFlag | ExpandFlag(7) | sizeHint()对widget来说是一个合理的尺寸。widget倾向于拉伸 | 能 | 能 |
QSizePolicy::MinimumExpanding | GrowFlag | ExpandFlag(3) | widget的尺寸不能比sizeHint()小。widget倾向于拉伸 | 不能 | 能 |
QSizePolicy::Ignored | ShrinkFlag | GrowFlag | IgnoreFlag(13) | widget的尺寸可以任意变大变小。widget倾向于拉伸 | 能 | 能 |
拉伸优先级:
( Expanding = MinimumExpanding )
> ( Preferred = Ignored = Minimum ),就是说Expanding和Preferred放在一个布局里的话,会拉伸Expanding,收缩Preferred。
关于收缩:
Ignored:最小可以收缩到minimumSize(默认0),就是可以收缩到不显示widget了。
其他模式:无论怎么收缩,widget一定可以完整显示出来。
widget有两个属性:minimumSize和maximumSize,它们控制收缩和拉伸的边界,并且可以在代码中设置。
label = QLabel("123")
print(label.minimumSize())
print(label.maximumSize())
print(label.sizeHint())
"""
输出:
PyQt5.QtCore.QSize() # 说明默认最小边界是0,这也是Ignored模式下,widget为什么可以收缩到不显示
PyQt5.QtCore.QSize(16777215, 16777215) # 可以拉伸的最大值
PyQt5.QtCore.QSize(18, 12) # 这个大小正好够显示123的
"""
label.setMinimumSize(10, 100)
label.setMaximumSize(200, 200)
print(label.minimumSize())
print(label.maximumSize())
print(label.sizeHint())
"""
输出:
PyQt5.QtCore.QSize(10, 100) # 这样无论什么模式,widget都不会收缩到不显示了
PyQt5.QtCore.QSize(200, 200)
PyQt5.QtCore.QSize(18, 12) # 这说明设置边界是不会影响sizeHint()的
"""
注意:
按理来说setMaximumSize()应该只是设置拉伸边界,不该影响到尺寸策略的,但是在实际的测试中发现,一旦setMaximumSize()之后,给widget设置Fixed和Maximum策略没有效果了,widget是一定倾向于拉伸的,并且优先级默认是Preferred级别,可以通过设置成Expanding和MinimumExpanding来提高级别。
二、QHBoxLayout QVBoxLayout
QHBoxLayout和QVBoxLayout都继承自QBoxLayout,QHBoxLayout水平方向上排列widget,QVBoxLayout垂直方向上排列widget。它们只是排列的方向不同,用法完全一致。下面就以QHBoxLayout为例来说明。
QHBoxLayout添加widget的函数是
def addWidget(self,
a0: QWidget,
stretch: int = ...,
alignment: Alignment | AlignmentFlag = ...)
1、stretch
stretch是拉伸因子,默认值是0,控制widget的拉伸比例,看下图:
这个QHBoxLayout有两个widget:QLabel("123")和QLabel("2"),现在用的是Fixed策略,所以它们都没有拉伸,那么图上画的1、2、3三个区间就是总共的可拉伸区域,那么QLabel("123")和QLabel("2")分别应该拉伸多少呢?就是通过尺寸策略和拉伸因子共同控制,下面直接出结果:
1、如果两个stretch都是0的话,那就按上面说的拉伸优先级来:优先级高的拉伸全部空间,优先级低的不拉伸;优先级一样的话,空间均分。
2、如果两个stretch都不为0的话,拉伸优先级失效(即不存在优先级高的就拉伸的多),大家按各自的stretch为比例来拉伸空间,如stretch1=1,stretch2=2,则widget1拉伸1/3,widget2拉伸2/3
3、如果有stretch为0,有stretch不为0的话,不管拉伸优先级,stretch为0的不拉伸,stretch不为0的拉伸全部空间,如stretch1=0,stretch2=1
2、alignment
alignment是widget的对其方式(上、下、左、右、中间)
重要说明:alignment会在stretch和拉伸优先级的基础上消除widget在指定方向上的拉伸效果。
不绕不解释,直接上效果图:
QSizePolicy1=Expanding,stretch1=1
QSizePolicy2=Minimum,stretch2=2
QSizePolicy1=Expanding,stretch1=1
QSizePolicy2=Minimum,stretch2=2,alignment2=Qt.AlignLeft | Qt.AlignTop
QSizePolicy1=Expanding,stretch1=1
QSizePolicy2=Minimum,stretch2=2,alignment2=Qt.AlignLeft
QSizePolicy1=Expanding,stretch1=1
QSizePolicy2=Minimum,stretch2=2,alignment2=Qt.AlignTop
三、实现一个计算器界面
效果图:
代码:
class MyTestWindow(QMainWindow):
def __init__(self):
super().__init__()
# 窗口标题
self.setWindowTitle('计算器')
# 窗口大小
self.resize(300, 400)
self.setCentralWidget(QWidget())
# 把按钮的文字都设置成20px
self.setStyleSheet("""QPushButton{font-size:20px;}""")
# 主界面layout是一个QVBoxLayout
self.main_layout = QVBoxLayout()
# 用一个QLabel展示计算结果
self.l_result = QLabel('0')
self.l_result.setStyleSheet("""QLabel{background-color: red; font-size:40px;}""")
# 设置QLabel的文件靠右垂直居中
self.l_result.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
# 把l_result添加到主界面layout,拉伸因子是1
self.main_layout.addWidget(self.l_result, stretch=1)
# 按钮区域layout
self.operation_layout = QVBoxLayout()
# 设置layout内各控件之间的space为0
self.operation_layout.setSpacing(0)
# 设置layout内控件整体的外边距(可以理解成layout的内边距)
self.operation_layout.setContentsMargins(0, 0, 0, 0)
self.layout_l1 = QHBoxLayout()
self.layout_l1.setSpacing(0)
self.btn_init = QPushButton("AC")
self.btn_back = QPushButton("X")
self.btn_remainder = QPushButton("%")
self.btn_div = QPushButton("÷")
self.layout_l1.addWidget(self.btn_init)
self.layout_l1.addWidget(self.btn_back)
self.layout_l1.addWidget(self.btn_remainder)
self.layout_l1.addWidget(self.btn_div)
self.operation_layout.addLayout(self.layout_l1)
self.layout_l2 = QHBoxLayout()
self.layout_l2.setSpacing(0)
self.btn_7 = QPushButton("7")
self.btn_8 = QPushButton("8")
self.btn_9 = QPushButton("9")
self.btn_mul = QPushButton("x")
self.layout_l2.addWidget(self.btn_7)
self.layout_l2.addWidget(self.btn_8)
self.layout_l2.addWidget(self.btn_9)
self.layout_l2.addWidget(self.btn_mul)
self.operation_layout.addLayout(self.layout_l2)
self.layout_l3 = QHBoxLayout()
self.layout_l3.setSpacing(0)
self.btn_4 = QPushButton("4")
self.btn_5 = QPushButton("5")
self.btn_6 = QPushButton("6")
self.btn_sub = QPushButton("-")
self.layout_l3.addWidget(self.btn_4)
self.layout_l3.addWidget(self.btn_5)
self.layout_l3.addWidget(self.btn_6)
self.layout_l3.addWidget(self.btn_sub)
self.operation_layout.addLayout(self.layout_l3)
self.layout_l4 = QHBoxLayout()
self.layout_l4.setSpacing(0)
self.btn_1 = QPushButton("1")
self.btn_2 = QPushButton("2")
self.btn_3 = QPushButton("3")
self.btn_add = QPushButton("+")
self.layout_l4.addWidget(self.btn_1)
self.layout_l4.addWidget(self.btn_2)
self.layout_l4.addWidget(self.btn_3)
self.layout_l4.addWidget(self.btn_add)
self.operation_layout.addLayout(self.layout_l4)
self.layout_l5 = QHBoxLayout()
self.layout_l5.setSpacing(0)
self.btn_zero = QPushButton("0")
self.btn_point = QPushButton(".")
self.btn_equal = QPushButton("=")
# 大家根据stretch为比例来拉伸空间
self.layout_l5.addWidget(self.btn_zero, stretch=2)
self.layout_l5.addWidget(self.btn_point, stretch=1)
self.layout_l5.addWidget(self.btn_equal, stretch=1)
self.operation_layout.addLayout(self.layout_l5)
# 按钮区域layout的拉伸因子是5
self.main_layout.addLayout(self.operation_layout, stretch=5)
self.centralWidget().setLayout(self.main_layout)
self.bat_set_policy()
def bat_set_policy(self):
for field in self.__dir__():
if field.startswith("btn_"):
# QPushButton的vPolicy默认是Fixed,要改成可拉伸的策略
getattr(self, field).setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)