一、好软件的三个维度
好用
易用
能用
二、PyQt5中的布局管理
对PyQt5的界面进行布局管理主要有两种方法,即采用绝对位置和布局类。在PyQt5中有四种布局方式,即水平布局、垂直布局、网格布局、表单布局,以及两种布局方法,即addLayout()和addWidget(),其中addLayout()用于在布局中插入子布局,addWidget()用于在布局中插入控件。
四种布局方式对应四个布局类:
水平布局类(QHBoxLayout),可以把所添加的控件在水平方向上依次排列
垂直布局类(QVBoxLayout),可以把所添加的控件在垂直方向上依次排列
网格布局类(QGridLayout),可以把所添加的控件以网格的形式排列
表单布局类(QFormLayout),可以把所添加的控件以两列的形式排列
在窗口中进行单一的布局不难,但是若是进行比较复杂的布局,就涉及布局的嵌套了,推荐使用Qt Designer的可视化管理工具来进行界面布局。
三、PyQt5的绝对位置布局
绝对布局是通过在窗口程序中指定每一个控件的显示坐标和大小来实现的。
四、QBoxLayout(框布局)
采用QBoxLayout类可以在水平和垂直方向上排列控件,QHBoxLayout和QVBoxLayout类继承自QBoxLayout。
1、QHBoxLayout(水平布局)
采用该类,是按照从左到右的顺序来添加控件的。常用方法:
addLayout(self,QLayout,stretch=0) 在窗口的右边添加布局,使用stretch进行伸缩,伸缩量默认为0
addWidget(self,QWidget,stretch,Qt.Alignment allignment) 在布局中添加控件 其中stretch只适用于QBoxLayout,控件和窗口会随着伸缩量的变大而增大; alignment是指定对其的方式的。
addSpacing(self,int) 设置各控件的上下间距,通过该方法可以增加额外的空间
QHBoxLayout的对齐方式参数有:
Qt.AlignLeft 水平方向居左对齐
Qt.AlignRight 水平方向居右对齐
Qt.AlignCenter 水平方向居中对齐
Qt.AlignJustify 水平方向两端对齐
Qt.AlignTop 垂直方向靠上对齐
Qt.AlignBotton 垂直方向靠下对齐
Qt.AlignVCenter 垂直方向居中对齐
例子:
""" | |
【简介】 | |
水平布局管理例子 | |
""" | |
import sys | |
from PyQt5.QtWidgets import QApplication ,QWidget ,QHBoxLayout , QPushButton | |
class Winform(QWidget): | |
def __init__(self,parent=None): | |
super(Winform,self).__init__(parent) | |
self.setWindowTitle("水平布局管理例子") | |
# 水平布局按照从左到右的顺序进行添加按钮部件。 | |
hlayout = QHBoxLayout() | |
hlayout.addWidget( QPushButton(str(1))) | |
hlayout.addWidget( QPushButton(str(2))) | |
hlayout.addWidget( QPushButton(str(3))) | |
hlayout.addWidget( QPushButton(str(4))) | |
hlayout.addWidget( QPushButton(str(5))) | |
self.setLayout(hlayout) | |
if __name__ == "__main__": | |
app = QApplication(sys.argv) | |
form = Winform() | |
form.show() | |
sys.exit(app.exec_()) |
运行结果:
若需要将布局中的某些控件居上、居下显示,可以通过对齐方式参数Qt.Alignment来设置,例子如下:
""" | |
【简介】 | |
水平布局管理例子 | |
""" | |
import sys | |
from PyQt5.QtWidgets import QApplication ,QWidget ,QHBoxLayout , QPushButton | |
from PyQt5.QtCore import Qt | |
class Winform(QWidget): | |
def __init__(self,parent=None): | |
super(Winform,self).__init__(parent) | |
self.setWindowTitle("水平布局管理例子") | |
self.resize(800, 200) | |
# 水平布局按照从左到右的顺序进行添加按钮部件。 | |
hlayout = QHBoxLayout() | |
#水平居左 垂直居上 | |
hlayout.addWidget( QPushButton(str(1)) , 0 , Qt.AlignLeft | Qt.AlignTop) | |
hlayout.addWidget( QPushButton(str(2)) , 0 , Qt.AlignLeft | Qt.AlignTop) | |
hlayout.addWidget( QPushButton(str(3))) | |
#水平居左 垂直居下 | |
hlayout.addWidget( QPushButton(str(4)) , 0 , Qt.AlignLeft | Qt.AlignBottom ) | |
hlayout.addWidget( QPushButton(str(5)), 0 , Qt.AlignLeft | Qt.AlignBottom) | |
self.setLayout(hlayout) | |
if __name__ == "__main__": | |
app = QApplication(sys.argv) | |
form = Winform() | |
form.show() | |
sys.exit(app.exec_()) |
运行结果:
还可以使用setSpacing(int)设置各控件之间的间距,例子如下:
""" | |
【简介】 | |
水平布局管理例子 | |
""" | |
import sys | |
from PyQt5.QtWidgets import QApplication ,QWidget ,QHBoxLayout , QPushButton | |
from PyQt5.QtCore import Qt | |
class Winform(QWidget): | |
def __init__(self,parent=None): | |
super(Winform,self).__init__(parent) | |
self.setWindowTitle("水平布局管理例子") | |
# 水平布局按照从左到右的顺序进行添加按钮部件。 | |
hlayout = QHBoxLayout() | |
hlayout.addWidget( QPushButton(str(1)) ) | |
hlayout.addWidget( QPushButton(str(2)) ) | |
hlayout.addWidget( QPushButton(str(3))) | |
hlayout.addWidget( QPushButton(str(4)) ) | |
hlayout.addWidget( QPushButton(str(5))) | |
#设置控件间的间距 | |
hlayout.setSpacing( 0 ) | |
self.setLayout(hlayout) | |
if __name__ == "__main__": | |
app = QApplication(sys.argv) | |
form = Winform() | |
form.show() | |
sys.exit(app.exec_()) |
运行结果:
2、QVBoxLayout(垂直布局)
采用该类,按照从上到下的顺序添加控件。例子如下:
""" | |
【简介】 | |
垂直布局管理例子 | |
""" | |
import sys | |
from PyQt5.QtWidgets import QApplication ,QWidget ,QVBoxLayout , QPushButton | |
class Winform(QWidget): | |
def __init__(self,parent=None): | |
super(Winform,self).__init__(parent) | |
self.setWindowTitle("垂直布局管理例子") | |
self.resize(330, 150) | |
# 垂直布局按照从上到下的顺序进行添加按钮部件。 | |
vlayout = QVBoxLayout() | |
vlayout.addWidget( QPushButton(str(1))) | |
vlayout.addWidget( QPushButton(str(2))) | |
vlayout.addWidget( QPushButton(str(3))) | |
vlayout.addWidget( QPushButton(str(4))) | |
vlayout.addWidget( QPushButton(str(5))) | |
self.setLayout(vlayout) | |
if __name__ == "__main__": | |
app = QApplication(sys.argv) | |
form = Winform() | |
form.show() | |
sys.exit(app.exec_()) |
运行结果:
3、addStretch()
设置stretch伸缩量后,按比例分配剩余空间。例子如下:
''' | |
【简介】 | |
布局中用到的addStretch函数例子 | |
''' | |
from PyQt5.QtWidgets import QApplication ,QWidget, QVBoxLayout , QHBoxLayout ,QPushButton | |
import sys | |
class WindowDemo(QWidget): | |
def __init__(self ): | |
super().__init__() | |
btn1 = QPushButton(self) | |
btn2 = QPushButton(self) | |
btn3 = QPushButton(self) | |
btn1.setText('button 1') | |
btn2.setText('button 2') | |
btn3.setText('button 3') | |
hbox = QHBoxLayout() | |
# 设置伸缩量为1 | |
hbox.addStretch(1) | |
hbox.addWidget( btn1 ) | |
# 设置伸缩量为1 | |
hbox.addStretch(1) | |
hbox.addWidget( btn2 ) | |
# 设置伸缩量为1 | |
hbox.addStretch(1) | |
hbox.addWidget( btn3 ) | |
# 设置伸缩量为1 | |
hbox.addStretch(1 ) | |
self.setLayout(hbox) | |
self.setWindowTitle("addStretch 例子") | |
if __name__ == "__main__": | |
app = QApplication(sys.argv) | |
win = WindowDemo() | |
win.show() | |
sys.exit(app.exec_()) | |
运行结果:
五、QGridLayout(网格布局)
QGridLayout是将窗口分隔成行和列的网格来进行排列。通常可以使用函数addWidget()将被管理的控件(Widget)添加到窗口中,或者使用addLayout()函数将布局(Layout)添加到窗口中。也可以通过addWidget()函数对所添加的控件设置行数和列数的跨越,最后实现网格占据多个窗格。QGridLayout类中常用方法:
addWidget(QWidget widget,int row,int col,int alignment=0):给网格布局添加控件,设置指定的行和列。起始位置的默认值为(0,0) 其中 widget:所添加的控件 row:控件的行数,默认从0开始 column:控件的列数,默认从0开始 alignment:对齐方式
addWidget(QWidget widget,int fromRow,int fromColumn,int rowSpan,int columnSpan,Qt.Alignment alignment=0) 所添加的控件跨越很多行或列时,使用这个函数。其中 widget:所添加的控件 fromRow:控件的起始行数
fromColumn:控件的起始列数 rowSpan:控件跨越的行数 columnSpan:控件跨越的列数 alignment:对齐方式
setSpacing(int spacing):设置控件在水平和垂直方向的间隔
例子如下:
""" | |
【简介】 | |
网格布局管理例子 | |
""" | |
import sys | |
from PyQt5.QtWidgets import QApplication ,QWidget , QGridLayout, QPushButton | |
class Winform(QWidget): | |
def __init__(self,parent=None): | |
super(Winform,self).__init__(parent) | |
self.initUI() | |
def initUI(self): | |
#1创建QGridLayout的实例,并设置为窗口的布局 | |
grid = QGridLayout() | |
self.setLayout(grid) | |
#2创建按钮的标签列表 | |
names = ['Cls', 'Back', '', 'Close', | |
'7', '8', '9', '/', | |
'4', '5', '6', '*', | |
'1', '2', '3', '-', | |
'0', '.', '=', '+'] | |
#3在网格中创建一个位置列表 | |
positions = [(i,j) for i in range(5) for j in range(4)] | |
#4创建按钮,并通过addWidget()方法添加到布局中 | |
for position, name in zip(positions, names): | |
if name == '': | |
continue | |
button = QPushButton(name) | |
grid.addWidget(button, *position) | |
self.move(300, 150) | |
self.setWindowTitle('网格布局管理例子') | |
if __name__ == "__main__": | |
app = QApplication(sys.argv) | |
form = Winform() | |
form.show() | |
sys.exit(app.exec_()) |
运行结果:
2、跨越行和列的网格单元格
例子如下:
""" | |
【简介】 | |
网格布局管理例子 | |
""" | |
import sys | |
from PyQt5.QtWidgets import (QWidget, QLabel, QLineEdit, QTextEdit, QGridLayout, QApplication) | |
class Winform(QWidget): | |
def __init__(self,parent=None): | |
super(Winform,self).__init__(parent) | |
self.initUI() | |
def initUI(self): | |
titleLabel = QLabel('标题') | |
authorLabel = QLabel('提交人') | |
contentLabel = QLabel('申告内容') | |
titleEdit = QLineEdit() | |
authorEdit = QLineEdit() | |
contentEdit = QTextEdit() | |
grid = QGridLayout() | |
grid.setSpacing(10) | |
grid.addWidget(titleLabel, 1, 0) #把titleLabel放在QGridLayout布局的第一行第0列 | |
grid.addWidget(titleEdit, 1, 1) | |
grid.addWidget(authorLabel, 2, 0) | |
grid.addWidget(authorEdit, 2, 1) | |
grid.addWidget(contentLabel, 3, 0) | |
grid.addWidget(contentEdit, 3, 1, 5, 1) | |
self.setLayout(grid) | |
self.setGeometry(300, 300, 350, 300) | |
self.setWindowTitle('故障申告') | |
if __name__ == "__main__": | |
app = QApplication(sys.argv) | |
form = Winform() | |
form.show() | |
sys.exit(app.exec_()) |
六、QFormLayout(表单布局)
表单是提示用户进行交互的一种模式,其主要由两列组成,第一列用于显示信息,给用户提示,一般叫做label域;第二列需要用户进行选择或输入,一般叫做field域。label与field的关系就是label关联field。
举例:
""" | |
【简介】 | |
垂直布局管理例子 | |
""" | |
import sys | |
from PyQt5.QtWidgets import QApplication ,QWidget ,QFormLayout , QLineEdit, QLabel | |
class Winform(QWidget): | |
def __init__(self,parent=None): | |
super(Winform,self).__init__(parent) | |
self.setWindowTitle("窗体布局管理例子") | |
self.resize(400, 100) | |
fromlayout = QFormLayout() | |
labl1 = QLabel("标签1") | |
lineEdit1 = QLineEdit() | |
labl2 = QLabel("标签2") | |
lineEdit2 = QLineEdit() | |
labl3 = QLabel("标签3") | |
lineEdit3 = QLineEdit() | |
fromlayout.addRow(labl1, lineEdit1) | |
fromlayout.addRow(labl2, lineEdit2) | |
fromlayout.addRow(labl3, lineEdit3) | |
self.setLayout(fromlayout) | |
if __name__ == "__main__": | |
app = QApplication(sys.argv) | |
form = Winform() | |
form.show() | |
sys.exit(app.exec_()) |
运行结果:
七、嵌套布局
在布局中添加其他布局的代码:
""" | |
【简介】 | |
嵌套布局 | |
""" | |
import sys | |
from PyQt5.QtWidgets import QApplication ,QWidget , QHBoxLayout, QVBoxLayout, QGridLayout , QFormLayout, QPushButton | |
class MyWindow( QWidget): | |
def __init__(self): | |
super().__init__() | |
self.setWindowTitle('嵌套布局示例') | |
# 全局布局(1个):水平 | |
wlayout = QHBoxLayout() | |
# 局部布局(4个):水平、竖直、网格、表单 | |
hlayout = QHBoxLayout() | |
vlayout = QVBoxLayout() | |
glayout = QGridLayout() | |
formlayout = QFormLayout() | |
# 局部布局添加部件(例如:按钮) | |
hlayout.addWidget( QPushButton(str(1)) ) | |
hlayout.addWidget( QPushButton(str(2)) ) | |
vlayout.addWidget( QPushButton(str(3)) ) | |
vlayout.addWidget( QPushButton(str(4)) ) | |
glayout.addWidget( QPushButton(str(5)) , 0, 0 ) | |
glayout.addWidget( QPushButton(str(6)) , 0, 1 ) | |
glayout.addWidget( QPushButton(str(7)) , 1, 0) | |
glayout.addWidget( QPushButton(str(8)) , 1, 1) | |
formlayout.addWidget( QPushButton(str(9)) ) | |
formlayout.addWidget( QPushButton(str(10)) ) | |
formlayout.addWidget( QPushButton(str(11)) ) | |
formlayout.addWidget( QPushButton(str(12)) ) | |
# 准备四个部件 | |
hwg = QWidget() | |
vwg = QWidget() | |
gwg = QWidget() | |
fwg = QWidget() | |
# 四个部件设置局部布局 | |
hwg.setLayout(hlayout) | |
vwg.setLayout(vlayout) | |
gwg.setLayout(glayout) | |
fwg.setLayout(formlayout) | |
# 四个部件加至全局布局 | |
wlayout.addWidget(hwg) | |
wlayout.addWidget(vwg) | |
wlayout.addWidget(gwg) | |
wlayout.addWidget(fwg) | |
# 窗体本体设置全局布局 | |
self.setLayout(wlayout) | |
if __name__=="__main__": | |
app = QApplication(sys.argv) | |
win = MyWindow() | |
win.show() | |
sys.exit(app.exec_()) |
运行结果:
2、在控件中添加布局
用刚刚那个布局方式是两层嵌套,这样的布局有一个缺点:4种局部布局需要4个空白控件,假如有10种局部布局,就需要10个空白控件。所以我们选择在控件中添加布局,这样不管有多少种局部布局只需要一个空白控件,然后在这个空白控件中进行多种布局,就可以实现相同的效果。例子如下:
""" | |
【简介】 | |
嵌套布局 | |
""" | |
from PyQt5.QtWidgets import * | |
import sys | |
class MyWindow(QWidget): | |
def __init__(self): | |
super().__init__() | |
self.setWindowTitle('嵌套布局示例') | |
self.resize(700, 200) | |
# 全局部件(注意参数 self),用于"承载"全局布局 | |
wwg = QWidget(self) | |
# 全局布局(注意参数 wwg) | |
wl = QHBoxLayout(wwg) | |
hlayout = QHBoxLayout() | |
vlayout = QVBoxLayout() | |
glayout = QGridLayout() | |
formlayout = QFormLayout() | |
# 局部布局添加部件(例如:按钮) | |
hlayout.addWidget( QPushButton(str(1)) ) | |
hlayout.addWidget( QPushButton(str(2)) ) | |
vlayout.addWidget( QPushButton(str(3)) ) | |
vlayout.addWidget( QPushButton(str(4)) ) | |
glayout.addWidget( QPushButton(str(5)) , 0, 0 ) | |
glayout.addWidget( QPushButton(str(6)) , 0, 1 ) | |
glayout.addWidget( QPushButton(str(7)) , 1, 0) | |
glayout.addWidget( QPushButton(str(8)) , 1, 1) | |
formlayout.addWidget( QPushButton(str(9)) ) | |
formlayout.addWidget( QPushButton(str(10)) ) | |
formlayout.addWidget( QPushButton(str(11)) ) | |
formlayout.addWidget( QPushButton(str(12)) ) | |
# 这里向局部布局内添加部件,将他加到全局布局 | |
wl.addLayout(hlayout) | |
wl.addLayout(vlayout) | |
wl.addLayout(glayout) | |
wl.addLayout(formlayout) | |
if __name__=="__main__": | |
app = QApplication(sys.argv) | |
win = MyWindow() | |
win.show() | |
sys.exit(app.exec_()) |
运行结果:
八、QSplitter
它可以动态地拖动子控件之间的边界,算是一个动态的布局管理器。QSplitter允许用户通过拖动子控件的边界来控制子控件的大小,并提供了一个处理拖拽子控件的控制器。在QSplitter对象中各子控件默认是横向布局的,可以使用Qt.Vertical进行垂直布局。QSplitter类中的常用方法如下:
addWidget():将小控件添加到QSplitter管理器的布局中
indexOf():返回小控件在QSplitter管理器中的索引
insertWidget():根据指定的索引将一个控件插入到QSplitter管理器中
setOrientation():设置布局方向 Qt.Horizontal Qt.Vertical
setSizes():设置控件的初始化大小
count():返回小控件在QSplitter管理器中的数量
例子如下:
''' | |
【简介】 | |
PyQt5中 QSplitter 例子 | |
''' | |
import sys | |
from PyQt5.QtCore import * | |
from PyQt5.QtGui import * | |
from PyQt5.QtWidgets import * | |
class SplitterExample(QWidget): | |
def __init__(self): | |
super(SplitterExample, self).__init__() | |
self.initUI() | |
def initUI(self): | |
hbox = QHBoxLayout(self) | |
self.setWindowTitle('QSplitter 例子') | |
self.setGeometry(300, 300, 300, 200) | |
topleft = QFrame() | |
topleft.setFrameShape(QFrame.StyledPanel) | |
bottom = QFrame() | |
bottom.setFrameShape(QFrame.StyledPanel) | |
splitter1 = QSplitter(Qt.Horizontal) | |
textedit = QTextEdit() | |
splitter1.addWidget(topleft) | |
splitter1.addWidget(textedit) | |
splitter1.setSizes([100,200]) | |
splitter2 = QSplitter(Qt.Vertical) | |
splitter2.addWidget(splitter1) | |
splitter2.addWidget(bottom) | |
hbox.addWidget(splitter2) | |
self.setLayout(hbox) | |
if __name__ == '__main__': | |
app = QApplication(sys.argv) | |
demo = SplitterExample() | |
demo.show() | |
sys.exit(app.exec_()) |
运行结果: