目錄
一、絕對布局
二、盒布局
三、格柵布局
四、格柵布局跨行跨列顯示
布局管理即設置窗體上各個控件的位置,對於新手來說,這是學習的難點。
布局管理根據絕對坐標是否變動分為絕對布局和相對布局兩大類。采用相對布局的窗口在變大或縮小時,各控件的位置關系會保持固定比例做相應變動。而采用絕對布局的窗口變動時,空間位置不會變動。
而相對布局根據方式不同,又可以分為水平布局(QHBoxLayout)、垂直布局管理(QVBoxLayout)、柵格布局管理(QGridLayout)、表單布局管理(QFormLayout)。
一、絕對布局
絕對布局中以像素為單位區分元素的位置,衡量元素的大小,以設定坐標的方式精准的定位每個控件的位置,但是這種布局方式也有缺點:
- 窗體控件無法根據窗口的位置和大小而變化;
- 改變字體大小時可能會破壞布局;
- 當分辨率有較大改變時,原有布局會破壞;
- 在設計階段,如果需要添加或刪除控件,如果要調整控件位置,就需要全部調整。
下面是絕對布局的示例:

1 import sys,os 2 from PyQt5.QtWidgets import QWidget,QLabel,QApplication 3 from PyQt5.QtGui import QIcon 4 5 path = os.path.dirname(os.path.dirname( os.path.dirname(__file__))) 6 7 class MyWindow(QWidget): 8 9 def __init__(self): 10 super(MyWindow, self).__init__() 11 self.initUI() 12 13 def initUI(self): 14 self.setGeometry(300,300,500,400) 15 self.setWindowIcon(QIcon(r'%s\4.圖標素材\chuan.ico' % path)) 16 self.setWindowTitle('絕對布局示例') 17 18 lbl1 = QLabel('這是第一個標簽',self) 19 lbl1.setGeometry(80,80,30,30) 20 21 lbl2 = QLabel('這是第二個標簽',self) 22 lbl2.move(150,150) 23 24 25 if __name__ == '__main__': 26 app = QApplication(sys.argv) 27 win = MyWindow() 28 win.show() 29 sys.exit(app.exec_())
效果圖如下:
根據效果圖可以發現:
- 在改變窗口長和寬的時候,兩個標簽的位置不會變動;
- 兩個標簽用不同的方法創建。第一個標簽不僅設定了位置,還固定了大小,所以內容不能完全展示。
二、盒布局(Box Layout)
采用盒布局的窗口在改變窗體大小時,各控件會按相應比例自動調整。代碼如下:

1 import sys,os 2 from PyQt5.QtWidgets import QApplication,QHBoxLayout,QVBoxLayout,QWidget,QPushButton 3 from PyQt5.QtGui import QIcon 4 5 path = os.path.dirname(os.path.dirname( os.path.dirname(__file__))) 6 7 class MyWindow(QWidget): 8 9 def __init__(self): 10 super(MyWindow, self).__init__() 11 self.initUI() 12 13 def initUI(self): 14 self.setGeometry(300,300,500,400) 15 self.setWindowIcon(QIcon(r'%s/4.圖標素材/chuan.ico' % path)) 16 self.setWindowTitle('盒布局示例') 17 18 okbutton = QPushButton('確認') #設置“確認”按鈕 19 cancelbutton = QPushButton('取消') #設置“取消”按鈕 20 21 hbox = QHBoxLayout() #布局實例化對象 22 hbox.addStretch(1) #設置分配比例 23 hbox.addWidget(okbutton) #添加“確認”按鈕到窗體 24 hbox.addWidget(cancelbutton) 25 26 vbox = QVBoxLayout() 27 vbox.addStretch(1) 28 vbox.addLayout(hbox) 29 30 self.setLayout(vbox) #設置窗體布局 31 32 if __name__ == '__main__': 33 app = QApplication(sys.argv) 34 win = MyWindow() 35 win.show() 36 sys.exit(app.exec_())
盒布局中使用stretch函數在布局中增加了一個伸縮量,里面的參數表示QSpacerItem的個數,默認值為零(也可以理解為除去控件外,空白部分所占的比例),會將你放在layout中的空間壓縮成默認的大小。而且在窗體縮小時,這個伸縮量也可以變小,直至為零。
“確認”和“取消”兩個按鈕出現在窗體右下角。示例中實現的方法是首先創建一個垂直布局,按鈕上部伸縮量占比為1,接着把這整個布局放在水平布局里,伸縮量占比為1.於是實現了給出的顯示效果。
下面對addstretch方法做個補充講解:

1 import sys,os 2 from PyQt5.QtWidgets import QApplication,QHBoxLayout,QVBoxLayout,QWidget,QPushButton 3 from PyQt5.QtGui import QIcon 4 5 path = os.path.dirname(os.path.dirname( os.path.dirname(__file__))) 6 7 class MyWindow(QWidget): 8 9 def __init__(self): 10 super(MyWindow, self).__init__() 11 self.initUI() 12 13 def initUI(self): 14 self.setGeometry(300,300,500,300) 15 self.setWindowIcon(QIcon(r'%s/4.圖標素材/chuan.ico' % path)) 16 self.setWindowTitle('addstrtch示例') 17 18 fir_button = QPushButton('按鈕一') #設置按鈕一 19 sec_button = QPushButton('按鈕二') #設置按鈕二 20 thir_button = QPushButton('按鈕三') #設置按鈕三 21 22 hbox = QHBoxLayout() 23 hbox.addStretch(1) 24 hbox.addWidget(fir_button) 25 hbox.addStretch(2) 26 hbox.addWidget(sec_button) 27 hbox.addStretch(3) 28 hbox.addWidget(thir_button) 29 30 # vbox = QVBoxLayout() 31 # vbox.addStretch(1) 32 # vbox.addLayout(hbox) 33 34 self.setLayout(hbox) #設置窗體布局 35 36 if __name__ == '__main__': 37 app = QApplication(sys.argv) 38 win = MyWindow() 39 win.show() 40 sys.exit(app.exec_())
使用addstretch方法分別給定了參數1、2、3,即在水平布局上,除去按鈕部分外的空白區域,按照1:2:3的比例分配空間。下過如下圖所示。
三、格柵布局
格柵布局是最常用的布局方式,下面我們通過模擬計算器界面的方式來學習格柵布局。

1 import sys,os 2 from PyQt5.QtWidgets import QApplication,QPushButton,QWidget,QGridLayout 3 from PyQt5.QtGui import QIcon 4 5 path = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) 6 7 class MyWindow(QWidget): 8 9 def __init__(self): 10 super(MyWindow, self).__init__() 11 self.initUI() 12 13 def initUI(self): 14 self.setWindowTitle('格柵布局示例') 15 self.setWindowIcon(QIcon(r'%s\4.圖標素材\chuan.ico' % path)) 16 self.setGeometry(600,300,500,400) 17 18 names = ['On','Off','AC','/', 19 '7','8','9','*', 20 '4','5','6','-', 21 '1','2','3','+', 22 '0','00','.','='] 23 24 positions = [(i,j) for i in range(5) for j in range(4)] 25 26 grid = QGridLayout() 27 self.setLayout(grid) 28 for position,name in zip(positions,names): 29 button = QPushButton(name) 30 grid.addWidget(button,*position) 31 32 33 34 if __name__ == '__main__': 35 app = QApplication(sys.argv) 36 win = MyWindow() 37 win.show() 38 sys.exit(app.exec_())
顯示效果如下:
下面來分析代碼
names = ['On','Off','AC','/', '7','8','9','*', '4','5','6','-', '1','2','3','+', '0','00','.','=']
names列表是各按鍵的名稱。
positions = [(i,j) for i in range(5) for j in range(4)]
這個列表生成式生成的列表類似一個矩陣,各元素的數值恰好是按鈕在格柵布局中位置
for position,name in zip(positions,names): button = QPushButton(name) grid.addWidget(button,*position)
這里使用的zip函數。python3中為了減少內存,zip函數返回的是一個對象。如果要展示列表,可通過 list() 轉換。
names = ['On','Off','AC','/', '7','8','9','*', '4','5','6','-', '1','2','3','+', '0','00','.','='] positions = [(i,j) for i in range(5) for j in range(4)] a = zip(names,positions) print(a) print(list(a)) 顯示效果如下: <zip object at 0x0000020EFF489508> [('On', (0, 0)), ('Off', (0, 1)), ('AC', (0, 2)), ('/', (0, 3)), ('7', (1, 0)), ('8', (1, 1)), ('9', (1, 2)), ('*', (1, 3)), ('4', (2, 0)), ('5', (2, 1)), ('6', (2, 2)), ('-', (2, 3)), ('1', (3, 0)), ('2', (3, 1)), ('3', (3, 2)), ('+', (3, 3)), ('0', (4, 0)), ('00', (4, 1)), ('.', (4, 2)), ('=', (4, 3))]
四、格柵布局跨行跨列顯示
雖然格柵布局最常用,但是實際窗口的控件往往是大小不一的,單個控件跨行跨列很常見,下面來介紹下跨行跨列的情況。

1 import sys,os 2 from PyQt5.QtWidgets import QWidget,QPushButton,QLabel,QLineEdit,QApplication,QGridLayout 3 from PyQt5.QtGui import QIcon, QPixmap 4 5 path = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) 6 7 class MyWindow(QWidget): 8 9 def __init__(self): 10 super().__init__() 11 self.initUI() 12 13 def initUI(self): 14 self.setWindowTitle('跨行跨列布局示例') 15 self.setWindowIcon(QIcon(r'%s\4.圖標素材\chuan.ico' % path)) 16 self.setGeometry(600,300,450,300) 17 18 lbl_image = QLabel() 19 png = QPixmap(r'%s\4.圖標素材\cartoon1.ico' % path) 20 lbl_image.setPixmap(png) 21 lbl_image.setScaledContents(True) #圖片自適應標簽大小 22 23 lbl_user = QLabel('賬號:') #設置標簽 24 lbl_pwd = QLabel('密碼:') 25 okbutton = QPushButton('確認') #設置按鈕 26 cancelbutton = QPushButton('取消') 27 lineedit_user = QLineEdit() #設置單行文本框 28 lineedit_pwd = QLineEdit() 29 30 grid = QGridLayout() 31 self.setLayout(grid) 32 grid.setSpacing(10) #間距為10 33 34 grid.addWidget(lbl_image,1,1,3,1) 35 grid.addWidget(lineedit_user,1,2,1,2) 36 grid.addWidget(lineedit_pwd,2,2,1,2) 37 grid.addWidget(lbl_user,1,4) 38 grid.addWidget(lbl_pwd,2,4) 39 grid.addWidget(okbutton,3,2,1,1) 40 grid.addWidget(cancelbutton,3,3,1,1) 41 42 if __name__ == '__main__': 43 app = QApplication(sys.argv) 44 win = MyWindow() 45 win.show() 46 sys.exit(app.exec_())
顯示效果如下:
下面來分析代碼:
grid.addWidget(lbl_image,1,1,3,1)
addwidget方法第一個參數是添加的控件對象,后面的(1,1)表示位置坐標為(1,1),即控件左上角坐標為(1,1)。這里的1並不是以像素為單位,而是以行和列為單位,(1,1)即第一行第一列。最后的(3,1)意思是這個控件占三行1列。下面用表格和圖片來表示布局方式
圖片 (3行1列) |
單行文本框1 | “賬號”標簽 | |
單行文本框2 | “密碼”標簽 | ||
“確認”按鈕 | “取消”按鈕 |
grid.setSpacing(10)
即各控件之間的上下間距為10(以像素為單位)。同理還有grid.setMargin(int)為設置控件之間的左右間距。