本文主要讲解Python3 PyQt5的布局,详细地介绍布局如何使用。
PyQt5中有哪些呢,可以利用Pycharm的自动补全找出来, 输入from PyQt5.QtWidgets import QLayout
,等代码补全出来,显示的就算布局相关可以使用的类或者其基类(除绝对布局外), 善用代码提示。
主要有以下:
from PyQt5.QtWidgets import QHBoxLayout, QLayout, \
QVBoxLayout, QFormLayout, QGridLayout, \
QBoxLayout, QLayout, QStackedLayout, QGraphicsGridLayout,\
QGraphicsAnchorLayout, QGraphicsLayout, QGraphicsLinearLayout, \
QLayoutItem,\
QGraphicsLayoutItem
按继承可以分成两部分, 一部分是继承自QLayout
(QLayout
继承自QLatoutItem
), 一部分继承自QGraphicsLayout
(而QGraphicsLayout
继承自QGraphicsLayoutItem
),关系图如下:
本文主要内容:
实例皆以此代码为基础实现:
import sys
from PyQt5.QtWidgets import QApplication, QWidget
class Window(QWidget):
"""
窗体类
"""
def __init__(self):
super().__init__()
self.setup_ui()
def setup_ui(self):
"""
设置控件
"""
pass
if __name__ == '__main__':
app = QApplication(sys.argv)
w = Window()
w.setWindowTitle('嵌套布局')
w.show()
sys.exit(app.exec_())
该段代码的执行效果是一个空面板,只有一个标题,如图:
下面开始讲解各个布局的使用。
绝对布局可以直接定位每个空间的坐标。
绝对布局在改变窗体大小之后不会自适应,所以看起来很乱,甚至改变个字体都会影响布局,新添加一个控件需要修改布局,那么所有控件都要进行修改,非常繁琐。
绝对布局主要使用到以下两个方法:
实例在窗体上摆放标签和按钮:
import sys
from PyQt5.QtWidgets import QApplication, QWidget
from PyQt5.QtWidgets import QLabel, QPushButton
from PyQt5.QtCore import QPoint
class Window(QWidget):
"""
"""
def __init__(self):
super().__init__()
self.setup_ui()
def setup_ui(self):
label1 = QLabel('标签1', self)
label2 = QLabel('标签2', self)
button = QPushButton('按钮', self)
point = QPoint(10, 20) # 定义一个点坐标
label1.move(point) # 移动到该坐标上
label2.move(40, 50) # 移动到坐标(40, 50)
button.move(100, 30) # 移动到坐标(100, 30)
self.setGeometry(300, 300, 400, 300)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = Window()
w.setWindowTitle('绝对定位')
w.show()
sys.exit(app.exec_())
定义在水平布局中的控件会按水平方式排列, 如图:
水平布局主要使用两个方法:
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout
from PyQt5.QtWidgets import QLabel, QLineEdit, QPushButton
from PyQt5.QtCore import Qt
class Window(QWidget):
"""
窗体类
"""
def __init__(self):
super().__init__()
self.setup_ui()
def setup_ui(self):
"""
设置控件
:return:
"""
usr_label = QLabel('账号')
usr_input = QLineEdit('user')
pwd_label = QLabel('密码')
pwd_input = QLineEdit('pwd')
login_btn = QPushButton('登录')
layout = QHBoxLayout() # 定义水平布局
# 将空间加到布局中
layout.addWidget(usr_label, 10, Qt.AlignTop) # 垂直靠上对齐
layout.addWidget(usr_input, 10, Qt.AlignBottom) # 垂直靠下对齐
layout.addWidget(pwd_label, 10, Qt.AlignRight) # 右对齐
layout.addWidget(pwd_input, 10, Qt.AlignLeft) # 左对齐
layout.addWidget(login_btn, 10, Qt.AlignHCenter) # 居中对齐
self.setLayout(layout) # 设置当前widget的布局方式为水平布局
self.setWindowTitle('水平布局!')
if __name__ == '__main__':
app = QApplication(sys.argv)
w = Window()
w.show()
sys.exit(app.exec_())
垂直布局和水平布局使用方法一致,我们将刚刚的登录框代码做简单修改。
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout
layout = QVBoxLayout() # 定义垂直布局
边得到了垂直布局的效果图:
代码:
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout
from PyQt5.QtWidgets import QLabel, QLineEdit, QPushButton
from PyQt5.QtCore import Qt
class Window(QWidget):
"""
窗体类
"""
def __init__(self):
super().__init__()
self.setup_ui()
def setup_ui(self):
"""
设置控件
:return:
"""
usr_label = QLabel('账号')
usr_input = QLineEdit('user')
pwd_label = QLabel('密码')
pwd_input = QLineEdit('pwd')
login_btn = QPushButton('登录')
layout = QVBoxLayout() # 定义垂直布局
# 将空间加到布局中
layout.addWidget(usr_label, 10, Qt.AlignTop) # 垂直靠上对齐
layout.addWidget(usr_input, 10, Qt.AlignBottom) # 垂直靠下对齐
layout.addWidget(pwd_label, 10, Qt.AlignRight) # 右对齐
layout.addWidget(pwd_input, 10, Qt.AlignLeft) # 左对齐
layout.addWidget(login_btn, 10, Qt.AlignHCenter) # 居中对齐
self.setLayout(layout) # 设置当前widget的布局方式为layout
self.setWindowTitle('垂直布局!')
if __name__ == '__main__':
app = QApplication(sys.argv)
w = Window()
w.show()
sys.exit(app.exec_())
网格布局主要用到两个方法:
方法 | 说明 |
---|---|
addWidget(QWidget widget, int row, int col, int alignment = 0) | 给网格布局添加控件。widget:要添加的控件。row:位置所在行,col:位置所在列,alignment:对齐方式 |
addWidget(QWidget widget, int fromRow, int fromColumn, int rowSpan, int columnSpan, int alignment) | 当所添加的控件跨越多行或多列时,使用这个函数 fromRow:起始行 fromColumn:起始列 rowSpan:控件跨越的行 columnSpan:控件跨越的列 |
通过layout = QGridLayout()
定义网格布局, addWidet进行控件摆放。
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QGridLayout
from PyQt5.QtWidgets import QLabel, QLineEdit, QPushButton
from PyQt5.QtCore import Qt
class Window(QWidget):
"""
窗体类
"""
def __init__(self):
super().__init__()
self.setup_ui()
def setup_ui(self):
"""
设置控件
:return:
"""
label = QLabel('标签')
button = QPushButton('按钮')
layout = QGridLayout()
layout.addWidget(label, 1, 0, Qt.AlignCenter)
layout.addWidget(button, 1, 1, Qt.AlignCenter)
layout.addWidget(QPushButton('按钮2'), 3, 3, 5, 5, Qt.AlignCenter)
for i in range(1, 10):
layout.addWidget(QPushButton('测试{}'.format(str(i))), 2, i, Qt.AlignCenter)
self.setLayout(layout) # 设置布局
self.setWindowTitle('网格布局!')
if __name__ == '__main__':
app = QApplication(sys.argv)
w = Window()
w.show()
sys.exit(app.exec_())
表单布局是以标签, 输入框的形式进行布局。
网格布局主要使用addRow
方法:
代码:
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QFormLayout
from PyQt5.QtWidgets import QLabel, QLineEdit, QPushButton
from PyQt5.QtCore import Qt
class Window(QWidget):
"""
窗体类
"""
def __init__(self):
super().__init__()
self.setup_ui()
def setup_ui(self):
"""
设置控件
:return:
"""
layout = QFormLayout()
self.setWindowTitle('网格布局!')
usr_label = QLabel('账号')
usr_input = QLineEdit('user')
pwd_label = QLabel('密码')
pwd_input = QLineEdit('pwd')
layout.addRow(QPushButton('只添加了控件'))
layout.addRow(usr_label, usr_input)
layout.addRow(pwd_label, pwd_input)
layout.addRow('验证码', QLineEdit('验证码'))
layout.addRow('操作', QPushButton('提交'))
child_layout = QFormLayout()
child_layout.addRow('other', QLineEdit('token'))
child_layout.addRow('other2', QLineEdit('token'))
layout.addRow('附加信息', child_layout) # 添加子布局, 子布局又是一个表单布局
self.setLayout(layout) # 设置布局
if __name__ == '__main__':
app = QApplication(sys.argv)
w = Window()
w.show()
sys.exit(app.exec_())
在一个窗体中同时使用多种布局方式进行控件摆放。
混合布局也挺简单的, 控件可以添加到布局上, 控件可以设置布局。
父widget选取一种布局方式来布局, 然后创建一些子widget,在把子widget加到父widget中。
本例中选取QVBoxLayout
垂直布局来作为基本布局, 在子布局中设置其他布局方式。
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout, QVBoxLayout, QFormLayout
from PyQt5.QtWidgets import QLabel, QLineEdit, QPushButton
from PyQt5.QtCore import Qt
class Window(QWidget):
"""
窗体类
"""
def __init__(self):
super().__init__()
self.setup_ui()
def setup_ui(self):
"""
设置控件
:return:
"""
self.setWindowTitle('混合布局')
layout = QVBoxLayout() # 垂直布局作为基布局
self.setLayout(layout)
form_layout = QFormLayout() # 表单布局
h_layout = QHBoxLayout() # 垂直布局
form_widget = QWidget() # 创建一个子widget 并设置为表单布局
form_widget.setLayout(form_layout)
h_widget = QWidget() # 创建一个子widget 并设置水平布局
h_widget.setLayout(h_layout)
# 表单布局中添加两个编辑框
form_layout.addRow('账号:', QLineEdit())
form_layout.addRow('密码:', QLineEdit())
# 水平布局中添加两个按钮
h_layout.addWidget(QPushButton('登录'))
h_layout.addWidget(QPushButton('注册'))
# 将子widget添加到基础布局中
layout.addWidget(form_widget)
layout.addWidget(h_widget)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = Window()
w.show()
sys.exit(app.exec_())
运行效果:
QSplitter用于创建可以动态拖动控件。
方法 | 描述 |
---|---|
addWidget() | 将控件添加到QSplitter管理器的布局中 |
indexOf() | 返回控件在QSplitter管理器中的索引 |
insertWidget() | 根据索引将一个控件插入到QSplitter管理器中 |
setOrientation() | 设置布局方法 |
Qt.Horizontal | 水平方向 |
Qt.Vertical | 垂直方向 |
setSizes() | 设置控件的初始化大小 |
count() | 返回小控件在QSplitter管理器中的数量 |
实例代码:
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout, QVBoxLayout, QFormLayout, QSplitter
from PyQt5.QtWidgets import QPushButton, QTextBrowser
from PyQt5.QtCore import Qt
class Window(QWidget):
"""
窗体类
"""
def __init__(self):
super().__init__()
self.setup_ui()
def setup_ui(self):
"""
设置控件
:return:
"""
layout = QVBoxLayout()
self.setWindowTitle('QSplitter')
self.setGeometry(300, 300, 800, 600)
# 左上角对应一个表单布局并添加三个按钮
top_left_widget = QWidget()
top_left_layout = QFormLayout()
top_left_widget.setLayout(top_left_layout)
top_left_layout.addWidget(QPushButton('推荐'))
top_left_layout.addWidget(QPushButton('电台'))
top_left_layout.addWidget(QPushButton('歌单'))
# 底部创建一个水平布局,添加三个按钮
bottom_layout = QHBoxLayout()
bottom_widget = QWidget()
bottom_widget.setLayout(bottom_layout)
bottom_layout.addWidget(QPushButton('快退'))
bottom_layout.addWidget(QPushButton('暂停'))
bottom_layout.addWidget(QPushButton('快进'))
splitter = QSplitter(Qt.Horizontal)
text_browser = QTextBrowser()
# 第一个splitter分别把三个控件添加进去
splitter.addWidget(top_left_widget)
splitter.addWidget(text_browser)
splitter.addWidget(bottom_widget)
splitter.setSizes([100, 200])
# 创建一个splitter2将第一个splitter和底部的空控件添加进来
splitter2 = QSplitter(Qt.Vertical)
splitter2.addWidget(splitter)
splitter2.addWidget(bottom_widget)
layout.addWidget(splitter2) # splitter2添加到基础布局
self.setLayout(layout)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = Window()
w.show()
sys.exit(app.exec_())
栈式布局管理器管理的所有组件在垂直于屏幕的方向上,每次只显示一个组件界面。
实例:
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel
from PyQt5.QtWidgets import QAbstractItemView, QStackedLayout, QListWidget
class Window(QWidget):
"""
窗体类
"""
def __init__(self):
super().__init__()
self.setup_ui()
def setup_ui(self):
"""
设置控件
:return:
"""
layout = QVBoxLayout()
self.setLayout(layout)
stacked_layout = QStackedLayout()
list_widget = QListWidget()
list_widget.setDragEnabled(True)
list_widget.setDragDropMode(QAbstractItemView.InternalMove)
layout.addWidget(list_widget)
layout.addLayout(stacked_layout)
stacked_layout.addWidget(QLabel('页面1的内容展示出来啦'))
list_widget.addItem('页面1')
stacked_layout.addWidget(QLabel('页面2的内容展示出来啦'))
list_widget.addItem('页面2')
# 槽函数绑定
list_widget.currentRowChanged.connect(stacked_layout.setCurrentIndex)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = Window()
w.show()
sys.exit(app.exec_())
福利: