前面介紹的LabelEntryCombobox…等元件,每個元件一次只能顯示或編輯「一筆」資料。但實務上常常需要一次顯示「多筆、多欄位」的資料,例如查詢資料庫後要把所有學生資料列成一張表格,這時候如果每一筆資料都用一排Label來顯示,不但寫起來麻煩,畫面也不好管理。

ttk模組提供的Treeview元件,可以用「表格」的方式一次顯示多欄、多列資料(類似Excel工作表或Windows檔案總管的詳細資料檢視),並支援讓使用者點選其中一列。建立語法如下:

物件變數 = ttk.Treeview(容器物件, columns=(欄位代號1, 欄位代號2, …), show='headings', [其他參數])

建立步驟

與前面的元件不同,Treeview通常需要下面幾個步驟才能組成一張完整的表格:

  1. 建立Treeview物件:透過columns參數指定每一欄的「欄位代號」(供程式內部識別用,不會直接顯示在畫面上),show='headings'表示只顯示自訂的欄位標題列,隱藏tkinter預設會顯示的第一個樹狀縮排欄位(因為這裡只需要平面表格,不需要樹狀結構)。
  2. 設定欄位標題:對每個欄位呼叫heading(欄位代號, text=標題文字),設定該欄在畫面上顯示的標題文字。
  3. 設定欄位寬度與對齊:對每個欄位呼叫column(欄位代號, width=寬度, anchor=對齊方式)anchor可指定'center'(置中)、'w'(靠左)、'e'(靠右)等。
  4. 新增資料列:呼叫insert(父節點, 位置, values=(值1, 值2, …))新增一列資料。由於這裡不使用樹狀結構,父節點固定給空字串''(代表最上層),位置通常給'end'表示插入到清單最後面。

常用方法

方法說明
heading(欄位代號, text=標題)設定指定欄位的標題文字。
column(欄位代號, width=寬度, anchor=對齊)設定指定欄位的寬度與對齊方式。
insert(父節點, 位置, values=(值,...))新增一列資料,values的順序須對應columns參數的欄位順序。
get_children()取得目前清單中所有列的識別碼(item id),常搭配迴圈用來走訪或清空所有資料列。
delete(item_id)刪除指定識別碼的那一列資料。
item(item_id, 'values')取得(或設定)指定列目前的資料內容。
selection()取得目前使用者選取的列識別碼(可能有多筆,回傳為tuple)。
selection_remove(item_id, ...)取消指定列的選取狀態。

[簡例] 清空並重新載入清單資料時,常見寫法是先用get_children()取出所有列、逐一delete(),再重新insert()一次,避免舊資料與新資料重複顯示:

for row in tree.get_children():
    tree.delete(row)
for row in 資料來源:
    tree.insert('', 'end', values=row)

事件綁定:bind() 與 <<TreeviewSelect>>

前面ButtonRadiobuttonCheckbutton都是用command參數指定「按下/狀態改變時」要執行的函式,但Treeview本身沒有command參數,因為「使用者選取某一列」是Treeview特有的動作,必須改用所有tkinter元件都通用的bind()方法來監聽事件:

物件變數.bind(事件名稱, 處理函式)

Treeview在使用者選取(或改變選取)某一列時,會觸發tkinter內建的「虛擬事件」'<<TreeviewSelect>>',因此只要用bind()把這個事件綁定到自訂函式即可:

def on_select(event):
    selected = tree.selection()
    if not selected:
        return
    ...
 
tree.bind('<<TreeviewSelect>>', on_select)

綁定事件的處理函式(如上面的on_select)都必須固定接收一個event參數,這個參數是由tkinter在事件觸發時自動傳入的物件,內含事件相關資訊;即使函式內用不到event的內容,也不能省略這個參數。

[簡例] 建立一張成績清單表格,使用者點選某一列時,把該列姓名與成績帶出並顯示在下方標籤。(檔名:treeview01.py)

[結果]
(可自行執行程式碼查看畫面:上方為三欄成績表格,點選任一列後下方標籤會顯示該列姓名與成績。)

說明

  1. 8~9行:建立tree這個Treeviewcolumns指定idnamescore三個欄位代號,show='headings'隱藏預設的樹狀欄位。
  2. 10~15行:分別用heading()設定三欄的標題文字,用column()設定欄寬與置中對齊。
  3. 18~20行:用迴圈把data串列中的每一筆資料,依序用insert('', 'end', values=row)新增到清單最後面。
  4. 25~30行:定義on_select()函式,用tree.selection()取得目前選取的列,再用tree.item(選取id, 'values')取出該列的(座號, 姓名, 成績)資料。
  5. 32行:用bind('<<TreeviewSelect>>', on_select)把「選取清單項目」事件綁定到on_select()函式,之後只要使用者點選清單中的任一列就會自動觸發。

在 student_crud.py 的應用

student_crud.py_build_tree()refresh()on_select()三個方法就是這裡介紹方法的完整實際應用:

  • _build_tree():建立self.tree並設定 7 個欄位(cIDcNamecSexcBirthdaycEmailcPhonecAddr)的標題與寬度,並用bind('<<TreeviewSelect>>', self.on_select)綁定選取事件。
  • refresh():每次重新查詢資料庫後,先用get_children() + delete()清空清單,再依查詢結果逐筆insert(),確保畫面永遠反映資料庫最新內容。
  • on_select():使用者點選某一列時,用selection()取得選取的列,再用item(選取id, 'values')取出該列資料填回上方表單,並記錄self.selected_id供「修改」/「刪除」使用。

相關主題與延伸閱讀