#

ttk的treeview視窗元件提供了樹狀及多欄位的資料顯示功能,也就是說treeview可以讓我們做出類似檔案總管左邊的樹狀結構方塊,或是做出條列式的檔案清單方塊。在這一個章節,我們會簡單的介紹treeview最基本的使用方法,也就是建立平面式而且只有一個欄位的清單方塊,至於更進階的功能有機會的話會在以後的章節說明。

21.1 建立清單方塊

使用ttk的treeview建立平面式、單一欄位的清單方塊算是treeview最簡單的使用方法,請看下面的程式範例:



這個程式的第1行建立了一個treeview視窗元件,然後指定它的-show選項為tree,這樣會讓清單方塊上的標題列不要顯示出來。當然你想要顯示標題列的話,不指定-show選項就可以了。

接下來的2~3行使用了treeview的insert命令來把「項目1」及「項目2」插入清單方塊的尾巴(end)。請注意到insert後面的大括號,它可以用來指定新建立的項目要安插在哪一個已存在項目的下層(因為treeview可以允許你建立一個樹狀的清單方塊),如果指定為 {} 就表示要把新建立的項目安插在root(根)項目之下。接下來的-text選項可以用來設定新建立項目的文字內容。當insert命令執行後它會回傳一個獨一無二的項目id,這個項目id可以用來識別這一個新建立的項目,往後我們可以靠這個id來刪除或取用對應的項目。它執行起來像這樣:

圖 21-1


□ 階層式的treeview

接下來讓我們稍微測試一下樹狀結構怎麼做:



請注意到第3~4行,在insert後面我指定要把項目安插在item1的下層,所以它顯示起來就變成了一棵樹:

圖 21-2


□ 掃描檔案清單

讓我們再來看一個比較實用的例子,這個例子可以掃描你選擇的資料夾,然後把MP3檔案全都加入清單方塊:



因為這個程式用到的都是以前說明過的命令,所以這邊就不多做解釋囉。它執行起來像這樣:

圖 21-3


□ 加入捲軸方塊

不知道你們執行時上面的程式有沒有發現一個小問題,就是MP3檔案的數量多到超出清單方塊的可視區域時,清單方塊不會自動跑出水平或垂直的「捲軸方塊」就像下面的圖一樣:

圖 21-4


事實上這是正常的啦!! 因為我們並沒有幫這個清單方塊加上「捲軸方塊」的視窗元件!! 請看下面的程式,它示範了捲軸方塊的使用方法:



哇~~這個程式變得有點長...。在說明之前讓我們來了解捲軸方塊的運作原理。重點有兩個 1.捲軸方塊在拉的時候,要讓程式知道哪一個視窗元件的可視區域(內容)要跟著捲動 2.視窗元件的可視區域在捲動時,也要通知對應的捲軸方塊跟著捲動。

ok!! 有了上面的觀念以後就可以看程式了,現在我們要開始處理的是重點1的部份。請先看到第10~11行,我們用::ttk::scrollbar來建立捲軸方塊,它的-orient可以用來決定捲軸方塊的類型,horizontal表示採用水平顯示,vertical表示採用垂直顯示。然後兩個捲動方塊的-command可以用來設定「哪一個視窗元件的可視區域要跟著自己捲動」,其中「list $tree xview」表示$tree這個視窗元件的x軸要跟著捲動,而「list $tree yview」表示$tree這個視窗元件的y軸要跟著捲動。

接下來要處理重點2的部份了。請看到12~13行,treeview的-yscrollcommand選項可以用來設定treeview的y軸可視區域變動時,那一個捲動方塊要跟著捲動,其中「list $sv set」表示$sv這個捲軸方塊要跟著捲動。當然-xscrollcommand也是採用相同的原理。所以這個程式執行起來就會像下面一樣:

圖 21-5


清單方塊的內容超出可視區域時,xy的捲軸方塊會發揮功用了。

21.2 刪除項目

前一個小節的內容我們使用了treeview的insert命令來建立新的項目,而且也說了insert命令在執行後會回傳一個獨一無二的id,現在我們要來使用這個id了。treeview的delete可以用來刪除一票的項目,刪除一個項目的方法像這樣:



請注意到第4行,我用delete把清單方塊中的「項目1」刪掉了,所以執行上面的程式時只有「項目2」會顯示在清單方塊上。

□ 一次刪除多個項目

如果你想要一次刪多個項目的話可以這樣做:



上面的例子我一次刪掉了2個項目,當然你可以一次刪掉更多的項目,只要把它們都接在delete後的清單就可以了。

□ 清空清單方塊

如果你想要一次把所有的項目都清掉的話,可以使用下面的程式範例:



請注意到第4行,treeview的children可以用清單的方式取得某個項目下層的「子項目」,在這個例子裡我把root(根)項目之下的所有子項目都取出來然後刪除它們,當然這樣的結果就會把清單方塊上的所有項目都刪掉。

21.3 查詢及設定項目資訊

treeview的item命令,可以用來查詢或是設定已建立項目的資訊。例如你想要修改項目的文字內容可以這樣做:



注意到程式的第4行,我透過了treeview的item命令來重新設定$item1的文字內容為「項目A」。

上面示範了設定的用法,如果是查詢的話就更簡單了,只要使用下面的程式片段就可以了:



上面的一行程式可以輸出$item1上的文字內容。

21.4 選取控制

treeview也可以允許我們透過程式去查詢目前清單方塊上已選取(反白)的項目,或是把某些項目設定為已選取,選取項目的範例如下:



這個程式的重點在第6行,treeview的selection命令可以用來取出清單方塊上已選取的項目的id清單。在預設的情況下treeview是採用多選模式的,也就是說你可以配合的shift及ctrl鍵來選取多個項目。如果想要改成單選模式的話,可以在建立treeview視窗元件時指定-selectmode選項,指定為extended表示為多選模式,指定為browse表示使用單選模式。例如:



□ 設定選取狀態

treeview提供了多種方法讓我們使用程式來改變清單方塊中項目的選取狀態,現在讓我們先看看如何選取清單中的項目:



請注意到程式的第5行,treeview的selection set命令可以用來切換選取的項目,selection set可以接受一個項目id的清單,只要是包含在這個清單上的項目都會被設定為選取狀態,其它不包含在內的項目則會被設定為未選取。這個例子中設定了「項目1」及「項目3」,所以它執行起來像這個樣子:

圖 21-6


上面的selection set可用來切換選取的項目群,但有些時候我們也會想要用「添加」的方式來選取項目,也就是在不清除目前選取的項目狀況下加入新的選取項目。這樣子的功能可以使用treeview的selection add命令來完成,例如:



注意到程式的第6行,我用添加的方式把「項目2」也一併選取了,所以現在所有的項目都是處於選取中的狀態。如下圖:

圖 21-7


□ 移除選取狀態

除了可以設定項目變成選取狀態外,當然也有對應的移除選取狀態的功能,使用selection remove即可以移除特定項目群的選取狀態,例如:



$items 是一個項目id的清單,凡是包含在內的項目,都會被設為未選取。

□ 切換選取狀態

treeview也提供了方便的selection toggle命令讓你可以切換特定項目群的狀態,例如:



$items 是一個項目id的清單,凡是包含在內的項目,都會被改變狀態,也就是選取的變未選取,末選取的變選取。

41 個意見

hsuan | 2009年9月29日 上午11:10

關於捲軸方塊, 請問要如何實現像是wireshark中, automatic scrolling in live capture? 即如何讓scrollbar 能夠自動的依照所輸出的資料, 自動捲動到最新的位置(通常是最下方)?

Dai | 2009年9月29日 下午4:35

假設你永遠都把最後一個建立的item儲存在$end變數裡,那你只要執行下面的一行程式scrollbar就會捲到$end這個項目的位置:

$tree see $end

hsuan | 2009年9月30日 下午10:30

謝謝你的回應。其實我現在是想用tcl實做一個serial console(com1)程式,但我遇到幾個問題,不知是否適合在這邊提出?

第一個就是auto scrolling,現在程式可以把serial com1的output顯示在text widget中,隨著資料的輸出,scrollbar越變越短,但不會自動跑到最下方顯示最新的資料。你提到可以用see這個指令去實現,但text widget是否也支援此種方式?

另一個問題是,我同時想在text widget裡輸入指令到com1裡,我知道如何送資料到com1,但是不知道要如何抓取從text widget輸入的值,再送到com1中,不知你知道有沒有什麼方法?

Dai | 2009年10月1日 上午10:05

text也可以用一樣的方法,像這樣:

$txt see end

從text取資料的例子如下:

$txt get 1.0 2.1 ;#從第1行的開頭取到第2行第1個字

$txt get 2.3 end ;#從第2行第3個字取到最後一個字

$txt get "3.2 linestart" "4.7 lineend" ;#從第3行的開頭取到第4行的尾巴

ps.用tcl寫serial port件一個非常幸福的事,因為真的非常...非常...的方便。我熱愛Tcl有一部份是這個原因~哈哈!!

TerryWang | 2009年12月11日 下午1:41

請問Dai大...由於公司使用的8.4.19版的ActiveTCL

我在tclsh下該如何成功使用ttk::的指令呢??

我拿您這篇的範例要練習...可是一直出現
invalid command name "::ttk::treeview"

麻煩幫忙一下!!!

dai | 2009年12月11日 下午2:19

8.4版的Tcl沒有內建ttk,所以你可能要先做以下兩個步驟:

1.安裝tile

$ teacup install tile

2.在程式碼的開頭引入tile

package require tile


這樣就可以了

TerryWang | 2009年12月11日 下午2:52

哈...自行解決了!!!

package require Tk
package require tile

假如沒裝過tile..可以cmd 執行c:\tcl\bin\teacup tile 即可

繼續了解TCL...呵呵

dai | 2009年12月11日 下午7:08

呵~~恭喜。

翊翾 | 2009年12月31日 下午1:41

Dai^^~我又來發問了
如果我想treeview中做一個搜尋的功能
我要怎麼讓我選到的選項
有被選擇到且反白的效果呢?

翊翾 | 2009年12月31日 下午2:28

Dai 你好
目前我已經找到方法可以選擇tree中的item
使用 $tree selection set $item
不過現在的問題是我選到一個tree中資料夾中的item
他並不會自動展開資料夾
請問有展開tree中資料夾的指令嗎?

dai | 2009年12月31日 下午4:02

有的~可以這樣子 :

$tree item $item -open true


如果要收起來的話:

$tree item $item -open false

新年快樂 ^^

小白 | 2011年1月19日 下午4:47

Dai 您好
請問treeview 可以做成syn的嗎?
set tree [::ttk::treeview .tree -show tree]
set item1 [$tree insert {} end -text "項目1"]
set item2 [$tree insert {} end -text "項目2"]
set item3 [$tree insert {} end -text "項目3"]
$tree selection set [list $item1 $item3]
$tree selection add $item2
pack $tree -expand 1 -fill both
#$tree selection remove $items
bind $tree {
set deleitem [$::tree selection]

$tree selection remove $deleitem
update
}
以上範例,我按Delete時候,刪除的東西不能馬上更新
假設我刪除項目1,照理來說list的清單要變成只剩項目2.3
但是卻還是1.2.3都在上面,麻煩幫我解答><

小白 | 2011年1月25日 上午9:17

bind $tree <> {...}
我漏打了 sorry

小白 | 2011年1月25日 上午10:09

研究出來了,在這邊分享給大家~~~~
按下enter會insert child到選擇的項目內
按下delete會刪除選擇的項目
package require tile

set tree [::ttk::treeview .tree -show tree]
set item1 [$tree insert {} end -text "項目1"]
set item2 [$tree insert {} end -text "項目2"]
set item3 [$tree insert {} end -text "項目3"]
$tree selection set [list $item1 $item3]
$tree selection add $item2
pack $tree -expand 1 -fill both


#tree selection insert $items
bind $tree < Return > {
set deleitem [$::tree selection]

.tree insert $deleitem end -text "child"
update
}

#tree delete insert $items
bind $tree < Delete > {
set deleitem [$::tree selection]

.tree delete $deleitem
update
}

dai | 2011年1月28日 上午12:38

這個例子很實用,謝謝小白的分享~

匿名 | 2012年1月12日 下午6:14

Hi, Dai

首先感謝你所寫的文章,讓我受益良多...

以下是我想問的問題:

我是透過GPIB去控制示波器,然後會抓取示波器的資訊

我現在是想把抓到的資訊秀出來(抓一筆資料,秀一筆)

我想到的是用text widget

但是就是沒辦法實現,目前的結果都是把資料抓完再一次秀出來

For example:

Data 1: 123456
Data 2: 123666
...
請問一下使用text widget可行嗎?
如果可以我該如何修改?
For example:

text .t -width 80 -height 20 -bg white
pack .t
for {set i 0} {$i < 10} {incr i} {
set data [getdata times ]
.t insert end $data

}

proc getdata {times} {
...
}

dai | 2012年1月13日 下午7:43

這要先看 [getdata times ] 是一次回傳所有資料 或是一次一行 ~
如果是一次就回傳所有的資料,用.t insert 就一定會一次把資料顯示出來,除非自己手動切開每一行 ~
如果說是一次一行,那你可以在.t insert 的上一行加入 update 或是 用after命令 讓每次insert資料的中間delay一點時間 ~

翊翾 | 2012年5月24日 下午1:24

哈囉~Dai~
好久不見,請問一下關於treeview的問題,如果我想改變文字顏色字型
set tree [::ttk::treeview .tree -show tree -foreground blue]
或者
set item1 [$tree insert {} end -text "項目1" -foreground blue]
都會顯示 unknown option "-foreground"
這套件沒辦法使用嗎?

dai | 2012年5月24日 下午5:50

Hi 翊翾,

真的很久不見了 @@ 。 請試試下面的程式,重點在treeview的tag運用:

set tree [::ttk::treeview .tree -show tree]
set item1 [$tree insert {} end -text "項目1" -tag blue]
set item2 [$tree insert {} end -text "項目2"]
pack $tree -expand 1 -fill both

$tree tag configure blue -foreground blue

翊翾 | 2012年5月25日 下午1:30

Dai大大
原來tag是這樣使用的阿...
又學到一招,希望我可以畫出美美的treeview,感恩呢^^

吉米 | 2012年5月30日 上午12:08

要美美的tree,可以玩一下treectrl
不過要小心上癮,這個功能強大,變化多端
一不小心就沉迷於調整出漂亮的介面,而荒廢了程式主要的功能

Romeo | 2012年11月5日 下午2:09

Dai,您好
我想請問一下
如果我要從選擇的這邊抓出value而不是selection ID的話
我該怎麼做呢?

Romeo | 2012年11月7日 下午4:37

我自己回答吧

set testitem [$seltree selection]
set text [.selectarea.tree item $testitem -text]

puts $text
就可以取出text值了

現在另一個問題是,請問我要怎樣能夠選擇parent的項目的時候,會自動選擇parent底下所有children項目呢?

Romeo | 2012年11月7日 下午6:08

作者已經移除這則留言。

Romeo | 2012年11月8日 上午9:40

set testitem [$seltree selection]
# 先取出selected item name
set itname [.selectarea.tree item $testitem]

foreach selected $testitem {
# 取得選擇項目的 parent id
set pp [.selectarea.tree parent $selected]
puts $pp
}

如果selection的parent id為空,就取出此id底下所有children項目

Romeo | 2012年11月8日 下午6:11

Dai
不知道你有沒有發現
水平捲軸在Treeview好像並不會動
我網路上看了,這好像是個Bug

小豬 | 2014年1月28日 上午9:56

如何可以讓scrollbar 自動回到最頂端

IAN | 2014年12月22日 下午2:11

你好,我也是剛開始使用TCL的使用者,想請問如果想在清單裡面使用checkbutton,而不使用反白,該怎麼做呢?

dai | 2014年12月22日 下午7:42

有點懶寫的不是很好,但原理大概是這樣

set t [ttk::treeview .t -show tree ]
pack .t -expand 1 -fill both

set ::imgCheck [image create photo -file check.gif]
set ::imgUncheck [image create photo -file uncheck.gif]

.t insert {} end -text 1 -tags tag1
.t tag configure tag1 -image $::imgCheck

.t tag bind tag1 {
if {[.t tag configure tag1 -image] == $::imgCheck} {
.t tag configure tag1 -image $::imgUncheck
} else {
.t tag configure tag1 -image $::imgCheck
}
}

Ian | 2014年12月23日 上午9:09

十分感謝在百忙之中還撥冗解惑!!
我嘗試執行你的程式碼,卻出現以下畫面,請問是何種問題?
couldn't open "check.gif":no such file or directory while executing "image create photo - file check.gif"......
另外想請教仿間有無推薦的TCL指令集或工具書可供參考。 TKs

dai | 2014年12月23日 上午10:32

1. 這個tcl程式檔需要跟2個gif的檔案放在同一個資料夾中,分別是 check.gif及uncheck.gif (請自己用小畫家做16x16)
2. 推薦這一本書: Practical Programming in Tcl and Tk (4th Edition)

Ian | 2014年12月24日 下午6:20

HI DAI, 想請教你一個問題,我原本有個程式長這樣
set id1 [$tree item $item1 -text]
puts [exec wish $id1] 是可以執行的
後來因需求改成
set i 1
set id$i [$tree item $[item$i] -text]
puts [exec wish $[id$i]]
執行結果卻出現 ERROR : invalid command name "item1"
請問是何問題?

dai | 2014年12月25日 下午6:38

用陣列比較方便吧!!

set i 1
set id($i) [$tree item $item($i) -text]
puts [exec wish $id($i)]

Ian | 2014年12月26日 上午9:27

HI Dai,嘗試了你說的方法,他卻出現不一樣的問題
Error: can't read"item(2)":no such variable

Ian | 2014年12月29日 上午11:07

HI Dai,上次的問題解決了,想請教你一個新的問題,因為有時候TREE裡面的item是後來執行的時候才增加,而不是一開始就設定好了,我如果想透過selection id 抓清單上新增加的value,該怎麼寫呢?

dai | 2014年12月29日 下午1:09

應該可以這樣做 :
1.所有你新增的item id都用陣列存起來
2.然後參考21.3及21.4小節應該就可以達到功能了

Ian | 2014年12月29日 下午1:47

抱歉,我可能不是講得很清楚,我的意思是說就像你上面其中一個範例是增加mp3的檔案,你可能一次增加幾個你不確定,所以他們就沒辦法事先設定到ITEM? 裡面,但是新增完他們就有SELECTION ID,我想問的是如何透過SELECTION ID,去PUTS你剛增加的MP3檔案,其中的一個MP3的名稱

dai | 2014年12月29日 下午7:09

不管item是預先建立或動態產生的,按下按鈕永遠顯示選中的項目:

set tree [::ttk::treeview .tree -show tree]
foreach item [list 1 2 3 4 5] {
set id [$tree insert {} end -text "項目$item"]
set item($id) "私人資料xxx" ;# 用陣列保存id及item私人資料
}
set btn [::ttk::button .btn -text "選取的項目" -command {
set id [.tree selection] ;#參考21.4
puts [.tree item $id -text] ;#參考21.3
puts $item($id)
}]
pack $tree -expand 1 -fill both
pack $btn -expand 1 -fill both

Ian | 2014年12月30日 上午10:03

HI DAI,十分感謝解答我的疑惑,原本看到 puts [$tree item $item1 -text],我就認為只能顯示原本設定好的ITEM,看到
set id [.tree selection] ;#參考21.4
puts [.tree item $id -text] ;#參考21.3
才恍然大悟,原來可以這樣顯示SELECTION的清單名稱^^

dai | 2014年12月30日 上午11:31

祝你學習順利~加油!!

鄭盛樹 | 2016年1月21日 下午1:25

Dai大你好:
ttk treeview 的水平卷軸好像不會動喔
請問有處理方法嗎?
謝謝

留下您的意見

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