不間斷 Python 挑戰 Day 30 - 使用tkinter開發GUI程式:常用視窗元件

閱讀時間約 19 分鐘
之前的文章中介紹過了Turtle Graphics,它是架構於tkinter模組之上以實現基本圖形介面的繪圖模組,這篇文章就來說明tkinter模組常用元件(widget)的基本用法,以及如何用它來設計出一個具有圖形使用者介面(Graphical User Interface, GUI)的程式。tkinter是Python內建的模組,使用時將它import進來即可,不需額外安裝,詳細用法可以參照以下官方文件:
https://docs.python.org/3/library/tkinter.html
https://www.tcl.tk/man/tcl8.6/TkCmd/contents.html

視窗(Window)

首先,將tkinter模組載入後,先以Tk()方法建立一個根視窗(root window)以放置其它的元件,與視窗相關的常用方法如下:
title():設定視窗標題
geometry("widthxheight"):設定視窗的寬與高,單位是像素(pixel)
minsize(width=int, height=int):設定視窗最小的寬與高,單位是像素(pixel)
maxsize(width=int, height=int):設定視窗最大的寬與高,單位是像素(pixel)
resizable(width=bool, height=bool):設定是否可更改視窗寬與高的大小
mainloop():等待處理視窗事件,讓程式可以繼續執行
from tkinter import *
window = Tk()
window.title("Hello World!")
window.minsize(width=500, height=500)
window.resizable(width=False, height=False)
window.mainloop()
執行結果:
根視窗

元件(Widget)

標籤(Label)

Label()方法可建立文字或圖形標籤,Label()的第一個參數為要包含這個標籤的視窗物件,也可省略不寫,其它常用的參數如下:
text:設定文字內容
font:設定字型與大小
width:設定寬度,單位是字元
height:設定高度,單位是字元
padx:設定左右空白的空間,單位是像素
pady:設定上下空白的空間,單位是像素
background/bg:設定背景顏色
foreground/fg:設定文字顏色
為了讓標籤出現在視窗,可以使用pack()方法將標籤包裝到視窗上,此方法也適用在其它元件上,詳細視窗元件配置的方法之後會在提到。
label = Label(text="my label", font=("Arial", 14, "bold"), padx=5, pady=5, bg="red", fg="yellow")
label.pack()
執行結果:
標籤

按鈕(Button)

按鈕可讓使用者在按下後執行某一些行為,因此除了按鈕本身的設定以外,還需要定義按下按鈕時所要執行的事件處理函式。Button()方法可建立按鈕元件,Button()的第一個參數為要包含這個按鈕的視窗物件,也可省略不寫,其它常用的參數如下:
text:設定按鈕文字
font:設定字型與大小
width:設定寬度,單位是字元
height:設定高度,單位是字元
padx:設定左右空白的空間,單位是像素
pady:設定上下空白的空間,單位是像素
background/bg:設定背景顏色
foreground/fg:設定文字顏色
command:按鈕按下後所要執行的方法
以下範例定義一個button_clicked()方法,當按下按鈕後,透過config()方法修改標籤的text參數,印出"Hello World!"。
def button_clicked():
  label.config(text="Hello World!")

button = Button(text="Click Me", font=("Arial", 14, "bold"), padx=5, pady=5, bg="blue", fg="light green", command=button_clicked)
button.pack()
執行結果:
按鈕

文字方塊(Entry)

文字方塊為單行的文字輸入區域,由Entry()方法來建立物件,Entry()的第一個參數為要包含這個文字方塊的視窗物件,也可省略不寫,其它常用的參數如下:
font:設定字型與大小
width:設定寬度,單位是字元
padx:文字最左或最右與文字方塊邊框的間距,單位是像素
pady:文字最上或最下與文字方塊邊框的間距,單位是像素
background/bg:設定背景顏色
foreground/fg:設定文字顏色
state:設定是否可輸入文字,預設為NORMAL,表示可以輸入,若設定為DISABLED則無法輸入
show:輸入文字時顯示的字元,例如用在輸入密碼時可設定show="*"以顯示星號
此外,還有一些常用的方法:
insert(index, string):在第index個字元前插入字串string
get():取得文字方塊內的文字
以下的範例在文字方塊中寫入"some text",END表示從文字的最末端開始插入,並在PyCharm的輸出視窗中印出文字方塊中的文字。
entry = Entry(width=30, font=("Arial", 14, "bold"), bg="red", fg="yellow", state=NORMAL)
entry.insert(END, string="some text")
entry.pack()
print(entry.get())
執行結果:
文字方塊
some text

文字區域(Text)

文字區域和文字方塊類似,差別在於文字區域可以輸入多行的文字,由Text()方法來建立物件,Text()的第一個參數為要包含這個文字區域的視窗物件,也可省略不寫,其它常用的參數如下:
font:設定字型與大小
width:設定寬度,單位是字元
height:設定高度,單位是字元
background/bg:設定背景顏色
foreground/fg:設定文字顏色
state:設定是否可輸入文字,預設為NORMAL,表示可以輸入,若設定為DISABLED則無法輸入
和文字方塊類似,有一些常用的方法:
insert(index, string):在第index個字元前插入字串string
get("row1.index1", "row2.index2"):取得從row1行第index1之後到row2行第index2個字元的文字
以下範例在文字區域中寫入三行文字,分別是"line 1"、"line 2"、以及"line 3",並在PyCharm的輸出視窗中印出從第一行開始到第二行的第四個字元。
text = Text(height=5, width=30, font=("Arial", 14, "bold"), bg="blue", fg="light green", state=NORMAL)=
text.insert(END, "line 1\nline 2\nline 3")
text.pack()
print(text.get("1.0", "2.4"))
執行結果:
文字區域
line 1
line

Spinbox

Spinbox可以輸入文字,並可以用捲軸來調整文字內容,由Spinbox()方法來建立物件,第一個參數為要包含這個Spinbox的視窗物件,也可省略不寫,其它常用的參數如下:
from_:當內容為數字時的初值
to:當內容為數字時的終值
font:設定字型與大小
width:設定寬度,單位是字元
background/bg:設定背景顏色
foreground/fg:設定文字顏色
state:設定是否可更改內容,預設為NORMAL,表示可以更動,若設定為DISABLED則無法輸入或使用
command:按下捲軸按鈕後所要執行的方法
以下範例建立一個可調整數字區間為0到10的Spinbox,當按下捲軸按鈕時,使用get()方法取得Spinbox的文字內容並輸出。
def spinbox_used():
  print(spinbox.get())

spinbox = Spinbox(from_=0, to=10, width=5, font=("Arial", 14, "bold"), command=spinbox_used, bg="red", fg="yellow", state=NORMAL)
spinbox.pack()
執行結果:
Spinbox
1
2
3
4
5
6
7
8
9
10

Scale

Scale帶有一個可移動的滑桿,用來調整一固定連續範圍內的數字,由Scale()方法建立物件,第一個參數為要包含這個Scale的視窗物件,也可省略不寫,其它常用的參數如下:
from_:連續數字範圍的初值
to:連續數字範圍的終值
font:設定字型與大小
width:設定滑桿寬度,單位是像素
background/bg:設定背景顏色
foreground/fg:設定文字顏色
orient:設定滑桿為水平(HORIZONTAL)或垂直(VERTICAL),預設為垂直
length:設定滑桿長度,預設為100像素
以下範例建立一個可調整數字區間為0到10的水平Scale,當滑動滑桿時,會將目前調整的數值傳入command指定的函數中並輸出。
def scale_used(value):
  print(value)

scale = Scale(from_=0, to=10, font=("Arial", 14, "bold"), width=15, orient=HORIZONTAL, length=200, command=scale_used)
scale.pack()
執行結果:
Scale
1
2
3
4
5
6
7
8
9
10

選項鈕(Radiobutton)

選項鈕可讓使用者點擊勾選,並且同時只有一個選項鈕可以被選取,由Radiobutton()方法建立物件,第一個參數為要包含這個選項鈕的視窗物件,也可省略不寫,其它常用的參數如下:
text:設定選項鈕旁的文字
font:設定字型與大小
width:設定文字的寬度,單位是字元
height:設定文字的行數
padx:設定左右空白的空間,單位是像素
pady:設定上下空白的空間,單位是像素
background/bg:設定背景顏色
foreground/fg:設定文字顏色
value:選項鈕的值
command:點擊選項鈕時所要執行的方法
variable:設定或取得目前選取的選項按鈕
以下範例建立兩個選項鈕,並使用tkinter的整數類別變數radio_state來存取目前選取的選項鈕,當選項鈕被選取時,在輸出視窗中印出目前選取的按鈕。
def radiobutton_used():
  print(radio_state.get())

radio_state = IntVar()
radiobutton1 = Radiobutton(text="Option1", value=1, variable=radio_state, command=radiobutton_used)
radiobutton2 = Radiobutton(text="Option2", value=2, variable=radio_state, command=radiobutton_used)
radiobutton1.pack()
radiobutton2.pack()
執行結果:
選項鈕
1
2
1

核取方塊(Checkbutton)

相較於選項鈕只能單選,核取方塊可以一次選取多個項目,由Checkbutton()方法建立物件,第一個參數為要包含這個核取方塊的視窗物件,也可省略不寫,其它常用的參數如下:
text:設定核取方塊旁的文字
font:設定字型與大小
width:設定文字的寬度,單位是字元
height:設定文字的行數
padx:設定左右空白的空間,單位是像素
pady:設定上下空白的空間,單位是像素
background/bg:設定背景顏色
foreground/fg:設定文字顏色
command:點擊核取方塊時所要執行的方法
variable:設定或取得目前選取的核取方塊
以下範例建立兩個核取方塊,並使用兩個tkinter的整數變數來存取目前選取的核取方塊,當核取方塊被選取時,在輸出視窗中印出目前選取的選項。
def checkbutton_clicked():
  print(f"Option1: {checked_state1.get()}, Option2: {checked_state2.get()}")

checked_state1 = IntVar()
checked_state2 = IntVar()

checkbutton1 = Checkbutton(text="Option1", variable=checked_state1, command=checkbutton_clicked)
checkbutton2 = Checkbutton(text="Option2", variable=checked_state2, command=checkbutton_clicked)
checkbutton1.pack()
checkbutton2.pack()
執行結果:
核取方塊
Option1: 1, Option2: 0
Option1: 1, Option2: 1
Option1: 0, Option2: 1

列表選單

列表選單可在表單內顯示多個選項,使用者可做單選或多選,由Listbox()方法來建立物件,第一個參數為要包含這個列表選單的視窗物件,也可省略不寫,其它常用的參數如下:
font:設定字型與大小
width:設定文字的寬度,單位是字元
height:設定文字的行數
background/bg:設定背景顏色
foreground/fg:設定文字顏色
selectmode:設定選項為單選或多選,以及滑鼠拖曳時的選取方式
  BROWSE:單選,點擊後拖曳滑鼠最後放開的位置為選取項目,此為預設
  SINGLE:單選,點擊項目即為選取項目,不受滑鼠拖曳影響
  MULTIPLE:多選,點擊項目即為選取項目,再點一下可取消選取
  EXTENDED:多選,點擊後拖曳滑鼠到最後放開的區間為選取項目,或點選
         第一個項目後,按下shift點選第二個項目,兩者的區間即為選
         取項目
以下範例建立一個包含四個選項的列表選單,並設定選取方式為可拖曳多選。
listbox = Listbox(width=15, height=4, selectmode=EXTENDED)
options = ["Option1", "Option2", "Option3", "Option4"]
for item in options:
  listbox.insert(options.index(item), item)
listbox.pack()
列表選單

功能表(Menu)

功能表一般位於視窗的最上層,屬於一種下拉式的表單,由Menu()方法來建立物件,第一個參數為要包含這個列表選單的視窗物件,也可省略不寫,其它常用的參數如下:
font:設定功能表項目的字型與大小
activebackground:設定功能表項目被選取時的背景顏色
activeforeground:設定功能表項目被選取時的文字顏色
activeborderwidth:設定功能表項目被選取時的背景寬度
borderwidth:設定功能表項目的表單寬度
background/bg:設定功能表項目未被選取時的背景顏色
foreground/fg:設定功能表項目未被選取時的文字顏色
image:設定功能表項目的圖示
tearoff:設定功能表上方的虛線分隔線,預設有分隔線,tearoff為1,此時按一下分隔線可將功能表分離;tearoff設為0則無分隔線
搭配常用的方法如下:
add_cascade():建立階層式的功能表
add_command():增加功能表的項目
add_separator():增加功能表項目間的分隔線
以下範例程式建立一個含有兩個子功能表的功能表選單,在第一個子功能表menu1中,增加兩個功能表項目,項目之間有分隔線,並且可按功能表上方的虛線將功能表分離。
menu = Menu()
window.config(menu=menu)

submenu1 = Menu(activebackground="green", activeborderwidth=10, borderwidth=20)
menu.add_cascade(label="menu1", menu=submenu1)

submenu1.add_command(label="label1")
submenu1.add_separator()
submenu1.add_command(label="label2")

submenu2 = Menu()
menu.add_cascade(label="menu2", menu=submenu2)
執行結果:
功能表
功能表分離

總結

這篇文章中列出了一些使用tkinter建立GUI程式時常見的視窗元件,以及基本的參數用法,很多沒有提到的參數,使用時都可以到開頭提到的網站上查詢,全部寫出來就顯得冗長了。然而可以注意到,到目前為止所建立的元件都是由上而下依序排列下來,實際設計界面時,我們還要考量到元件所擺放的位置,這就留到下篇文章中再來說明。

程式範例

為什麼會看到廣告
46會員
36內容數
留言0
查看全部
發表第一個留言支持創作者!
Wei-Jie Weng的沙龍 的其他內容
在上一篇的文章中,我們提到關於開啟CSV文件的方式,從單純的當作純文字文件開啟,到引入Python內建的csv模組對CSV類型的檔案做基本的讀寫操作,在這篇文章中,我們將再進一步使用Pandas這套強大的工具,來簡化CSV文件處理的流程。
CSV全名為Comma-Separated Values,中文稱為逗號分隔值,也可稱為字元分隔值,因為分隔字元也可以不是逗號。它以純文字的形式儲存表格資料,同一列的資料以逗號或其它符號分隔成不同欄位,每一列的資料間以換行符號分隔。網路上很多資料的格式都是以CSV檔案呈現,例如交通部中央氣象局的觀測資
到目前為止,我們都還是在附檔名為.py的Python檔案執行程式碼,當程式需要與外部的檔案互動,例如讀取文字、表格、或是影像來做分析,或是把程式執行的結果儲存下來,就需要能夠存取外部的檔案。例如,在上一節中,當貪食蛇遊戲結束之後,隨著程式停止執行,該次的分數也就被丟棄,若能將分數記錄下來,下次遊戲開
接續前一篇文章,說明貪食蛇遊戲架構的後半部份,來完成整個遊戲的程式設計。
接下來我們會運用之前所學過的各種觀念,包括Turtle 模組、繼承等等,來製作這個大家都熟知的小遊戲-貪食蛇。完成品會長得像下圖這樣,包括可以設定難度(速度)、使用鍵盤的上、下、左、右鍵來移動貪食蛇的移動方向來吃到食物並加長蛇身、以及記分板等。
Turtle Graphic的前身是一種設計給小朋友學習的簡易繪圖程式,最初來自於Wally Feurzeig、Seymour Papert和Cynthia Solomon於1967年所創造的Logo編程語言,它是Python內建的繪圖函式庫,我們可以根據函式庫裡面的指令,操控一隻或多隻「小烏龜」在
在上一篇的文章中,我們提到關於開啟CSV文件的方式,從單純的當作純文字文件開啟,到引入Python內建的csv模組對CSV類型的檔案做基本的讀寫操作,在這篇文章中,我們將再進一步使用Pandas這套強大的工具,來簡化CSV文件處理的流程。
CSV全名為Comma-Separated Values,中文稱為逗號分隔值,也可稱為字元分隔值,因為分隔字元也可以不是逗號。它以純文字的形式儲存表格資料,同一列的資料以逗號或其它符號分隔成不同欄位,每一列的資料間以換行符號分隔。網路上很多資料的格式都是以CSV檔案呈現,例如交通部中央氣象局的觀測資
到目前為止,我們都還是在附檔名為.py的Python檔案執行程式碼,當程式需要與外部的檔案互動,例如讀取文字、表格、或是影像來做分析,或是把程式執行的結果儲存下來,就需要能夠存取外部的檔案。例如,在上一節中,當貪食蛇遊戲結束之後,隨著程式停止執行,該次的分數也就被丟棄,若能將分數記錄下來,下次遊戲開
接續前一篇文章,說明貪食蛇遊戲架構的後半部份,來完成整個遊戲的程式設計。
接下來我們會運用之前所學過的各種觀念,包括Turtle 模組、繼承等等,來製作這個大家都熟知的小遊戲-貪食蛇。完成品會長得像下圖這樣,包括可以設定難度(速度)、使用鍵盤的上、下、左、右鍵來移動貪食蛇的移動方向來吃到食物並加長蛇身、以及記分板等。
Turtle Graphic的前身是一種設計給小朋友學習的簡易繪圖程式,最初來自於Wally Feurzeig、Seymour Papert和Cynthia Solomon於1967年所創造的Logo編程語言,它是Python內建的繪圖函式庫,我們可以根據函式庫裡面的指令,操控一隻或多隻「小烏龜」在
你可能也想看
Google News 追蹤
Thumbnail
接下來第二部分我們持續討論美國總統大選如何佈局, 以及選前一週到年底的操作策略建議 分析兩位候選人政策利多/ 利空的板塊和股票
Thumbnail
🤔為什麼團長的能力是死亡筆記本? 🤔為什麼像是死亡筆記本呢? 🤨作者巧思-讓妮翁死亡合理的幾個伏筆
Thumbnail
十年前,我跟小悟參加了台南政府所舉辦的 24 小時不間斷進食比賽,當年,我以兩分之差,沒擠進金榜。在挑戰時間內,來不及吃狀元糕跟芋頭八寶冰,以致失掉食物王的頭銜。十年後,小悟說,要不要再來回味當年勇?我知道,年輕的我胃袋能撐船,可逝去的青春,最先反映在食慾上。我笑著說不了......
Thumbnail
持著前作的風格,《蜘蛛人:穿越新宇宙》在色彩或畫面的呈現上依舊大膽、前衛,並且嘗試藉由多重宇宙的「優勢」,在成長動畫的既有敘事上,嘗試更多不同的可能性。精彩的安排讓觀看這部作品讓成為十足的享受,並賦予了觀眾再次進場支持的充分動機。
Thumbnail
節目這邊聽 📎錄製時間: 2023.10 汪汪隊立大功! MRT(Mission Readiness Test)是難度最高的國際搜救犬認證! 但是我們有兩位英雄(一人一汪)順利通過這次的認證,拿到了優良的成績! 讓我們歡迎領犬員羅浩芳!就讓她帶著小編、江小姐還有各位聽眾,一起了解這次搜救犬
Thumbnail
這一陣子不少投資標的都在上漲 連成長性沒有科技股強的食品股也在慢慢上升中 今天記錄一下自己食品股大成的最新績效 身為雞肉雙雄的其中一雄 除了飼料、雞肉、雞蛋以外,還跨足了沙拉油、豬肉、海鮮、餐廳等事業體 幾乎可說是一檔食品ETF(還不用付管理費),進去它的官網就可以看到許多不同的事業體
Thumbnail
你現在也許單一個句子可以說得溜,但持續說話覺得尷尬的原因在於:缺乏連接詞。好的連接詞會讓口語表達更精確有力,讓韓國人聽到你說的話不需要猜測意思或重新思考,表達能力也就跟著上升!
Thumbnail
喜歡音樂的羅小咩,不定期將在周六中午12:00與你聊聊戲劇中的音樂,還有音樂中的戲劇。本次介紹:惡作劇之吻、慶餘年、Good Casting、溫柔時光、鋼之鍊金術師這五個戲劇作品的主題曲。
Thumbnail
喜歡音樂的羅小咩,不定期將在周六中午12:00與你聊聊戲劇中的音樂,還有音樂中的戲劇。本次介紹:必娶女人、kill me heal me、錦衣之下、101次求婚、灌籃高手這五個戲劇作品的主題曲。
近期許多地方受到洪水以及颱風的侵襲 但包你發的線路及網路穩定度完全不受到影響 大家可以放心,我們是24小時全年無休不間斷 就算你半夜三點要玩包你發也完全OK 不會像有些系統只在白天開放 而同樣的穩定度也是一大考驗 在這種供電不穩定的情況 也有可能導致其他遊戲斷線 但包你發機房穩定、有備用電源 完全不
Thumbnail
疫情長時間帶來了減少開放性的社交機會,最直接的影響莫過於情緒勒索造成的封閉性失調,感謝國立台灣戲曲學院傑出校友,為身心障礙的大寶寶們帶來一系列精彩的演出,透過精心策劃的舞蹈及雜耍表演,帶給住民體驗了跟以往不同的志工服務內容,見到了久違住民
Thumbnail
每一次TDCA的關愛陪伴,都讓受贈的服務對象感受到滿滿的幸福感,一起協力完成手作皮革鑰匙圈,每一針每一線都在訓練孩子們手眼協調的肢體,透過不同花色皮革來搭配,不僅訓練孩子對色彩的辨識能力,同時也在培養眼界裡的審美觀噢!
Thumbnail
接下來第二部分我們持續討論美國總統大選如何佈局, 以及選前一週到年底的操作策略建議 分析兩位候選人政策利多/ 利空的板塊和股票
Thumbnail
🤔為什麼團長的能力是死亡筆記本? 🤔為什麼像是死亡筆記本呢? 🤨作者巧思-讓妮翁死亡合理的幾個伏筆
Thumbnail
十年前,我跟小悟參加了台南政府所舉辦的 24 小時不間斷進食比賽,當年,我以兩分之差,沒擠進金榜。在挑戰時間內,來不及吃狀元糕跟芋頭八寶冰,以致失掉食物王的頭銜。十年後,小悟說,要不要再來回味當年勇?我知道,年輕的我胃袋能撐船,可逝去的青春,最先反映在食慾上。我笑著說不了......
Thumbnail
持著前作的風格,《蜘蛛人:穿越新宇宙》在色彩或畫面的呈現上依舊大膽、前衛,並且嘗試藉由多重宇宙的「優勢」,在成長動畫的既有敘事上,嘗試更多不同的可能性。精彩的安排讓觀看這部作品讓成為十足的享受,並賦予了觀眾再次進場支持的充分動機。
Thumbnail
節目這邊聽 📎錄製時間: 2023.10 汪汪隊立大功! MRT(Mission Readiness Test)是難度最高的國際搜救犬認證! 但是我們有兩位英雄(一人一汪)順利通過這次的認證,拿到了優良的成績! 讓我們歡迎領犬員羅浩芳!就讓她帶著小編、江小姐還有各位聽眾,一起了解這次搜救犬
Thumbnail
這一陣子不少投資標的都在上漲 連成長性沒有科技股強的食品股也在慢慢上升中 今天記錄一下自己食品股大成的最新績效 身為雞肉雙雄的其中一雄 除了飼料、雞肉、雞蛋以外,還跨足了沙拉油、豬肉、海鮮、餐廳等事業體 幾乎可說是一檔食品ETF(還不用付管理費),進去它的官網就可以看到許多不同的事業體
Thumbnail
你現在也許單一個句子可以說得溜,但持續說話覺得尷尬的原因在於:缺乏連接詞。好的連接詞會讓口語表達更精確有力,讓韓國人聽到你說的話不需要猜測意思或重新思考,表達能力也就跟著上升!
Thumbnail
喜歡音樂的羅小咩,不定期將在周六中午12:00與你聊聊戲劇中的音樂,還有音樂中的戲劇。本次介紹:惡作劇之吻、慶餘年、Good Casting、溫柔時光、鋼之鍊金術師這五個戲劇作品的主題曲。
Thumbnail
喜歡音樂的羅小咩,不定期將在周六中午12:00與你聊聊戲劇中的音樂,還有音樂中的戲劇。本次介紹:必娶女人、kill me heal me、錦衣之下、101次求婚、灌籃高手這五個戲劇作品的主題曲。
近期許多地方受到洪水以及颱風的侵襲 但包你發的線路及網路穩定度完全不受到影響 大家可以放心,我們是24小時全年無休不間斷 就算你半夜三點要玩包你發也完全OK 不會像有些系統只在白天開放 而同樣的穩定度也是一大考驗 在這種供電不穩定的情況 也有可能導致其他遊戲斷線 但包你發機房穩定、有備用電源 完全不
Thumbnail
疫情長時間帶來了減少開放性的社交機會,最直接的影響莫過於情緒勒索造成的封閉性失調,感謝國立台灣戲曲學院傑出校友,為身心障礙的大寶寶們帶來一系列精彩的演出,透過精心策劃的舞蹈及雜耍表演,帶給住民體驗了跟以往不同的志工服務內容,見到了久違住民
Thumbnail
每一次TDCA的關愛陪伴,都讓受贈的服務對象感受到滿滿的幸福感,一起協力完成手作皮革鑰匙圈,每一針每一線都在訓練孩子們手眼協調的肢體,透過不同花色皮革來搭配,不僅訓練孩子對色彩的辨識能力,同時也在培養眼界裡的審美觀噢!