#

一般來說除了menu(功能表)之外,使用Tk來製作視窗元件都需要經過兩個步驟:第一步是使用button、label之類的命令先建立視窗元件,然後再使用像pack、grid之類幾合管理命令把新建立的視窗元件排放在視窗上。這一篇文件主要在說明Tk的三個幾合管理命令,只要弄懂它們就可以自由的排放出各種視窗元件的佈局了。

16.1 place

place命令可以說是最直覺的幾合管理命令,因為它在排放視窗元件時是採用x軸、y軸、長及寬,當作參數來排放它們。注意哦!! 位置及長寬的單位都是畫素。例如:



這個例子讓.btn放在root視窗內,而且坐標是 (10,30),長寬各是100畫素。其中坐標(0,0)指的是父視窗元件的左上角。它的執行畫面如下:

圖 16-1


在上面的例子中我們使用了畫素來表示座標及長寬的絕對值。事實上place也允許你使用相對值來設定這些參數,如下面的例子:



用-rel開頭的參數表示要使用相對值來設定視窗元件,它們都是相對於父視窗元件。簡單的說就是使用百分比的意思,例如:-relwidth 0.4 就表示寬度要設定為父視窗元件的0.4倍寬。-relx 0.5就表示為x軸的坐標是父視窗元件寬度的一半。如果採用了這種相對值的方法,你會發現子視窗元件會隨者它的父視窗元件放大縮小,而且位置也會相對移動。所以這個例子執行起來像這樣:

圖 16-2


另外,Tcl也允許你混用絕對值及相對值的參數,如果是這種情況的話,place會把絕對值和相對值加起來,例如:



這樣的話.btn的寬度將會是父視窗元件的一半再加100個畫素。

接下來是一些比較不常用的參數,如果有興趣的話請自己試試

-anchor where 這個參數可以強制視窗元件黏住父視窗元件的某一邊。where的值可以是n、ne、e、se、s、sw、w、nw, 或center。其中的n、e、w、s分別表示北、東、西、南。
-bordermode mode 這個參數可以用來指定視窗元件排放時與父視窗元件邊框的關系(父視窗也許會有邊框,而邊框線也會有寬度)。如果mode指定為inside,表示坐標的(0,0)是在父視窗元件的框線內開始算。如果mode是outside,表示坐標的(0,0)是在父視窗元件的框線外開始算。最後一個模式是ignore,因為官方文件說這個參數不會用到,所以...別理它。
-in master 我們知道Tk是由視窗元件的path來決定父視窗元件是誰,如果你想要打破這個規則,可以使用這個參數來強制指定master作為父視窗元件。

如果你排放完視窗元件後想要改變它們的設定值,可以使用place configure命令。例如,下面的程式每按一下Move按鈕它就會自己向右移10畫素。我們使用全域變數::x來存儲按鈕目前的x軸座標,然後每按下一次按鈕就把::x加上10。特別注意到程式的第4行,我們用place configure來重新設定按鈕的-x參數讓按鈕的x座標會反應::x的現在值。另外place configure可以一次指定多個參數,Tk並沒有限定你一次只能更變一個值。



接下來我們要看看怎麼樣把已經排放出來的視窗元件給拿下來。請看下面的例子,當我們按下forget按鈕,place forget就會把已經排放在畫面上的視窗元件拿下來,也就是變為還沒排放的狀態。當然想要重新排放只要再執行place一次命令就可以了。



最後再介紹一個比較有機會用到的功能。place slaves可以用來取得某個父視窗元件內包含了哪些的子視窗元件。例如下面的例子,會以清單的方式印出root視窗元件內的子項目。



16.2 pack

pack提供了由上到下、由下到上、由左至右、由右至左的排放功能。依個人的使用經驗,它比place更常使用,更順手,所以請儘可能熟悉它吧!! 好我們來看第一個例子。



在上面的例子裡,我們透過pack的-side選項來指定btn1~btn3要由「左至右」排放。-side的值除了left之外,你還可以指定為right由「右至左」、top由「上至下」、 bottom由「下至上」來排放視窗元件。這個例子的執行畫面如下:

圖 16-3


如果把-side指定為top的話,執行起來會變成這樣子,btn1~btn3由上至下的排放:

圖 16-4


請你再執行一次上面的例子,然後試著拉大和縮小root視窗,你應該會發現一件事:btn1~btn3並不會跟著root視窗改變大小。如果你想要讓btn1~btn3其中一個或它們的全部都跟著root視窗縮放,那你就需要使用-expand及-fill的選項了。-expand可以用來設定視窗元件是否要佔滿父視窗元件的「剩餘空間」,-fill可以用來指定視窗元件本身是否要填滿剩餘空間。那什麼是剩餘空間? 簡單的說就是沒有被視窗元件佔用的空間。我們用下面的例子來說明-expand的用途:



這個例子我們只先設定了-expand,看看排放起來會有什麼效果。-expand設定為1表示要佔滿剩餘空間,設定為0表示不要佔滿。圖16-5是把-expand設定為0的執行畫面,圖16-6是把-expand設定為1的執行畫面,你應該會發現剩餘空間被平均分給btn1~btn3了。

圖 16-5 把-expand設為0


圖 16-6 把-expand設為1


如果你想要只讓btn1~btn3其中之一佔滿剩餘空間,那就要把pack命令拆成3行來寫了,像下面的程式,只讓btn2佔滿剩餘空間。所以btn1及btn3被推到邊邊了。



執行畫面如下:

圖 16-7 把-expand設為1


剩餘空間的觀念在後面的pack及grid命令也會用到,請一定要清礎它的觀念。接下來我們要指定-fill選項了,指定它的話可以讓視窗元件的本身填滿剩餘空間。請看下面的例子:



注意程式的第5~6行。-fill可以指定的值有none、x、y及both。指定none表示不要填補,指定x表示要填滿x軸,指定y表示要填滿y軸,指定both表示要x及y軸要同時填滿。所以執行起來會變這樣:btn2把xy兩軸都填滿了,btn3只填滿了y軸,而btn1因為沒有指定-fill所以沒有填任一軸:

圖 16-8 指定-fill選項


再來看一個比較有感覺的例子:



這次我們把btn1~btn3都加上了-expand的選項,所以它們會均分剩餘空間。這樣-fill的效果就更容易看出來了。btn1指定了-fill x所以它槙滿了x軸,btn2指定了-fill both所以它的xy軸都填滿了,最後btn3指定了-fill y所以填滿了y軸。

圖 16-9


以下是pack命令可以接受的其它選項,它們都很容易了解有興趣的話請自己試試。

-after other -after選項用來指定視窗元件要排放在other之後。
-anchor anchor -anchor選項用來指定視窗元件要對齊剩餘空間的哪一個方位。請參考place的-anchor選項。
-before other -before選項用來指定視窗元件要排放在other之前。
-in master 我們知道Tk是由視窗元件的path來決定父視窗元件是誰,如果你想要打破這個規則,可以使用這個參數來強制指定master作為父視窗元件。
-ipadx amount 這個選項可以讓你在「剩餘空間」及「視窗元件」之間(左邊及右邊)加入大小amount的空間。這樣用-fill x的時候就不會把整x個軸填滿。單位是畫素。
-ipady amount 這個選項可以讓你在「剩餘空間」及「視窗元件」之間(上面及下面)加入大小amount的空間。這樣用-fill y的時候就不會把整y個軸填滿。單位是畫素。
-padx amount 這個選項可以讓你在「剩餘空間」及「剩餘空間」之間(左邊及右邊)加入大小amount的空間。單位是畫素。
-pady amount 這個選項可以讓你在「剩餘空間」及「剩餘空間」之間(上面及下面)加入大小amount的空間。單位是畫素。

pack命令也跟place一樣可以用pack configure來重新設定選項值,可以用pack forget把已經排放出來的視窗元件拿下來,或是用pack slaves來取得某個父視窗元件內的子項目。詳細的用法請參考上一小節的place命令。

16.3 grid

grid命令可以說是Tk裡功能最強的幾合管理命令,這意謂著...我們要學比較多的東西。 事實上grid是我用最多的幾合管理命令,因為它的功能實在是好用,而且很容易就可以把視窗元件排得整整齊齊的,非常的建議把它用熟。grid的主要觀念是,父視窗元件上畫有水平和垂直的虛擬格線,這些格線組成很多的小格子,然後我們可以把視窗元件放在這些格子裡。例如我們要產生一個填資料的表單:



請注意程式的7~9行,我們用3個grid命令排出了3列,而且每一列有2行,所以它執行起來像這樣子:

圖 16-10


當然我們也可以排成3x2的樣子,像這樣:



執行起來就變成這樣:

圖 16-11


如果你不想要像上面那樣一個grid命令排好多個視窗元件,你也可以用下面的法方一次排一個視窗元件就好。



一次排一個視窗元件的方法,必需要自己指定-row(第幾列)及-column(第幾行)來設定視窗元件被排放的位置。如果沒有指定的話每一個視窗元件都會被當成一列。下面是可能會用到一次排一個視窗元件的時機。我們用程式來製作一個3x3乘法表,程式執行時像下面的圖一樣,使用者每按一下畫面上的按鈕就會輸出計算的結果:

圖 16-12


它的程式碼如下:



這個程式使用了2個foreach迴圈來產生9個按鈕,而且我故意不由第0行第0列開始放,反正不會影響到外觀,就別在意吧!!。請注意到第4行,以前我們都是使用大括號來包含-command後面的程式,在這邊這樣是行不通的,原因是-command後面的程式被執行時,Tcl會把那些程式當成是在「::」頂層的名稱空間下執行。所以在這樣的情況下Tcl會找不到$row及$col變數。一個決解的方法就是像我一樣使用「雙引號包夾」這樣$row及$col的值在執行button命令時就會先被代換出來了,因為值被直接代換出來了,所以就不用擔心在頂層名稱空間會找不到$row及$col了。另一個更好的方法是像下面一樣用list命令,事實上我都是用下面的方法比較多。



以下是grid可以接受的其它選項:

-columnspan n 一般情況每個視窗元件只會佔用一行一列,指定這個選項可以讓視窗元件佔用n行。
-rowspan n 一般情況每個視窗元件只會佔用一行一列,指定這個選項可以讓視窗元件佔用n列。
-sticky style 這個選項可以用來指定視窗元件要黏住剩餘空間的那一邊,style可以由n、e、w、s等4個方位任意組成。如果指定為ns它的效果就像pack的-fill y一樣,如果指定為we效果就像pack的-fill x一樣。如果指定為news效果就像pack的-fill both一樣。
-in master 我們知道Tk是由視窗元件的path來決定父視窗元件是誰,如果你想要打破這個規則,可以使用這個參數來強制指定master作為父視窗元件。
-ipadx amount 這個選項可以讓你在「剩餘空間」及「視窗元件」之間(左邊及右邊)加入大小amount的空間。單位是畫素。
-ipady amount 這個選項可以讓你在「剩餘空間」及「視窗元件」之間(上面及下面)加入大小amount的空間。單位是畫素。
-padx amount 這個選項可以讓你在「剩餘空間」及「剩餘空間」之間(左邊及右邊)加入大小amount的空間。單位是畫素。
-pady amount 這個選項可以讓你在「剩餘空間」及「剩餘空間」之間(上面及下面)加入大小amount的空間。單位是畫素。

之前看到的pack命令可以用-expand來決定哪些視窗元件要佔滿剩餘空間。但,從上面的列表你應該有發現grid裡並沒這樣的選項。沒錯!! grid要用另外的方法來設定,像下面的語法一樣,grid可以一次設定排在某一行的視窗元件都要佔滿剩餘空間。

grid columnconfigure master index -weight 1

其中master是父視窗元件的path,而index是要佔滿剩餘空間的第n行,n必需由0開始算。當然,我們也可以設定某一列都要佔滿剩餘空間:

grid rowconfigure master index -weight 1

下面的例子改寫自上面的3x3乘法表,我們透過columnconfigure及rowconfigure來把位於第2行及第2列的視窗元件都佔滿剩餘空間。



注意哦!! 程式的第4行我幫每一個grid的命令都加上了-sticky news的選項。所以它執行起來像這樣,位於第2行第2列的視窗元件都會跟著放大縮小。

圖 16-13


grid命令也跟place及pack一樣可以用grid configure來重新設定選項值,可以用grid forget把已經排放出來的視窗元件拿下來,或是用grid slaves來取得某個父視窗元件內的子項目。詳細的用法請參考之前的place命令。其它grid還有一些比較少用到的功能,如果你有興想要學深入一點的話,請自行參考Tcl/Tk的官方手冊吧。

★ 最後提醒一點:請不要在某一個父視窗下混用兩種以上的幾合管理命令,不然程式視窗元件可能會顯示不出來哦!!

8 個意見

匿名 | 2012年9月12日 下午6:17

Dai您好,我想請問在嘗試圖16-12的程式時
我下了wish test.tcl的指令後並沒有像只下wish一樣出現console視窗+程式視窗
只有跳出程式視窗後就結束了
導致沒有地方可以輸出結果
這是哪裡的問題呢?

匿名 | 2012年9月12日 下午6:28

自問自答一下
原來要在程式裡面加上console show
抱歉,沒注意到 ^^"

豬事大吉 | 2013年5月3日 上午9:16

請問ROOT試窗可以固定大小嗎?

dai | 2013年5月3日 上午11:04

試試

wm resizable window ?width height?

豬事大吉 | 2013年5月8日 下午4:19

感謝dai指點

Mike | 2014年12月22日 下午2:01

Dai大大您好,我可以請問一下,如果我不想用place一個一個去牌的話,pack或grid可以這樣排版嗎?
[ text0 ]
[ text1 ]
[text2][text3]
[text4][text5]
我這邊嘗試用pack排版,結果都會變成,第三行沒辦法下移
[ text0 ]
[ text1 ]
[text2][text3][text4][text5]
pack .text0 .text1 -side top
pack .text2 .text3 -side left
pack .text4 text5 -side left
用grid排版則變成,grid的寬度會和第一行的設定一樣
[ text0 ]
[ text1 ]
[ text2 txet3 ] 兩行重疊overwrite,似乎他的寬度設定會和第一行grid一樣
gird .text0 -row 0 -column 0 -columnspan 2
gird .text1 -row 1 -column 0 -columnspan 2
gird .text2 -row 2 -column 0 -columnspan 2 -sticky w
gird .text3 -row 2 -column 1 -columnspan 2 -sticky w

匿名 | 2014年12月22日 晚上7:01

一般遇到這樣的情況,用grid應該是較方便的方法
grid .t0 -sticky news
grid. t1 -sticky news
grid .t2 .t3 -sticky news
grid .t4 .t5 -sticky news

dai

匿名 | 2022年11月18日 下午4:35

您好,請問程式碼的部分是否看不到了?

留下您的意見

Theme Design by devolux.org. Converted by Wordpress To Blogger for WP Blogger Themes. Sponsored by iBlogtoBlog
This template is brought to you by : allblogtools.com | Blogger Templates