注:文中譯者自己的話將用方括號【】標出。
佈局管理(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行。
最後的效果是這樣的: