### 8.2.4 布局
布局指的是界面元素在界面中的位置安排。Tkinter 中提供了布局管理器,其任務是根據 程序員的要求以及其他一些約束來安排構件的位置。使用布局管理器的優點是程序員不需要 了解底層顯示系統的細節,可以在較高層次上考慮界面布局問題。
如前所述,多數構件在創建之后還需進行布局才會顯示在屏幕上,即要經過兩個步驟:
```
w = Constructor(parent,...) w.GeometryManager(...)
```
其中 Constructor 是構件類(如 Label,Button 等),parent 是父構件,GeometryManager 是布局管理器方法。Tkinter 提供了三種布局管理器:Pack、Grid 和 Place。
Pack 布局管理器
Pack 布局管理器以緊湊的方式將構件在窗口中“打包”,調用構件的 pack 方法即可以這 種方式布局。具體的打包方式可以用一個比方來說明:設想窗口是由彈性材料制成的,當要 放入一個構件時,就先把窗口空間撐大到足夠容納該構件,然后將構件緊貼內部的某條邊(缺 省是頂邊)放入,然后重復此過程不斷放入構件。可見,在缺省情形下,放入同一個窗口的 所有構件是沿垂直方向自頂向下一個緊貼一個進行布置的,但可以通過 pack 方法的 side 選項 設置成沿水平方向打包。
例如,執行下列語句后得到的布局效果如圖 8.16(a)所示。
```
>>> Label(root,text="Input a number:").pack()
>>> Entry(root).pack()
>>> Button(root,text="OK").pack()
```
而執行下列語句后的布局效果如圖 8.16(b)所示,這里用到了 side 屬性:side="left"使得構件 貼著左邊放置。
```
>>> Label(root,text="Input a number:").pack(side="left")
>>> Entry(root).pack(side="left")
>>> Button(root,text="OK").pack(side="left")
```


圖 8.16 Pack 布局管理器
如果對窗口中的所有基本構件以打包方式布局,將使大大小小的構件形成一摞,顯然不 美觀。實際應用中常見的做法是對框架進行打包布局,一個一個框架像集裝箱一樣并排或堆 疊,然后再將基本構件作為各框架的子構件進行布局,這樣能使界面整齊美觀。
雖然 Pack 管理器還提供其他布局選項,但在此不多介紹了,因為我們有更靈活、更好用 的布局管理器:Grid 和 Place。
pack 方法的“逆”方法是 pack_forget 方法,意為將用 pack 布局的構件從界面中拿掉, 從而構件變成不可見。注意,這時構件作為對象仍然存在,只是未顯示在界面中而已,我們 隨時可以再次調用任何布局管理器方法來使構件可見。
rid 布局管理器*
Grid 布局管理器將窗口或框架視為一個由行和列構成的二維表格,并將構件放入行列交 叉處的單元格中。為了使用這種布局,只需先創建構件,再用 grid 方法的選項 row 和 column 指定行、列編號即可。不需要預先定義表格的尺寸,Grid 布局管理器會根據構件的大小自動 調整:每一列的寬度由該列中最寬的構件決定,每一行的高度由該行最高的構件決定。行、列都是從 0 開始編號,row 的缺省值為當前下一空行,column 的缺省值總為 0。可 以在布置構件時指定不連續的行號或列號,這相對于預留了一些行列,但這些預留的行列是 不可見的,因為行列上沒有構件存在,也就沒有寬度和高度。
Grid 布局管理器的使用非常簡單,可以說是進行圖形界面布局的首選工具。先看一個簡 單例子:
```
>>> Label(root,text="ID Number:").grid()
>>> Label(root,text="Name:").grid()
>>> Entry(root).grid(row=0,column=1)
>>> Entry(root).grid(row=1,column=1)
```
其中第一條語句使用缺省行號 0 和缺省列號 0 來安排標簽“ID Number:”,第二條語句使用缺省行號 1 和缺省列號 0 來安排標簽“Name”,后面兩條語句在指定的位置安排錄入框。布局效果如圖 8.17 所示:

圖 8.17 Grid 布局
從圖 8.17 可以看出,標簽構件在單元格里是居中放置的,如果覺得這種對齊方式不好看, 可以用 grid 方法的 sticky 選項來改變對齊方式。
Tkinter 中常利用“方位”概念來指定對齊方式,具體方位值包括 N、NE、E、SE、S、 SW、W、NW 和 CENTER(見圖 8.18)。將 sticky 選項設置為某個方位,就表示將構件沿單元格的某條邊或某個角對齊。

圖 8.18 方位
如果構件比單元格小,未能填滿單元格,則可以指定如何處理多余空間,比如在水平方 向或垂直方向拉伸構件以填滿單元格。可以利用方位值的組合來使構件延伸,例如若將 sticky 設置為 E+W,則構件將在水平方向延伸,占滿單元格的寬度;若設置為 E+W+N+S(或 NW+SE),則構件將在水平和垂直兩個方向延伸,占滿整個單元格。
如果想讓一個構件占據多個單元格,可以使用 grid 方法的 rowspan 和 columnspan 選項來 指定在行和列方向上的跨度。例如,下面的語句序列能產生如圖 8.19 所示的復雜布局:
```
>>> Label(root,text="ID Number:").grid(sticky=E)
>>> Label(root,text="Name:").grid(sticky=E)
>>> Entry(root).grid(row=0,column=1)
>>> Entry(root).grid(row=1,column=1)
>>> Checkbutton(root,text="Registered User").grid(
... columnspan=2,sticky=W)
>>> Label(root,text="X").grid(row=0,column=2,
... columnspan=2,rowspan=2,sticky=W+E+N+S)
>>> Button(root,text="Zoom In").grid(row=2,column=2)
>>> Button(root,text="Zoom Out").grid(row=2,column=3)
```

圖 8.19 利用 Grid 進行復雜布局
下面再看一個利用框架來實現復雜界面結構的例子:
```
>>> f1 = Frame(root,width=100,height=100,bd=4,relief="groove")
>>> f1.grid(row=1,column=1,rowspan=2,sticky=N+S+W+E)
>>> Checkbutton(f1,text="PC").grid(row=1,sticky=W)
>>> Checkbutton(f1,text="Laptop").grid(row=2,sticky=W)
>>> f2 = Frame(root,width=100,height=50,bd=4,relief="groove")
>>> f2.grid(row=1,column=2,columnspan=2,sticky=N)
>>> b1 = Button(root,text="OK",width=6)
>>> b1.grid(row=2,column=2,sticky=E+W,padx=2)
>>> b2 = Button(root,text="Cancel",width=6)
>>> b2.grid(row=2,column=3,sticky=E+W,padx=2)
```
結果如圖 8.20 所示。可以看出,這個例子大致實現了圖 8.2 所要求的界面。

圖 8.20 利用框架的布局
grid 方法的“逆”方法是 grid_forget 方法,意為將用 grid 布局的構件從界面中拿掉,從 而構件變成不可見。注意,這時構件作為對象仍然存在,只是未顯示在界面中而已,我們隨 時可以再次調用任何布局管理器方法來使構件可見。
Place 布局管理器*
Place 布局管理器直接指定構件在父構件中的位置坐標。為使用這種布局,只需先創建構 件,再調用構件的 place 方法,該方法的選項 x 和 y 用于設定坐標。父構件(窗口或框架) 的坐標系統以左上角為(0,0),x 方向向右,y 方向向下。
由于(x,y)坐標確定的是一個點,而子構件可看作是一個矩形,這個矩形怎么放置在一個 點上呢?Place 通過“錨點”來處理這個問題:利用方位值(見圖 8.18)指定子構件的錨點, 再利用 place 方法的 anchor 選項來將子構件的錨點定位于父窗口的指定坐標處。利用這種精 確的定位,可以實現一個或多個構件在窗口中的各種對齊方式。anchor 的缺省值為 NW,即 構件的左上角。例如下面兩條語句分別將兩個標簽置于根窗口的(0,0)和(199,199)處,定位錨 點分別是(默認的)NW 和 SE:
```
>>> Label(root,text="Hello").place(x=0,y=0)
>>> Label(root,text="World").place(x=199,y=199,anchor=SE)
```
下列語句序列圍繞根窗口的點(100,100)按不同錨點布置了若干個按鈕:
```
>>> Button(root,text="CCCCCCCCCCCCCCCCCC").place(x=100,y=100,
... anchor=CENTER)
>>> Button(root,text=" NW ").place(x=100,y=100,anchor=NW)
>>> Button(root,text="E").place(x=100,y=100,anchor=E)
>>> Button(root,text="W").place(x=100,y=100,anchor=W)
>>> Button(root,text=" SE ").place(x=100,y=100,anchor=SE)
```
以上語句的執行結果如圖 8.21 所示。

圖 8.21 利用 Place 布局
Place 布局管理器既可以像上例這樣用絕對坐標指定位置,也可以用相對坐標指定位置。 相對坐標通過選項 relx 和 rely 來設置,取值范圍為 0~1,表示構件在父構件中的相對比例 位置,如 relx=0.5 即表示父構件 x 方向上的二分之一處。相對坐標的好處是當窗口改變大小 時,構件位置將隨之調整,不像絕對坐標固定不變。例如下面這條語句將標簽布置于水平方 向四分之一、垂直方向二分之一處,定位錨點是 SW:
```
Label(root,text="Hello").place(relx=0.25,rely=0.5,anchor=SW)
```
除了指定構件位置,Place 布局管理器還可以指定構件大小。既可以通過選項 width 和 heightPlace 來定義構件的絕對尺寸,也可以通過選項 relwidth 和 relheight 來定義構件的相對 尺寸(即相對于父構件兩個方向上的比例值)。
Place 是最靈活的布局管理器,但用起來比較麻煩,通常不適合對普通窗口和對話框進行 布局,其主要用途是實現復合構件的定制布局。
place 方法的“逆”方法是 place_forget 方法,意為將用 place 布局的構件從界面中拿掉, 從而構件變成不可見。注意,這時構件作為對象仍然存在,只是未顯示在界面中而已,我們 隨時可以再次調用任何布局管理器方法來使構件可見。
- 前言
- 第 1 章 計算與計算思維
- 1.1 什么是計算?
- 1.1.1 計算機與計算
- 1.1.2 計算機語言
- 1.1.3 算法
- 1.1.4 實現
- 1.2 什么是計算思維?
- 1.2.1 計算思維的基本原則
- 1.2.2 計算思維的具體例子
- 1.2.3 日常生活中的計算思維
- 1.2.4 計算思維對其他學科的影響
- 1.3 初識 Python
- 1.3.1 Python 簡介
- 1.3.2 第一個程序
- 1.3.3 程序的執行方式
- 1.3.4 Python 語言的基本成分
- 1.4 程序排錯
- 1.5 練習
- 第 2 章 用數據表示現實世界
- 2.1 數據和數據類型
- 2.1.1 數據是對現實的抽象
- 2.1.1 常量與變量
- 2.1.2 數據類型
- 2.1.3 Python 的動態類型*
- 2.2 數值類型
- 2.2.1 整數類型 int
- 2.2.2 長整數類型 long
- 2.2.3 浮點數類型 float
- 2.2.4 數學庫模塊 math
- 2.2.5 復數類型 complex*
- 2.3 字符串類型 str
- 2.3.1 字符串類型的字面值形式
- 2.3.2 字符串類型的操作
- 2.3.3 字符的機內表示
- 2.3.4 字符串類型與其他類型的轉換
- 2.3.5 字符串庫 string
- 2.4 布爾類型 bool
- 2.4.1 關系運算
- 2.4.2 邏輯運算
- 2.4.3 布爾代數運算定律*
- 2.4.4 Python 中真假的表示與計算*
- 2.5 列表和元組類型
- 2.5.1 列表類型 list
- 2.5.2 元組類型 tuple
- 2.6 數據的輸入和輸出
- 2.6.1 數據的輸入
- 2.6.2 數據的輸出
- 2.6.3 格式化輸出
- 2.7 編程案例:查找問題
- 2.8 練習
- 第 3 章 數據處理的流程控制
- 3.1 順序控制結構
- 3.2 分支控制結構
- 3.2.1 單分支結構
- 3.2.2 兩路分支結構
- 3.2.3 多路分支結構
- 3.3 異常處理
- 3.3.1 傳統的錯誤檢測方法
- 3.3.2 傳統錯誤檢測方法的缺點
- 3.3.3 異常處理機制
- 3.4 循環控制結構
- 3.4.1 for 循環
- 3.4.2 while 循環
- 3.4.3 循環的非正常中斷
- 3.4.4 嵌套循環
- 3.5 結構化程序設計
- 3.5.1 程序開發過程
- 3.5.2 結構化程序設計的基本內容
- 3.6 編程案例:如何求 n 個數據的最大值?
- 3.6.1 幾種解題策略
- 3.6.2 經驗總結
- 3.7 Python 布爾表達式用作控制結構*
- 3.8 練習
- 第 4 章 模塊化編程
- 4.1 模塊化編程基本概念
- 4.1.1 模塊化設計概述
- 4.1.2 模塊化編程
- 4.1.3 編程語言對模塊化編程的支持
- 4.2 Python 語言中的函數
- 4.2.1 用函數減少重復代碼 首先看一個簡單的用字符畫一棵樹的程序:
- 4.2.2 用函數改善程序結構
- 4.2.3 用函數增強程序的通用性
- 4.2.4 小結:函數的定義與調用
- 4.2.5 變量的作用域
- 4.2.6 函數的返回值
- 4.3 自頂向下設計
- 4.3.1 頂層設計
- 4.3.2 第二層設計
- 4.3.3 第三層設計
- 4.3.4 第四層設計
- 4.3.5 自底向上實現與單元測試
- 4.3.6 開發過程小結
- 4.4 Python 模塊*
- 4.4.1 模塊的創建和使用
- 4.4.2 Python 程序架構
- 4.4.3 標準庫模塊
- 4.4.4 模塊的有條件執行
- 4.5 練習
- 第 5 章 圖形編程
- 5.1 概述
- 5.1.1 計算可視化
- 5.1.2 圖形是復雜數據
- 5.1.3 用對象表示復雜數據
- 5.2 Tkinter 圖形編程
- 5.2.1 導入模塊及創建根窗口
- 5.2.2 創建畫布
- 5.2.3 在畫布上繪圖
- 5.2.4 圖形的事件處理
- 5.3 編程案例
- 5.3.1 統計圖表
- 5.3.2 計算機動畫
- 5.4 軟件的層次化設計:一個案例
- 5.4.1 層次化體系結構
- 5.4.2 案例:圖形庫 graphics
- 5.4.3 graphics 與面向對象
- 5.5 練習
- 第 6 章 大量數據的表示和處理
- 6.1 概述
- 6.2 有序的數據集合體
- 6.2.1 字符串
- 6.2.2 列表
- 6.2.3 元組
- 6.3 無序的數據集合體
- 6.3.1 集合
- 6.3.2 字典
- 6.4 文件
- 6.4.1 文件的基本概念
- 6.4.2 文件操作
- 6.4.3 編程案例:文本文件分析
- 6.4.4 緩沖
- 6.4.5 二進制文件與隨機存取*
- 6.5 幾種高級數據結構*
- 6.5.1 鏈表
- 6.5.2 堆棧
- 6.5.3 隊列
- 6.6 練習
- 第 7 章 面向對象思想與編程
- 7.1 數據與操作:兩種觀點
- 7.1.1 面向過程觀點
- 7.1.2 面向對象觀點
- 7.1.3 類是類型概念的發展
- 7.2 面向對象編程
- 7.2.1 類的定義
- 7.2.2 對象的創建
- 7.2.3 對象方法的調用
- 7.2.4 編程實例:模擬炮彈飛行
- 7.2.5 類與模塊化
- 7.2.6 對象的集合體
- 7.3 超類與子類*
- 7.3.1 繼承
- 7.3.2 覆寫
- 7.3.3 多態性
- 7.4 面向對象設計*
- 7.5 練習
- 第 8 章 圖形用戶界面
- 8.1 圖形用戶界面概述
- 8.1.1 程序的用戶界面
- 8.1.2 圖形界面的組成
- 8.1.3 事件驅動
- 8.2 GUI 編程
- 8.2.1 UI 編程概述
- 8.2.2 初識 Tkinter
- 8.2.3 常見 GUI 構件的用法
- 8.2.4 布局
- 8.2.5 對話框*
- 8.3 Tkinter 事件驅動編程
- 8.3.1 事件和事件對象
- 8.3.2 事件處理
- 8.4 模型-視圖設計方法
- 8.4.1 將 GUI 應用程序封裝成對象
- 8.4.2 模型與視圖
- 8.4.3 編程案例:匯率換算器
- 8.5 練習
- 第 9 章 模擬與并發
- 9.1 模擬
- 9.1.1 計算機建模
- 9.1.2 隨機問題的建模與模擬
- 9.1.3 編程案例:乒乓球比賽模擬
- 9.2 原型法
- 9.3 并行計算*
- 9.3.1 串行、并發與并行
- 9.3.2 進程與線程
- 9.3.3 多線程編程的應用
- 9.3.4 Python 多線程編程
- 9.3.5 小結
- 9.4 練習
- 第 10 章 算法設計和分析
- 10.1 枚舉法
- 10.2 遞歸
- 10.3 分治法
- 10.4 貪心法
- 10.5 算法分析
- 10.5.1 算法復雜度
- 10.5.2 算法分析實例
- 10.6 不可計算的問題
- 10.7 練習
- 第 11 章 計算+X
- 11.1 計算數學
- 11.2 生物信息學
- 11.3 計算物理學
- 11.4 計算化學
- 11.5 計算經濟學
- 11.6 練習
- 附錄
- 1 Python 異常處理參考
- 2 Tkinter 畫布方法
- 3 Tkinter 編程參考
- 3.1 構件屬性值的設置
- 3.2 構件的標準屬性
- 3.3 各種構件的屬性
- 3.4 對話框
- 3.5 事件
- 參考文獻