#

框架(frame)是一種容器型的視窗元件,它的功能是可以把某些視窗元件裝載在一起,這樣一方面可以讓程式的介面看起來比較有條理,另一方面對我們寫程式的人來說,把多個視窗元件裝載在同一個frame裡,也可以方便我們控制整組的視窗元件,例如:同時隱藏或顯示。這一篇文章會介紹ttk的frame及labelframe等兩種視窗元件。

20.1 建立框架

相信經過之前章節的洗禮,大家對於建立視窗元件應該已經非常得心應手了,如果和你說建立框架(frame)的命令是::ttk::frame,你知道怎麼建立frame嗎? 應該沒問題吧!! 不就是::ttk::frame加上path再加上一些選項嗎? 是的就是這樣,下面的例子展示了建立frame的程式片段:



嗯~建立frame就真的只有這樣而以,但如果執行上面的程式可能會令你感到失望,因為root視窗上竟然什麼都沒有,甚至找不到root視窗。請不要懷疑這並不是程式出了問題,而是因為frame預設的樣式就是這個樣子,它沒有外框,沒有鮮明的背景,也沒有預設的寬度和高度。

□ 把視窗元件放進框架

如果想要真正的展現frame的特性,在它裡放點東西可以更容易觀察些,因為frame就是為此而存在的。現在就讓我們把上面的程式小改一下:



程式的第1行建立了一個叫.f1的frame,然後在第2~4行我建立了3個核取按鈕,並分別指定它們顯示Tcl、Python及Ruby。請特別注意!! 3個核取按鈕的路徑都是掛在.f1之下,所以根據「階層式路徑」的觀念,它們都會排放在.f1裡面。接下來的第5行我們用pack命令把剛剛建立的3個核取按鈕由上至下的排放。接下來請看到第7~11行,它們跟1~5行的觀念是一樣的,唯一不同的地方只有3個核取按鈕改成由左至右排放。最後第11行把兩個frame即.f1及.f2由上至下排放。這個程式執行起來像這樣:

圖 20-1


這個例子用了2個frame把6個核取方塊分成兩組,然後一組由上至下排放,另一組由左至右排放。有了frame是不是讓視窗的佈局更靈活了呢? 如果你感覺不出來的話,請試著不要使用frame然後排出一樣的佈局(連放大縮小的功能也要一樣)。相信你試過就會知道frame多好用了。

□ 均分父視窗元件的寬或高

在很多時候我也會用frame把多個按鈕組合在一起,讓它們均勻的分配父視窗元件的寬或高,因為這樣會讓介面好看些。請看下面的程式:



這個程式的1~2行,很單純的建立了一個lable及一個entry。接下來在第3行的地方建立了一個frame,然後在4~5行建立了兩個按鈕,而且這兩個按鈕的路徑都是掛在.f1之下,所以它們都會被排放在.f1的裡面,然後第6行使用了pack命令把這兩個按鈕排放出來。

第7~8行改使用grid來放排視窗元件,它把.lblName及.txtName放在root視窗的第一列且各佔一個格子,然後把.f1放在第二列但佔了2個格子。最後9~10行設定了root視窗上位於第1行第1列的視窗元件都要隨root視窗放大縮小。

所以程式執行起來像這樣:

圖 20-2


請注意下方的兩個按鈕,它們很均勻的佔據了橫向的空間。

□ 群組隱藏及顯示

在這篇文章的一開頭有提到,透過frame可以群組某些視窗元件,並且控制它們整組顯示或是隱藏,現在就讓我們來看看怎麼做:



這個程式的第一個重點在第7行,請看到-command裡的內容,我們透過pack forget來把.f1隱藏起來。第二個重點在第8行,對於已經隱藏起來的.f1我們可以再透過pack命令把它排放出來。其中,不管是第7行還是第8行,因為所有的核取方塊都是放在.f1裡面,所以它們會跟著被隱藏或是顯示。透過這個小技巧我們正確的完成了群組顯示或隱藏的功能。

20.2 調整框架的外觀

如果你希望修改frame預設的樣式,frame也有-borderwidth選項可以讓你調整邊框的大小,以及-relief選項讓你調整邊框的樣式。注意哦!!預設的情況下frame的邊框大小是0而且邊框樣式是flat,也就是說你可能要同時指定這兩個項目才會看出效果,讓我們再小改一下之前看過的程式:



請注意到第1行及第6行,我指定了frame的邊框大小是2,然後樣式是groove。而且為了方便觀查,我在第11行加上了-padx及-pady選項,這可以讓frame和root邊框有一點額外的空間。它執行起來像這樣:

圖 20-3


加了邊框是不是更有分組的感覺了呢? 如果你不小心忘了-relief的使用方法,請參考這一篇文章。

20.3 標籤式框架

標籤式框架和一般的框架有一些小小的不同,它可在框邊的框線上加上文字說明,而且預設是會顯示框線的,請看下面的例子:



請看第1行::ttk::labelframe是用來建立標籤式框架的命令,-text選項可以用來設定它的說明文字。它在Mac的系統下執行起來像這樣:

圖 20-4


圖中上框線的上面多了一行說明文字。如果是在Windows或Linux平台的話,說明文字會剛好卡在框線上。

labelframe也允許我們透過-labelanchor選項來設定說明文字要放在框線上的哪個位置,它可以接受的值是nw、n、ne、en、e、es、se、s、sw、ws、w及wn。說真的這個選項我平常都是使用預設值,如果你有興趣想知道這些設定值的效果,請自己試看看吧!!

30 個意見

匿名 | 2010年6月23日 下午4:55

我想請教一下,我用"群組隱藏及顯示"的例子,稍為小改一下,當使用者按下"隱藏"鍵時,我只隱
藏'TCL',如下: ::ttk::button .f2.btn1 -text "隱藏" -command {pack forget .f1.chk1}
;當使用者按下"顯示"鍵時,我把"TCL"、"PYTHON"、"RUBY" 這三個全都顯示出來,如下:
::ttk::button .f2.btn2 -text "顯示" -command {pack .f1.chk1 -side top -expand 1 -fill both}
但是如果按照我這樣做的話,沒錯他們三個都會顯示出來,可是他們的排序跟原本的排序不同了,請問
我如何寫才能讓它的排序跟原先的排序一樣呢?

sam | 2010年6月23日 下午6:26

暴力法:先將全部forget,再全部show出來。科科。

::ttk::button .f2.btn1 -text "隱藏" -command {pack forget .f1.chk1}
::ttk::button .f2.btn2 -text "顯示" -command {
pack forget .f1.chk1 .f1.chk2 .f1.chk3
pack .f1.chk1 .f1.chk2 .f1.chk3 -side top -expand 1 -fill both}

dai | 2010年6月23日 下午8:10

阿 ~ sam 你失蹤了好久 ... 終於又見到你了

sam的暴力法很好....因為我也都是用這樣的方法

感謝 sam 的回答

匿名 | 2010年6月24日 上午9:23

sam 謝謝您。

匿名 | 2010年6月24日 上午10:56

我想再請問一下,我怎麼可以設定checkbotton(正方塊)background的顏色呢?也就是說default
時,正方塊的background是藍色,如果使用者選了它(剔它)之後,正方塊就變成白色background。
簡單來說,如果剔它,它的background是白色,如果沒有剔它,它的background是藍色。

sam | 2010年6月24日 下午5:09

Hi

試試看這個~

pack [checkbutton .cb1 -text "Good button1"]
pack [checkbutton .cb2 -text "Good button2" -bg blue -fg white -selectcolor gray]

http://www.tcl.tk/man/tcl8.4/TkCmd/checkbutton.htm

匿名 | 2010年6月25日 上午9:17

Hello Sam,謝謝您。 我試過你的方法。checkbutton(正方塊)的顏色default是灰色,但是點撃後它
的顏色還是灰色,不曉得有沒有什麼方法,點擊後它的顏色可以改成我想要的顏色的呢? 謝謝!

sam | 2010年6月25日 上午9:53

set ::value 1
checkbutton .chk -text "CheckButton" -onvalue 1 -offvalue 0 -variable ::value -command {
if { $::value == 1 } {
.chk configure -selectcolor gray
} else {
.chk configure -selectcolor blue
}
}
grid .chk
----------------------------------------------------
Hi

目前我只想到可以這樣用,不知有沒有其它-option可以用,怎麼辦我技窮了~

匿名 | 2010年6月26日 下午12:19

sam, 謝謝。你這個方法對我來說, 已經很有用了。thanks.

isPeter | 2010年11月23日 下午3:34

hi Dai,

章節 "均分父視窗元件的寬或高" 內的 sample 第2行,我這邊執行會有錯誤。像是缺少 "-textvariable"

entry .txtName ::txtName

# change to:

entry .txtName -textvariable ::txtName

dai | 2010年11月30日 下午12:24

hi isPeter,

謝謝你幫忙指正,已經改好了 。

翊翾 | 2010年12月9日 上午11:12

Hi Dai~
好久不見了~ 最近想把整個TCL版面顏色做大改變
所以的元件都還可以改背景顏色
就只有labelframe的text 中的文字 以及背景都是預設值
無法改變,不知道有無方法能讓他改變背景顏色嗎?

dai | 2010年12月9日 下午10:40

Hi 翊翾
一個簡單的方法是用labelframe的-labelwidget選項,指定一個label widget取代labelframe的text區域,這樣就可以改text部份的顏色了。

匿名 | 2011年4月12日 上午9:36

Dear Dai:
我也是想請教一下關於"群組隱藏及顯示"的部份,我設定了兩個checkbutton,第一個checkbutton按下可出現及隱藏'one'的label,第二個checkbutton按下可出現及隱藏'two'的label,但是按出來的label順序會排至最下方,我希望'one'的label排在第一個checkbutton的下方,而'two'的label排在第二個checkbutton的下方,我的程式如下,請問要如何修改呢?謝謝!
#-------------------------------------------------------------
frame .fa1 -borderwidth 2 -relief groove
set sex0 1
checkbutton .fa1.btn1 -text "hide1" -command {

if {$sex0 == 1} {
pack .f1 -side top -expand 1 -fill both
set sex0 0
} else {
pack forget .f1
set sex0 1
}
}
frame .f1
label .f1.lab -text one -font { tahoma 8 }
entry .f1.ent -width 40 -bg white -fg blue
pack .f1.lab .f1.ent -expand 1 -side left

pack .fa1 .fa1.btn1 .f1 -side top -expand 1 -fill both
#-------------------------------------------------------------
frame .fa2 -borderwidth 2 -relief groove
set sex2 1
checkbutton .fa2.btn2 -text "hide2" -command {

if {$sex2 == 1} {
pack .f2 -side top -expand 1 -fill both
set sex2 0
} else {
pack forget .f2
set sex2 1
}
}
frame .f2
label .f2.lab -text two -font { tahoma 8 }
entry .f2.ent -width 40 -bg white -fg blue
pack .f2.lab .f2.ent -expand 1 -side left

pack .fa2 .fa2.btn2 .f2 -side top -expand 1 -fill both
#-------------------------------------------------------------


By YA

dai | 2011年4月18日 下午5:57

Hi 匿名的朋友,

請參考下面的程式碼

frame .fa1 -borderwidth 2 -relief groove
set sex0 0
checkbutton .fa1.btn1 -text "hide1" -command {
if {$sex0 == 1} {
pack .f1 -side top -expand 1 -fill both -after .fa1
set sex0 0
} else {
pack forget .f1
set sex0 1
}
}
frame .f1
label .f1.lab -text one -font { tahoma 8 }
entry .f1.ent -width 40 -bg white -fg blue
pack .f1.lab .f1.ent -expand 1 -side left

pack .fa1 .fa1.btn1 .f1 -side top -expand 1 -fill both
#-------------------------------------------------------------
frame .fa2 -borderwidth 2 -relief groove
set sex2 0
checkbutton .fa2.btn2 -text "hide2" -command {

if {$sex2 == 1} {
pack .f2 -side top -expand 1 -fill both -after .fa2
set sex2 0
} else {
pack forget .f2
set sex2 1
}
}
frame .f2
label .f2.lab -text two -font { tahoma 8 }
entry .f2.ent -width 40 -bg white -fg blue
pack .f2.lab .f2.ent -expand 1 -side left

pack .fa2 .fa2.btn2 .f2 -side top -expand 1 -fill both

HUUUUUU | 2012年4月24日 下午4:39

作者已經移除這則留言。

HUUUUUU | 2012年4月24日 下午5:18

Dai 大您好 請問上述的程式中
::ttk::frame .f1
::ttk::checkbutton .f1.chk1 -text "Tcl"
::ttk::checkbutton .f1.chk2 -text "Python"
::ttk::checkbutton .f1.chk3 -text "Ruby"
pack .f1.chk1 .f1.chk2 .f1.chk3 -side top -expand 1 -fill both
::ttk::frame .f2
::ttk::button .f2.btn1 -text "隱藏" -command {pack forget .f1}
::ttk::button .f2.btn2 -text "顯示" -command {pack .f1 -side top -expand 1 -fill both}
pack .f2.btn1 .f2.btn2 -side left -expand 1 -fill both
pack .f2 .f1 -side top -expand 1 -fill both

按下隱藏後會消失,但是frame1也會縮小,有沒有辦法讓f1留在原地,也就是說隱藏.f1.chk1後 欄位不縮小


再請教一個問題,如我希望entry 欄位裡面的數值不給人更動,除了改用label之外 有沒有指令能令使用者無法更改entry內部的文字?

dai | 2012年4月24日 下午11:42

不要讓frame1縮小有很多方法,其中一種像這樣:

::ttk::frame .f1
::ttk::checkbutton .f1.chk1 -text "Tcl"
::ttk::checkbutton .f1.chk2 -text "Python"
::ttk::checkbutton .f1.chk3 -text "Ruby"
pack .f1.chk1 .f1.chk2 .f1.chk3 -side top -expand 0 -fill both
::ttk::frame .f2
::ttk::button .f2.btn1 -text "隱藏" -command {pack forget .f1}
::ttk::button .f2.btn2 -text "顯示" -command {pack .f1 -side top -expand 1 -fill both}
pack .f2.btn1 .f2.btn2 -side left -expand 1 -fill both
pack .f2 .f1 -side top -expand 0 -fill both
wm geometry . 300x300


如果希望建立一個readonly的entry可以這樣:

ttk::entry .txt -state readonly

HUUUUUU | 2012年4月25日 上午10:52

感謝D大,但是我測試過後發現ttk::entry .txt -state readonly
只能在一開始設定,如果我這樣寫,只能在一開始輸入Text的數值,我可以設定一個button控制 -state readonly /-state normal 嗎
就好像是說按下button後entry 可以修改,再按一下他就不能修改~~
因為我剛剛試過,發現只能在一開始ttk::entry .txt -state readonly 決定readonly 後面好像不能修改?

感謝

HUUUUUU | 2012年4月26日 上午9:13

除了destory 之外有辦法辦到嗎@@?

dai | 2012年4月26日 上午11:45

可以這樣改

.txt configure -state normal

HUUUUUU | 2012年4月26日 下午2:15

感謝Dai大的解說 因為我是菜鳥,所以問題都很蠢..T^T..

dai | 2012年4月26日 下午5:13

別客氣!! 有問題可以儘量問~~

很高興又多一位Tcler 呵~

HUUUUUU | 2012年4月27日 下午4:36

謝謝Dai老師~拜謝OTz

HUUUUUU | 2012年4月27日 下午6:08

請問Dai老師有用過entry -validatecommand /-validate 這功能嗎? 我看TK的官網沒有例題看不太懂@@ 實際上如何使用呢?>.<"

dai | 2012年4月30日 下午11:37

是這樣的 -validate 用來指定要驗証的時機,例如 focusout 表示滑鼠停駐點,離開 entry 時要執行驗証的命令(-validatecommand)

-validatecommand 執行的結果必需是佈林值,一個最簡單的例子如下 :

entry .t -validate focusout -validatecommand {
set curr "%s"
puts "目前值 : $curr"
return 1
}

pack .t

HUUUUUU | 2012年5月2日 上午11:57

Dai老師,感謝您的舉例說明,
這個例子是說:當滑鼠停駐點改變時驗證 entry .t 內部輸入的值,將會被puts 出來 (%s ←)
return 1 又是甚麼意思呢??
還有-validatecommand 內的指令bind script (%d %i 等等) 又要如何使用@@?要增加判斷條件嗎?
因為我如果用以上的例子把%s改為%d 輸出一定都是-1 那我該如何用%d 讓驗證結果為1或是0??

感謝~~

匿名 | 2013年12月25日 下午4:54

Hi Dai 你好

想請教一個問題
我建立一個notebook的tab
並希望可以開關它
我執行.nb add [frame .nb.f1 -state]
這行程式時是可以關閉的


但執行.nb.f1 configure -state normal
這行程式時

它會出現unknown option "-state"

但我用其他的widget(如button or text)
卻可以執行

我有試過.nb state disable但它會關掉所有tab

想請問還有其他方法可以單獨關掉一個tab嗎?
謝謝




dai | 2013年12月25日 下午5:48

可以這樣:
.nb tab .nb.f1 -state disabled
或是:
.nb tab .nb.f1 -state normal

匿名 | 2013年12月25日 下午7:08

喔~太感謝了
沒注意到還有.nb tab可以用
謝謝 Dai

留下您的意見

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