布局管理是GUI编程中的一个重要的方面。
布局管理就是我们怎么在应用窗口上放组件的方法。
布局管理可以用两种基本的方式,我们可以用绝对定位(absolute positioning)或者布局类(layout class)。
absolute positioning
程序员以像素为单位指定每一个组件的位置和大小。当你用绝对定位,我们得理解一下几点:
1.当我们resize一个窗口时,组件的位置和大小不会改变。
2.应用程序在不同的平台上可能,看起来不一样
3.在我们的应用中改变字体,可能破坏布局。
4.如果我们决定改变布局,我们必须完全重做布局,浪费时间。
下面的例子采用绝对布局方式:
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
ZetCode PyQt5 tutorial
This example shows three labels on a window
using absolute positioning.
author: Jan Bodnar
website: zetcode.com
last edited: January 2015
"""
import sys
from PyQt5.QtWidgets import QWidget, QLabel, QApplication
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
lbl1 = QLabel('Zetcode', self)
lbl1.move(15, 10)
lbl2 = QLabel('tutorials', self)
lbl2.move(35, 40)
lbl3 = QLabel('for programmers', self)
lbl3.move(55, 70)
self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('Absolute')
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
我们使用move()方法来为组件设定位置。在上述例子中,组件为Label。
我们通过提供x, y坐标来定位他们。坐标系的原点为左上角。x值由左到右增加,y值由上到下增加。
lbl1 = QLabel('Zetcode', self)
lbl1.move(15, 10)
上面代码,表示将lbl1定位在(15, 10)的位置。
Box layout
用布局类(layout class)的布局管理很灵活,实用。他是布局管理的推荐方式。
QHBoxLayout和QVBoxLayout是用于水平或垂直线性排列组件的基布局类。
想象我们想在右下角放置两个按钮。为了创建这样一个布局,我们将会一个水平布局和一个垂直布局来实现。
为了使用一些必要的空间,我们将添加一个stretch factor(伸展因子)。
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
ZetCode PyQt5 tutorial
In this example, we position two push
buttons in the bottom-right corner
of the window.
author: Jan Bodnar
website: zetcode.com
last edited: January 2015
"""
import sys
from PyQt5.QtWidgets import (QWidget, QPushButton,
QHBoxLayout, QVBoxLayout, QApplication)
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
okButton = QPushButton("OK")
cancelButton = QPushButton("Cancel")
hbox = QHBoxLayout()
hbox.addStretch(1)
hbox.addWidget(okButton)
hbox.addWidget(cancelButton)
vbox = QVBoxLayout()
vbox.addStretch(1)
vbox.addLayout(hbox)
self.setLayout(vbox)
self.setGeometry(300, 300, 300, 150)
self.setWindowTitle('Buttons')
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
上述示例,放置了两个按钮在右下角。当resize窗口的时候,他们还是在右下角。我们用了HBoxLayout和VBoxLayout。
okButton = QPushButton("OK")
cancelButton = QPushButton("Cancel")
上述代码创建了两个按钮。
hbox = QHBoxLayout()
hbox.addStretch(1)
hbox.addWidget(okButton)
hbox.addWidget(cancelButton)
我们创建了一个水平布局,然后添加了一个伸展因子,和那两个按钮。
伸展因子在两个按钮之前增加了一个可伸缩空间。这会将按钮推到窗口的右边。
vbox = QVBoxLayout()
vbox.addStretch(1)
vbox.addLayout(hbox)
为了创建必要的布局,我们把水平布局放到垂直布局中,在垂直布局中的拉伸因子将会把水平box放置在window的底部。
self.setLayout(vbox)
最后,我们设置一下窗口的主布局。
QGridLayout
最常用的布局类是网格布局。这个布局把空间分为行和列。要创建一个网格布局,我们使用QGridLayout类。
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
ZetCode PyQt5 tutorial
In this example, we create a skeleton
of a calculator using a QGridLayout.
author: Jan Bodnar
website: zetcode.com
last edited: January 2015
"""
import sys
from PyQt5.QtWidgets import (QWidget, QGridLayout,
QPushButton, QApplication)
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
grid = QGridLayout()
self.setLayout(grid)
names = ['Cls', 'Bck', '', 'Close',
'7', '8', '9', '/',
'4', '5', '6', '*',
'1', '2', '3', '-',
'0', '.', '=', '+']
positions = [(i,j) for i in range(5) for j in range(4)]
for position, name in zip(positions, names):
if name == '':
continue
button = QPushButton(name)
grid.addWidget(button, *position)
self.move(300, 150)
self.setWindowTitle('Calculator')
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
在我们的例子中,我们创建了一个全是按钮的网格布局。
grid = QGridLayout()
self.setLayout(grid)
实例化QGridLayout类,将主布局设置为网格布局。
names = ['Cls', 'Bck', '', 'Close',
'7', '8', '9', '/',
'4', '5', '6', '*',
'1', '2', '3', '-',
'0', '.', '=', '+']
这些标签在后面用于按钮。
positions = [(i,j) for i in range(5) for j in range(4)]
创建了一个网格布局中定位的列表。
for position, name in zip(positions, names):
if name == '':
continue
button = QPushButton(name)
grid.addWidget(button, * position)
创建出按钮组件,并使用addWidget()方法向布局中添加按钮。
Review example
在网格布局中中,组件可以跨多列或多行。在下面这个例子中,我们进行阐释。
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
ZetCode PyQt5 tutorial
In this example, we create a bit
more complicated window layout using
the QGridLayout manager.
author: Jan Bodnar
website: zetcode.com
last edited: January 2015
"""
import sys
from PyQt5.QtWidgets import (QWidget, QLabel, QLineEdit,
QTextEdit, QGridLayout, QApplication)
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
title = QLabel('Title')
author = QLabel('Author')
review = QLabel('Review')
titleEdit = QLineEdit()
authorEdit = QLineEdit()
reviewEdit = QTextEdit()
grid = QGridLayout()
grid.setSpacing(10)
grid.addWidget(title, 1, 0)
grid.addWidget(titleEdit, 1, 1)
grid.addWidget(author, 2, 0)
grid.addWidget(authorEdit, 2, 1)
grid.addWidget(review, 3, 0)
grid.addWidget(reviewEdit, 3, 1, 5, 1)
self.setLayout(grid)
self.setGeometry(300, 300, 350, 300)
self.setWindowTitle('Review')
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
我们创建了包含三个标签,两个单行编辑框和一个文本编辑框组件的窗口。使用QGridLayout布局。
grid = QGridLayout()
grid.setSpacing(10)
我们创建了一个网格布局并且设置了组件之间的间距。
grid.addWidget(reviewEdit, 3, 1, 5, 1)
如果我们向网格布局中增加一个组件,我们可以提供组件的跨行和跨列参数。在上述代码中,我们让reviewEdit组件跨了5行。
本节教程专门讲布局管理。