#

終於...要開始介紹Tk了。Tk是專門用來繪製視窗元件(widget)的工具,如果你想要製作視窗介面的程式,就要用到Tk。事實上我會喜歡用Tcl有絕大部份是因為Tk,因為它真的太方便了。記得碩士班的時候,學弟把Tcl/Tk移植到ARM的板子上,然後在X Window下做了幾個簡單按鈕。一次meeting學弟把它的成果展示給大家看,顯然大家有驚訝到,然後開始一陣跨獎聲出來了....畢竟,觸碰式螢幕加上自己寫的GUI程式真的滿令人興奮的。當學弟開心的展示完成後,攤開程式碼....不到10行...大家傻了!!。那一次的經歷讓大家知道了一件事實,原來學Tcl/Tk還可以...唬人。雖然內行人會知道porting才是重點,但透過Tk開發GUI的能力確實讓那次的展示加了不少分。唉呀!! 說了很多的廢話,還是進入主題吧!! 這一篇的內容主要在介紹一些關於Tk的觀念。

15.1 視窗元件(Widget)

什麼是視窗元件? 簡單的說任何用來組成視窗介面的物件,就叫視窗元件,例如:按鈕、標籤、清單方塊、下拉選單....等,都是視窗元件。圖15-1~15-3是幾個常見的Tk視窗元件。

圖15-1 Mac下Tk視窗元件的外觀


它在Linux下看起來像這樣:

圖15-2 Linux下Tk視窗元件的外觀


它在Windows下看起來像這樣:

圖15-3 Windows下Tk視窗元件的外觀



15.2 視窗元件命名規則

在之前的文章裡有說過,每一個視窗元件,都需都有一個獨一無二的ID,而且要用點號「.」開頭。其實這種「.」號開頭的元件ID,正式的稱呼是path,中文的意思是「路徑」。那為什麼要叫path呢? 這是因為path除了用來識別widget之外,還有表示階層的意義,下面的文章會說明階層的觀念。

在Tk裡「.」永遠用來表示root(根)視窗,如果你有一個path是.btn的widget,那就表示.btn在顯示時要排放在root視窗裡面。又如果你有一個path叫.btn.lbl的widget,那就表示.btn.lbl要放在.btn的裡面。接下來我們用程式說明這種widget階層式命名觀念。

如果你在命令模式下直接執行wish,你會看到類似下面的圖:

圖15-4 Tk的root視窗


圖中空白的視窗就是root視窗,它是所有widget的根,也就是說所有你建立的widget都算是它的子項目。如果它被關掉的話,其它所有的widget也會一起被關掉,正常的情況下我們只會在結束程式的時候關閉它。接著我們來建立幾個以前用過的widget:



上面的例子我們把按鈕的path設定為.btn,標籤的path設定為.lbl,所以pack會把它們排放在「.」也就是root視窗的內層。另外指定pack命令的-expand及-fill選項,讓.btn及.lbl跟著root視窗放大縮小。程式執行起來像這樣:

圖15-5 在root視窗裡排放元件


現在我們來故意亂玩一下,如果把label的path命名為.btn.lbl看會發生什麼事,注意下方程式的第2行,label的path被我改了,而且這次只讓.btn跟著root視窗放大縮小。



它執行起來像下面的圖片一樣。.btn.lbl被排放在.btn裡面了!! 這件事說明了pack命令會根據widget的path來決定widget到底要放在誰的內層。而事實上除了pack之外Tk還有place及grid兩個排放widget的命令,它們也使用了這樣子的規則。

圖15-6 把widget排放在另一個widget裡面


其實在真正寫程式的時候,不太會像上面,把label排放在button裡面。取而代之的我們會用frame、labelframe、頁籤或是其它容器型的widget來裝載其它的widget。例如:

圖15-7 frame及labelframe


如圖15-7,button1及button2我用一個frame裝了起來,而label及entry我改用labelframe裝了起來。關於這些視窗元件的用法,請大家還不要太在意,因為未來會有詳細的說明。目前我們只要弄清礎widget命名及排放的觀念就行了。

15.3 關於Tk命令

在Tk的命令群裡,只要是用來建立視窗元件的命令,大多都採用像下面這樣子的語法:

Tk命令 path ?選項1 選項值1? ?選項2 選項值2? ... ?選項n 選項值n?

這種語法都是以建立視窗元件的「Tk命令」開頭,例如:button、label、entry...等。接著是視窗元件的路徑path。最後緊接著的是一大堆視窗元件可用的選擇性參數,而且它們都是以一根「-」開頭,像-text、-command、-background...等。由於它們都可以選擇性附加,所以我叫它們做「選項」,就是選擇性項目的縮寫。另外,每一個建立視窗元件的命令,它們都一定會回傳你指定的path,就是說如果你打button .btn -text "hello",那這個命令的回傳值也一定是.btn。事實上每一個用來建立視窗元件的命令都是這樣用的,而且沒有一個例外,所以只要看懂了上面的語法,你就會建立全部的Tk視窗元件了。

當你會建立各種視窗元件後,下一步要做的事就是「熟悉每個視窗元件的選項」。關於這一點,你只能靠多看手冊來熟悉它們。個人的小建議是:

  1. 每天看幾個命令的說明手冊,相信我它們很快就可以看完了。
  2. 當你看完1~2次手冊,對每個命令及它們的選項應該就會有一點印像,這樣就夠了,千萬別去背它們。
  3. 有印像後,每次寫程式前記得打開手冊,邊寫程式邊查手冊。
  4. 最後,多找些小程式練習,這樣會讓你加快命令及選項的熟練度。

到目前為止我們知道path有兩項功能 (1)用來當作視窗元件的識別 (2)給幾合管理命令當作排放的依據。其實它還有第3項功能,只要建立視窗元件成功後,path會被附予控制該視窗元件的能力,簡單的說path會變成一個控制視窗元件本身的命令,舉例來說:



在第1行執行後.txt會變成了一個專門用來控制自己的命令,然後第2行用.txt命令的insert功能來把「Hello Tk!!」插入文字方塊的尾巴。很神奇對吧!! 如果現在還沒辦法體會這個用法,請不要太擔心,因為以後我們會把它用爛....真的。

34 個意見

Midas | 2009年9月4日 下午2:28

Dear Sir .
我遇到一個問題 , 就是我用 Tk 的 text . 發現 無論如何. 記憶體就是越來越大 無論 我有沒有 砍了 裡面的內容..請問這怎麼辦呢?

package require Tk

text .txt1 -undo 0
pack .txt1

catch {console show}
set str ""
for {set i 0} {$i < 1000} {incr i} {
append str $i\n
.txt1 insert end $str
.txt1 see end
update
}
#到這裡 , wish85.exe 己長大到 66.632KB memory size 了.
.txt1 delete 1.0 end
.txt1 edit reset
#還是一樣...的 size....

Dai | 2009年9月6日 凌晨1:06

這個問題是出在
append str $i\n
.txt1 insert end $str

也這你是想要寫成
.txt1 insert end $i\n

事實上你不用太擔心Tk佔用了這麼多的記憶體,Tk只是先留住那些記憶體,如果你以後的程式需要時會由那些記憶體先分配。

餅乾 | 2009年11月12日 上午11:30

你好:想請教一件事,假若我希望在Tcl/Tk中可以直接呼叫Firefox瀏覽器的話,需要外掛什麼程式或者套件呢?謝謝。

Dai | 2009年11月12日 晚上10:25

不用裝額外的套件,執行下面的命令就可以了。

exec firefox的執行檔

餅乾 | 2009年11月13日 上午10:27

謝謝大大的回答,
只是說假若我是想將firefox的brower嵌入我所設計的一個tk程式中的話,
例如一個frame中的話,
需要怎麼做呢?
謝謝。

Dai | 2009年11月13日 下午1:09

我沒有這樣使用過,也許你可以參考下面的文章:

How to embed a non-Tk GUI into a Tk frame

sam | 2009年12月29日 下午3:36

Dai大大,請問一下,TK C中(tk嵌入c),要新建立一個TK元件首先要建立一windows,就是用Tk_CreateMainWindow(),但是我在編譯時出現'Tk_CreateMainWindow': 找不到識別項的錯誤,也就是找不到這個函式,標頭檔和lib沒有問題,因為其它的Tk_XXXX的函式都沒有問題,而我也去檔案中(C:\Tcl\include\目錄下header file叫tkDecls),尋找Tk_CreateMainWindow()確實找不到這個函式,其它的函式都有,請問一下這個最重要的Tk_CreateMainWindow()跑到哪里去了,Dai大大知道哪裡出了挫啵嗎,我用的是ActiveTcl8.5,謝謝。

sam | 2009年12月29日 下午4:23

嗯,找到答案了,自問自答一下:在tk4.x版時,好像把建立parent window的Tk_CreateMainWindow()給拿掉了,取而代之的是改用Tk_Init()就會幫您建立好這個window了喔,拍水,書太舊了(tcl and the tk toolkid)還在用Tk_CreateMainWindow(),有點跟不上時代了…

dai | 2009年12月29日 下午4:36

阿 ~ sam研究的很深入阿!! 說真的我沒有用過Tk的C API,我只有用Tcl的C API。 以後有問題就可以問你了。

sam | 2010年1月4日 下午5:40

dai大大言重了,跟大大比起來小的研究如九牛一毛丫,不信你看,我又來問大大你問題了,這次想問dai大大,若要在電腦上執行包含TCL指令的執行檔(我是嵌入到C++中),是不是那一台電腦一定要灌TCL/TK呢,我把產生出來的執行檔丟到沒有灌TCL/TK的電腦上執行,會顯示找不到tcl85.dll動態連結檔的訊息,不知道有沒有辦法讓包含TCL指令的執行檔在沒有灌TCL/TK的環境下執行呢,謝謝dai大大。

dai | 2010年1月4日 晚上9:18

沒有言重,沒有言重,一切屬實 阿 。


如果編譯的時候加上靜態連結的選項,就可以不用.dll或是.so,用g++的話加上 -static 參數,如果是vc的話...我就不清楚了。

sam | 2010年1月5日 上午9:23

dai大大,我在想說若要在沒有灌TCL/TK的PC上執行包含TCL的程式,我猜應該是行不通,因為TCL/TK的指令不是會被tclsh或wish直譯器所執行嗎,若電腦中沒有灌TCL/TK的話,那不就沒有這二個直譯器可以使用了嗎,想請問大大我這樣的想法是對的嗎,3Q...

dai | 2010年1月5日 上午11:59

是這樣的

若你的程式是用C寫的:

基本上如果是用c/c++寫的Tcl APP,你可以在C程式裡建立直譯器,並把直譯器初始化成像tclsh及wish一樣,也就是說,你可以把Tcl APP當成是你客製化過的直譯器。在這樣的情況下,只要你編譯Tcl APP時使用static的方法產生執行檔,如此一來你就可以在這個執行檔裡使用最基本的Tcl命令,而不需要安裝Tcl/Tk。

若你的程式是用Tcl Script寫的:

在這種情況你可以使用Tclkit及sdx把你的程式打包成單一可執行檔,在這樣的情況下,不用安裝Tcl/Tk也可以執行你的程式。類似的工具還有freewrap。

chunlin | 2012年9月12日 晚上9:21

Dai你好
我想請問一下
如何將console內嵌到root視窗內?

chunlin | 2012年9月15日 下午2:54

各位高手請幫忙
可能說的不太清楚
舉個簡單的例子
console show
button .btn -text "put" -command {puts "3"}
pack .btn
執行上面的程式會有一個widget+console兩個視窗
如何把console整合到button的上面?
謝謝!

Unknown | 2013年6月20日 下午6:18

DAI大大 如何利用TCL 自動壓縮數個檔案進一個壓縮檔阿?

dai | 2013年6月21日 晚上7:00

在windows下我喜歡用7-zip,像這樣:

1. 先把7z.exe , 7z.dll 加入環境變數 PATH

2. 然後執行像這樣的tcl的命令:

exec 7z.exe 輸出.zip file1.exe file2.txt file3.c

其中file1~3就是你要壓縮的檔案



Unknown | 2013年6月26日 上午10:32

感謝大大

我以為很複雜

Unknown | 2013年6月26日 上午11:54

我照你說的做了

但是他還是說無法執行耶~

是哪邊細節錯誤?

dai | 2013年6月26日 中午12:16

貼上錯誤訊息,我幫你看看!

Unknown | 2013年6月26日 下午1:25

couldn't execute "7z.exe" : no such file or directory while executing "exec 7z.exe"
(file "C:/User/XXXXX/Desktop/compress/main.tcl"line 1)

dai | 2013年6月26日 下午5:48

1.這可能是你沒有安裝7-zip的主程式
2.或是你沒有把7z.exe加到Windows的PATH環境變數

Unknown | 2013年6月27日 上午10:00

我應該都有用到

會不會是WIN8的問題

因為我OS 是WIN8

Unknown | 2013年6月27日 下午1:23

exec 7z.exe 輸出.zip file1.exe file2.txt file3.c

輸出的意思是?

dai | 2013年6月27日 晚上7:14

我假設你要把file1.exe file2.txt file3.c 這三個檔案壓縮到 輸出.zip 這個檔案裡。

dai | 2013年6月27日 晚上7:14

抱歉!! 我好像少打了一個 a 的參數

exec 7z.exe a 輸出.zip file1.exe file2.txt file3.c

Unknown | 2013年6月28日 下午3:36

不用抱歉啦 超感謝你 成功了

順便問一下 a 什麼意思啊 ?

要如何指定固定資料夾 讓他去找到FILE1 FILE2 那些

匿名 | 2013年6月30日 晚上8:34

您好 我來請教下
如果 遇到TCL 裡面剛好有 exec ping 127.0.0.1 -t 這種無限的指令
因為資源一直沒有回到TCL
所以底下的指令都無法執行
如何解決這種問題??
希望把 ping 127.0.0.1 -t 的資訊顯示在grid txt上面
謝謝

dai | 2013年7月1日 中午12:09

要改用pipe及fileevent類似這樣:

* 不管是linux或windows都要先確定有cat.exe而且可以執行

set fd [open "| ping 127.0.0.1 -t |& cat" r]

然後可以用fileevent註冊readable event 去讀 fd ,然後把結果增加到text上顯示

fileevent的使用方法可以參考官方手冊:

http://www.tcl.tk/man/tcl8.6/TclCmd/fileevent.htm



Mike | 2014年12月17日 上午10:02

可否請問幾個問題,因為還在研究要不要使用tcl/tk來開發 xwindow 下的軟體
1. tcl/tk 可不可以自動判斷是否有視窗存在,若有我就以視窗輸出訊息,若無就從console輸出文字訊息? 因為Unix下我不一定會在xwindow下操作系統。
我現在的做法是看有沒有參數輸入,如果有參數輸入就當作console,沒有就output視窗讓user重選單選擇,但有時只是output message不一定會有參數輸入,這樣就很麻煩。
2. 即便如此,不知道是不是我script前面是#!/usr/bin/wish的關係,因為沒有視窗訊息,他好像都會多顯示一段
"application-specific initialization failed: no display name and no $DISPLAY environment variable"
這個可以不要顯示嗎?
3. tcl/tk 可以自訂執行 script的icon嗎? 就像一般qt 編輯出來的程式一樣有自己的icon?

匿名 | 2014年12月17日 晚上7:31

嗯 ~ 都可以做到

1. 改用 #!/usr/bin/tclsh
2. 判斷環境變數 $DISPLAY 來決定是否要載入Tk (package require Tk)
3. tcl/tk可以自訂icon沒問題,這要看你Linux的桌面系統來決定怎麼設定


dai

Mike | 2014年12月18日 下午2:55

非常感謝Dai 大大的說明,我現在正著手把WIndows的程式移植到Linux上,也算是Tcl/Tk 的新手再否在請問一下,
我可以在一個label裡面放兩個圖示嗎? 而且一個是背景,一個是前景(Logo)這樣?
我目前的做法是用兩個Label,但是我Logo沒有圖的地方,就會是一個空白的方塊,把背景的顏色給蓋過去了。
不知道tcl/tk有沒有辦法處裡的這問題? 不知道這樣說明有沒有了解,或許我用圖示表示比較清楚。
兩個圖示合併後在Windows上期望的樣子:
http://www.mikehu.idv.tw/files/1.png
兩個圖示用兩個Label合併後在ubuntu上實際的樣子:
http://www.mikehu.idv.tw/files/2.png
這樣有辦法解嗎?

另外再問一個簡單的問題,但是我google了半天還是找不到答案,就是我主視窗的背景顏色要如何更改啊?
我想要把灰色(可能是ubuntu預設)改成白色的說。

匿名 | 2014年12月18日 晚上8:18

1.用label應該做不到,改用canvas應該可以。 ttk::label 也許也做得到,但我沒有試過這樣用!!

2. 下面一行程式可以改root視窗的背景

. configure -bg yellow

Mike | 2014年12月19日 上午10:53

Orz...再次感謝Dai大大的解惑,最後我用Canvas解決,就沒再去研究ttk::label了~

留下您的意見

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