注:文中译者自己的话将用方括号【】标出。
布局管理(Layout management)是GUI编程中一个重要的方面。布局是我们将各种部件摆放在窗口中的方式。在PyQt4中,布局可以通过两种方式实现:绝对定位(absolute positioning)和layout类。
绝对定位
在这种方式中,编程者指定各种部件的位置和大小。但是当你使用绝对定位时,需要知道有以下的限制:
- 如果我们改变窗口的大小,部件的大小和位置并不会改变。
- 你的应用在不同平台下可能长得不太一样。
- 改变应用中使用的字体可能会扰乱布局。
- 如果我们想改变现有的布局的话,我们必须完全重写布局,这很乏味而且浪费时间。
下面这个例子将使用绝对坐标来定位部件:
# -*- coding: utf-8 -*-
"""
This example shows three labels on a window
using absolute positioning.
"""
import sys
from PyQt4 import QtGui
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
lbl1 = QtGui.QLabel('ZetCode', self)
lbl1.move(15, 10)
lbl2 = QtGui.QLabel('tutorials', self)
lbl2.move(35, 40)
lbl3 = QtGui.QLabel('for programmers', self)
lbl3.move(55, 70)
self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('Absolute')
self.show()
def main():
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
我们使用move()方法来定位部件,在这个例子中我们定位了标签(label)。我们通过提供精确的x、y坐标来实现定位。注意,原点是左上角,x值从左到右增加,y值从上到下增加。【注意到这里标签移动到的位置是相对于我们建立的窗口的左上角的】
lbl1 = QtGui.QLabel('Zetcode', self)
lbl1.move(15, 10)
这里标签就被定位到了x=15、y=10的地方。
效果如下:
盒布局(Box layout)
用layout类实现的布局操作更加灵活和现实,这种方式是我们在窗口中定位部件的一种更推荐使用的方式。其中QtGui.QHBoxLayout和QtGui.QVBoxLayout是最基本的layout类,它们可以将部件水平或垂直排列。
假设我们想要在窗口的右下角放置两个按钮。为了做到这样的布局,我们将会用到一个水平盒子(horizontal box)和一个垂直盒子(vertical box)。为了创造必须的空间,我们加入了一个拉伸系数(stretch factor)【这里看不懂没关系,下面会讲】。
# -*- coding: utf-8 -*-
"""
In this example, we position two push
buttons in the bottom-right corner
of the window.
"""
import sys
from PyQt4 import QtGui
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
okButton = QtGui.QPushButton("OK")
cancelButton = QtGui.QPushButton("Cancel")
hbox = QtGui.QHBoxLayout()
hbox.addStretch(1)
hbox.addWidget(okButton)
hbox.addWidget(cancelButton)
vbox = QtGui.QVBoxLayout()
vbox.addStretch(1)
vbox.addLayout(hbox)
self.setLayout(vbox)
self.setGeometry(300, 300, 300, 150)
self.setWindowTitle('Buttons')
self.show()
def main():
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
这个例子中我们将两个按钮放在了窗口的右下角。即使我们改变窗口的大小,它们也会在那个地方。
okButton = QtGui.QPushButton("OK")
cancelButton = QtGui.QPushButton("Cancel")
这里我们创建了两个按钮(push button)。
hbox = QtGui.QHBoxLayout()
hbox.addStretch(1)
hbox.addWidget(okButton)
hbox.addWidget(cancelButton)
这里我们创建了一个水平盒子,并给两个按钮增加了一个伸展系数。伸展系数可以给两个按钮前方创造一个可以伸展的空间,这样两个按钮就被挤到了水平盒子的右边,也就呈现在了窗口的右边。
vbox = QtGui.QVBoxLayout()
vbox.addStretch(1)
vbox.addLayout(hbox)
我们这里将之前创建的水平盒子放进了一个垂直盒子里面【为什么要这样做呢?因为要把水平盒子挤到下面去!】。这里的伸展系数将水平盒子挤到了垂直盒子的下方,也就呈现在了屏幕的下方。【经过两次挤压,按钮现在就在窗口的右下方啦!大家可以自己试一下不加伸展因子是什么样子的,还要改变窗口大小试一试!】
self.setLayout(vbox)
最终,我们将垂直盒子设置成窗口的布局形式。
最后效果是这样子的:
网格布局:QtGui.QGridLayout类
最普遍使用的布局类是网格布局(grid layout),这种布局将空间分成行和列。为了创造网格布局,我们使用QtGui.QGridLayout类。
下面这个例子中,我们将使用网格布局创建一个计算器的框架。
# -*- coding: utf-8 -*-
import sys
from PyQt4 import QtGui
"""
In this example, we create a skeleton
of a calculator using a QtGui.QGridLayout.
"""
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
grid = QtGui.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 = QtGui.QPushButton(name)
grid.addWidget(button, *position)
self.move(300, 150)
self.setWindowTitle('Calculator')
self.show()
def main():
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
在这个例子中,我们创建了许多位于网格中的按钮。
grid = QtGui.QGridLayout()
self.setLayout(grid)
这里我们创建了QtGui.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 = QtGui.QPushButton(name)
grid.addWidget(button, *position)
这里我们创建了按钮并且用addWidget()方法将它们加入了表格中。
最终效果是这样子的:
一个书评窗口的例子
部件可以在表格中占据多行多列,下面这个例子中我们将会说明这一点。
# -*- coding: utf-8 -*-
"""
In this example, we create a bit
more complicated window layout using
the QtGui.QGridLayout manager.
"""
import sys
from PyQt4 import QtGui
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
title = QtGui.QLabel('Title')
author = QtGui.QLabel('Author')
review = QtGui.QLabel('Review')
titleEdit = QtGui.QLineEdit()
authorEdit = QtGui.QLineEdit()
reviewEdit = QtGui.QTextEdit()
grid = QtGui.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()
def main():
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
我们创建了一个拥有三个标签、两个行编辑框(line edit)和一个文本编辑框(text edit)的一个窗口,并且通过QtGui.QGridLayout来实现布局。
grid = QtGui.QGridLayout()
grid.setSpacing(10)
这里我们创建了一个网格布局,设定了两个部件之间的间距。
grid.addWidget(reviewEdit, 3, 1, 5, 1)
如果我们要把一个部件加入网格,我们可以提供行跨距和列跨局作为后两个参数,这里我们使reviewEdit部件占据了5行。
最后的效果是这样的: