轉載自 http://www.uml.org.cn/python/201912161.asp?utm_source=tuicool&utm_medium=referral
Python tkinter Canvas畫布完全攻略
Tkinter 提供了 Canvas 組件來實現繪圖。程序既可在 Canvas 中繪制直線、矩形、橢圓等各種幾何圖形,也可繪制圖片、文字、UI 組件(如 Button)等。Canvas 允許重新改變這些圖形項(Tkinter 將程序繪制的所有東西統稱為 item)的屬性,比如改變其坐標、外觀等。
Canvas 組件的用法與其他 GUI 組件一樣簡單,程序只要創建並添加 Canvas 組件,然后調用該組件的方法來繪制圖形即可。如下程序示范了最簡單的 Canvas 繪圖:
from tkinter import * # 創建窗口 root = Tk() # 創建並添加Canvas cv = Canvas(root, background='white') cv.pack(fill=BOTH, expand=YES) cv.create_rectangle(30, 30, 200, 200, outline='red', # 邊框顏色 stipple = 'question', # 填充的位圖 fill="red", # 填充顏色 width=5 # 邊框寬度 ) cv.create_oval(240, 30, 330, 200, outline='yellow', # 邊框顏色 fill='pink', # 填充顏色 width=4 # 邊框寬度 ) root.mainloop() |
上面程序先創建並添加了 Canvas 組件,分別繪制了矩形和橢圓。運行上面程序,可以看到如圖 1 所示的效果。
圖 1 最簡單的 Canvas 繪圖
從上面程序可以看到,Canvas 提供了 create_rectangle() 方法繪制矩形和 create_oval() 方法繪制橢圓(包括圓,圓是橢圓的特例)。實際上,Canvas 還提供了如下方法來繪制各種圖形:
create_arc:繪制弧。
create_bitmap:繪制位圖。
create_image:繪制圖片。
create_line():繪制直線。
create_polygon:繪制多邊形。
create_text:繪制文字。
create_window:繪制組件。
Canvas 的坐標系統是繪圖的基礎,其中點 (0,0) 位於 Canvas 組件的左上角,X 軸水平向右延伸,Y 軸垂直向下延伸。
繪制上面這些圖形時需要簡單的幾何基礎:
在使用 create_line() 繪制直線時,需要指定兩個點的坐標,分別作為直線的起點和終點。
在使用 create_rectangle() 繪制矩形時,需要指定兩個點的坐標,分別作為矩形左上角點和右下角點的坐標。
在使用 create_oval() 繪制橢圓時,需要指定兩個點的坐標,分別作為左上角點和右下角點的坐標來確定一個矩形,而該方法則負責繪制該矩形的內切橢圓,如圖 2 所示。
圖 2 內切橢圓
從圖 2 可以看出,只要矩形確定下來,該矩形的內切橢圓就能確定下來,而 create_oval() 方法所需要的兩個坐標正是用於指定該矩形的左上角點和右下角點的坐標。
在使用 create_arc 繪制弧時,和 create_oval 的用法相似,因為弧是橢圓的一部分,因此同樣也是指定左上角和右下角兩個點的坐標,默認總是繪制從 3 點(0)開始,逆時針旋轉 90° 的那一段弧。程序可通過 start 改變起始角度,也可通過 extent 改變轉過的角度。
在使用 create_polygon 繪制多邊形時,需要指定多個點的坐標來作為多邊形的多個定點;在使用 create_bitmap、create_image、create_text、create_window 等方法時,只要指定一個坐標點,用於指定目標元素的繪制位置即可。
在繪制這些圖形時可指定如下選項:
fill:指定填充顏色。如果不指定該選項,默認不填充。
outline:指定邊框顏色。
width:指定邊框寬度。如果不指定該選項,邊框寬度默認為 1。
dash:指定邊框使用虛線。該屬性值既可為單獨的整數,用於指定虛線中線段的長度;也可為形如(5,2,3)格式的元素,此時5 指定虛線中線段的長度,2 指定間隔長度,3 指定虛線長度……依此類推。
stipple:使用位圖平鋪進行填充。該選項可與 fill 選項結合使用,fill 選項用於指定位圖的顏色。
style:指定繪制弧的樣式。該選項僅對 create_arc 方法起作用。該選項支持 PIESLICE(扇形)、CHORD(弓形)、ARC(僅繪制弧)選項值。
start:指定繪制弧的起始角度。該選項僅對 create_arc 方法起作用。
extent:指定繪制弧的角度。該選項僅對 create_arc 方法起作用。
arrow:指定繪制直線時兩端是否有箭頭。該選項支持 NONE(兩端無箭頭)、FIRST(開始端有箭頭)、LAST(結束端有箭頭)、BOTH(兩端都有箭頭)選項值。
arrowshape:指定箭頭形狀。該選項是一個形如 "20 20 10" 的字符串,字符串中的三個整數依次指定填充長度、箭頭長度、箭頭寬度。
joinstyle:指定直接連接點的風格。僅對繪制直線和多向形有效。該選項支持 METTER、ROUND、BEVEL 選項值。
anchor:指定繪制文字、GUI 組件的位置。該選項僅對 create_text()、create_window() 方法有效。
justify:指定文字的對齊方式。該選項支持 CENTER、LEFT、RIGHT 常量值,該選項僅對 create_text 方法有效。
下面程序示范了通過不同的方法來繪制不同的圖形,這些圖形分別使用不同的邊框、不同的填充效果:
from tkinter import * # 創建窗口 root = Tk() root.title('繪制圖形項') # 創建並添加Canvas cv = Canvas(root, background='white', width=830, height=830) cv.pack(fill=BOTH, expand=YES) columnFont = ('微軟雅黑', 18) titleFont = ('微軟雅黑', 20, 'bold') # 使用循環繪制文字 for i, st in enumerate(['默認', '指定邊寬', '指定填充', '邊框顏色', '位圖填充']): cv.create_text((130 + i * 140, 20),text = st, font = columnFont, fill='gray', anchor = W, justify = LEFT) # 繪制文字 cv.create_text(10, 60, text = '繪制矩形', font = titleFont, fill='magenta', anchor = W, justify = LEFT) # 定義列表,每個元素的4個值分別指定邊框寬度、填充色、邊框顏色、位圖填充 options = [(None, None, None, None), (4, None, None, None), (4, 'pink', None, None), (4, 'pink', 'blue', None), (4, 'pink', 'blue', 'error')] # 采用循環繪制5個矩形 for i, op in enumerate(options): cv.create_rectangle(130 + i * 140, 50, 240 + i * 140, 120, width = op[0], # 邊框寬度 fill = op[1], # 填充顏色 outline = op[2], # 邊框顏色 stipple = op[3]) # 使用位圖填充 # 繪制文字 cv.create_text(10, 160, text = '繪制橢圓', font = titleFont, fill='magenta', anchor = W, justify = LEFT) # 定義列表,每個元素的4個值分別指定邊框寬度、填充色、邊框顏色、位圖填充 options = [(None, None, None, None), (4, None, None, None), (4, 'pink', None, None), (4, 'pink', 'blue', None), (4, 'pink', 'blue', 'error')] # 采用循環繪制5個橢圓 for i, op in enumerate(options): cv.create_oval(130 + i * 140, 150, 240 + i * 140, 220, width = op[0], # 邊框寬度 fill = op[1], # 填充顏色 outline = op[2], # 邊框顏色 stipple = op[3]) # 使用位圖填充 # 繪制文字 cv.create_text(10, 260, text = '繪制多邊形', font = titleFont, fill='magenta', anchor = W, justify = LEFT) # 定義列表,每個元素的4個值分別指定邊框寬度、填充色、邊框顏色、位圖填充 options = [(None, "", 'black', None), (4, "", 'black', None), (4, 'pink', 'black', None), (4, 'pink', 'blue', None), (4, 'pink', 'blue', 'error')] # 采用循環繪制5個多邊形 for i, op in enumerate(options): cv.create_polygon(130 + i * 140, 320, 185 + i * 140, 250, 240 + i * 140, 320, width = op[0], # 邊框寬度 fill = op[1], # 填充顏色 outline = op[2], # 邊框顏色 stipple = op[3]) # 使用位圖填充 # 繪制文字 cv.create_text(10, 360, text = '繪制扇形', font = titleFont, fill='magenta', anchor = W, justify = LEFT) # 定義列表,每個元素的4個值分別指定邊框寬度、填充色、邊框顏色、位圖填充 options = [(None, None, None, None), (4, None, None, None), (4, 'pink', None, None), (4, 'pink', 'blue', None), (4, 'pink', 'blue', 'error')] # 采用循環繪制5個扇形 for i, op in enumerate(options): cv.create_arc(130 + i * 140, 350, 240 + i * 140, 420, width = op[0], # 邊框寬度 fill = op[1], # 填充顏色 outline = op[2], # 邊框顏色 stipple = op[3]) # 使用位圖填充 # 繪制文字 cv.create_text(10, 460, text = '繪制弓形', font = titleFont, fill='magenta', anchor = W, justify = LEFT) # 定義列表,每個元素的4個值分別指定邊框寬度、填充色、邊框顏色、位圖填充 options = [(None, None, None, None), (4, None, None, None), (4, 'pink', None, None), (4, 'pink', 'blue', None), (4, 'pink', 'blue', 'error')] # 采用循環繪制5個弓形 for i, op in enumerate(options): cv.create_arc(130 + i * 140, 450, 240 + i * 140, 520, width = op[0], # 邊框寬度 fill = op[1], # 填充顏色 outline = op[2], # 邊框顏色 stipple = op[3], # 使用位圖填充 start = 30, # 指定起始角度 extent = 60, # 指定逆時針轉過角度 style = CHORD) # CHORD指定繪制弓 # 繪制文字 cv.create_text(10, 560, text = '僅繪弧', font = titleFont, fill='magenta', anchor = W, justify = LEFT) # 定義列表,每個元素的4個值分別指定邊框寬度、填充色、邊框顏色、位圖填充 options = [(None, None, None, None), (4, None, None, None), (4, 'pink', None, None), (4, 'pink', 'blue', None), (4, 'pink', 'blue', 'error')] # 采用循環繪制5個弧 for i, op in enumerate(options): cv.create_arc(130 + i * 140, 550, 240 + i * 140, 620, width = op[0], # 邊框寬度 fill = op[1], # 填充顏色 outline = op[2], # 邊框顏色 stipple = op[3], # 使用位圖填充 start = 30, # 指定起始角度 extent = 60, # 指定逆時針轉過角度 style = ARC) # ARC指定僅繪制弧 # 繪制文字 cv.create_text(10, 660, text = '繪制直線', font = titleFont, fill='magenta', anchor = W, justify = LEFT) # 定義列表,每個元素的5個值分別指定邊框寬度、線條顏色、位圖填充、箭頭風格, 箭頭形狀 options = [(None, None, None, None, None), (6, None, None, BOTH, (20, 40, 10)), (6, 'pink', None, FIRST, (40, 40, 10)), (6, 'pink', None, LAST, (60, 50, 10)), (8, 'pink', 'error', None, None)] # 采用循環繪制5個弧 for i, op in enumerate(options): cv.create_line(130 + i * 140, 650, 240 + i * 140, 720, width = op[0], # 邊框寬度 fill = op[1], # 填充顏色 stipple = op[2], # 使用位圖填充 arrow = op[3], # 箭頭風格 arrowshape = op[4]) # 箭頭形狀 # 繪制文字 cv.create_text(10, 760, text = '繪制位圖\n圖片、組件', font = titleFont, fill='magenta', anchor = W, justify = LEFT) # 定義包括create_bitmap, create_image, create_window三個方法的數組 funcs = [Canvas.create_bitmap, Canvas.create_image, Canvas.create_window] # 為上面3個方法定義選項 items = [{'bitmap' : 'questhead'}, {'image':PhotoImage(file='images/fklogo.gif')}, {'window':Button(cv,text = '單擊我', padx=10, pady=5, command = lambda :print('按鈕單擊')),'anchor': W}] for i, func in enumerate(funcs): func(cv, 230 + i * 140, 780, **items[i]) root.mainloop() |
上面程序示范了 Canvas 中不同的 create_xxx 方法的功能和用法,它們可用於創建矩形、橢圓、多邊形、扇形、弓形、弧、直線、位圖、圖片和組件等。在繪制不同的圖形時可指定不同的選項,從而實現豐富的繪制效果。
運行上面程序,可以看到如圖 3 所示的效果。
圖 3 使用Canvas 繪制圖形
掌握了上面的繪制方法之后,實際上已經可以實現一些簡單的游戲了。比如前面介紹的控制台五子棋,之前程序是在控制台打印游戲狀態的,實際上程序完全可以在界面上繪制游戲狀態,這樣就能看到圖形界面的五子棋了。
此外,該五子棋還需要根據用戶的鼠標動作來確定下棋坐標,因此程序會為游戲界面的 <Button-1>(左鍵單擊)、<Motion>(鼠標移動)、<Leave>(鼠標移出)事件綁定事件處理函數。下面程序示范了實現圖形界面的五子棋:
from tkinter import * import random BOARD_WIDTH = 535 BOARD_HEIGHT = 536 BOARD_SIZE = 15 # 定義棋盤坐標的像素值和棋盤數組之間的偏移距。 X_OFFSET = 21 Y_OFFSET = 23 # 定義棋盤坐標的像素值和棋盤數組之間的比率。 X_RATE = (BOARD_WIDTH - X_OFFSET * 2) / (BOARD_SIZE - 1) Y_RATE = (BOARD_HEIGHT - Y_OFFSET * 2) / (BOARD_SIZE - 1) BLACK_CHESS = "●" WHITE_CHESS = "○" board = [] # 把每個元素賦為"╋",代表無棋 for i in range(BOARD_SIZE) : row = ["╋"] * BOARD_SIZE board.append(row) # 創建窗口 root = Tk() # 禁止改變窗口大小 root.resizable(width=False, height=False) # 修改圖標 root.iconbitmap('images/fklogo.ico') # 設置窗口標題 root.title('五子棋') # 創建並添加Canvas cv = Canvas(root, background='white', width=BOARD_WIDTH, height=BOARD_HEIGHT) cv.pack() bm = PhotoImage(file="images/board.png") cv.create_image(BOARD_HEIGHT/2 + 1, BOARD_HEIGHT/2 + 1, image=bm) selectedbm = PhotoImage(file="images/selected.gif") # 創建選中框圖片,但該圖片默認不在棋盤中 selected = cv.create_image(-100, -100, image=selectedbm) def move_handler(event): # 計算用戶當前的選中點,並保證該選中點在0~14之間 selectedX = max(0, min(round((event.x - X_OFFSET) / X_RATE), 14)) selectedY = max(0, min(round((event.y - Y_OFFSET) / Y_RATE), 14)) # 移動紅色選擇框 cv.coords(selected,(selectedX * X_RATE + X_OFFSET, selectedY * Y_RATE + Y_OFFSET)) black = PhotoImage(file="images/black.gif") white = PhotoImage(file="images/white.gif") def click_handler(event): # 計算用戶的下棋點,並保證該下棋點在0~14之間 userX = max(0, min(round((event.x - X_OFFSET) / X_RATE), 14)) userY = max(0, min(round((event.y - Y_OFFSET) / Y_RATE), 14)) # 當下棋點沒有棋子時,才能下棋子,用戶才能下棋子 if board[userY][userX] == "╋": cv.create_image(userX * X_RATE + X_OFFSET, userY * Y_RATE + Y_OFFSET, image=black) board[userY][userX] = "●" while(True): comX = random.randint(0, BOARD_SIZE - 1) comY = random.randint(0, BOARD_SIZE - 1) # 如果電腦要下棋的點沒有棋子時,才能讓電腦下棋 if board[comY][comX] == "╋": break cv.create_image(comX * X_RATE + X_OFFSET, comY * Y_RATE + Y_OFFSET, image=white) board[comY][comX] = "○" def leave_handler(event): # 將紅色選中框移出界面 cv.coords(selected, -100, -100) # 為鼠標移動事件綁定事件處理函數 cv.bind('<Motion>', move_handler) # 為鼠標點擊事件綁定事件處理函數 cv.bind('<Button-1>', click_handler) # 為鼠標移出事件綁定事件處理函數 cv.bind('<Leave>', leave_handler) root.mainloop() |
上面程序先繪制了五子棋的棋盤,該棋盤就是一張預先准備好的圖片,然后繪制選擇框,當用戶鼠標在棋盤上移動時,該選擇框顯示用戶鼠標當前停留在哪個下棋點上。
隨后程序調用了 Canvas 的 coords() 方法,該方法負責重設選擇框的坐標。這是 Tkinter 繪圖的特別之處,繪制好的每一個圖形項都不是固定的,程序后面完全可以修改它們。因此,程序將會控制選擇框圖片隨着用戶鼠標的移動而改變位置。
程序根據用戶鼠標單擊來繪制黑色棋子(也就是下黑棋)和白色棋子(也就是下白棋)。在繪制黑色棋子和白色棋子的同時,也改變了底層代表棋盤狀態的 board 列表的數據,這樣即可記錄下棋狀態,從而讓程序在后面可以根據 board[] 列表來判斷勝負(本來這個功能在 Charlie 的程序中是有的,此處為了突出繪圖的主題,作者刪除了這部分)。另外,也可以加入人工智能,根據 board[] 列表來決定電腦的下棋點。
運行該程序,可以看到如圖 4 所示的效果:
圖 4 五子棋
在上面這個程序中,電腦下棋采用的方式是隨機下棋,因此下得比較“凌亂”。如果要讓電腦下棋更加智能,則可通過簡單的人工智能來實現,本教程暫不涉及。
Canvas操作圖形項的標簽
在 Canvas 中通過 create_xxx 方法繪制圖形項之后,這些圖形項井不是完全靜態的圖形,每個圖形項都是一個獨立的對象,程序完全可以動態地修改、刪除這些圖形項。
Canvas 以“堆疊”的形式來管理這些圖形項,先繪制的圖形項位於“堆疊”的下面,后繪制的圖形項位於“堆疊”的上面。因此,如果兩個圖形項有重疊的部分,那么后繪制的圖形項(位於上面)會遮擋先繪制的圖形項。
為了修改、刪除這些圖形項,程序需要先獲得這些圖形項的引用。獲得這些圖形項的引用有兩種方式:
通過圖形項的 id,也就是 Canvas 執行 create_xxx() 方法的返回值。一般來說,create_xxx() 會依次返回 1、2、3 等整數作為圖形項的 id。
通過圖形項的 tag(標簽)。
在 Canvas 中調用 create_xxx() 方法繪圖時,還可傳入一個 tags 選項,該選項可以為所繪制的圖形項(比如矩形、橢圓、多邊形等)添加一個或多個 tag(標簽)。此外,Canvas 還允許調用方法為圖形項添加 tag、刪除 tag 等,這些 tag 也相當於該圖形項的標識,程序完全可以根據 tag 來獲取圖形項。
總結來說,Canvas 提供了如下方法來為圖形項添加 tag:
addtag_aboove(self, newtag, tagOrId):為 tagOrId 對應圖形項的上一個圖形項添加新 tag。
addtag_all(self, newtag):為所有圖形項添加新 tag。
addtag_below(self, newtag, tagOrId):為 tagOrId 對應圖形項的下一個圖形項添加新 tag。
addtag_closest(self, newtag, x, y):為和 x、y 點最接近的圖形項添加新 tag。
addtag_enclosed(self, newtag, x1, y1, x2, y2):為指定矩形區域內最上面的圖形項添加新tag。其中 x1、y1 確定矩形區域的左上角坐標;x2、y2 確定矩形區域的右下角坐標。
addtag_overlapping(self, newtag, x1, y1, x2, y2):為與指定矩形區域重疊的最上面的圖形項添加tag。
addtag_withtag(self, newtag, tagOrId):為 tagOrId 對應圖形項添加新 tag。
Canvas 提供了如下方法來刪除圖形項的tag:
dtag(self, *args):刪除指定圖形項的tag。
Canvas 提供了如下方法來獲取圖形項的所有tag:
gettags(self, *args):獲取指定圖形項的所有tag。
Canvas 提供了如下方法根據 tag 來獲取其對應的所有圖形項:
find_withtag(self, tagOrId):獲取tagOrId 對應的所有圖形項。
為了更好地理解上面方法的作用,下面用一個程序來進行演示:
from tkinter import * # 創建窗口 root = Tk() root.title('操作標簽') # 創建並添加Canvas cv = Canvas(root, background='white', width=620, height=250) cv.pack(fill=BOTH, expand=YES) # 繪制一個矩形框 rt = cv.create_rectangle(40, 40, 300, 220, outline='blue', width=2, tag = ('t1', 't2', 't3', 'tag4')) # 為該圖形項指定標簽 # 訪問圖形項的id,也就是編號 print(rt) # 1 # 繪制一個橢圓 oval = cv.create_oval(350, 50, 580, 200, fill='yellow', width=0, tag = ('g1', 'g2', 'g3', 'tag4')) # 為該圖形項指定標簽 # 訪問圖形項的id,也就是編號 print(oval) # 2 # 根據指定tag該tag對應的所有圖形項 print(cv.find_withtag('tag4')) # (1, 2) # 獲取指定圖形項的所有tag print(cv.gettags(rt)) # ('t1', 't2', 't3', 'tag4') print(cv.gettags(2)) # ('g1', 'g2', 'g3', 'tag4') cv.dtag(1, 't1') # 刪除id為1的圖形項上名為t1的tag cv.dtag(oval, 'g1') # 刪除id為oval的圖形項上名為g1的tag # 獲取指定圖形項的所有tag print(cv.gettags(rt)) # ('tag4', 't2', 't3') print(cv.gettags(2)) # ('tag4', 'g2', 'g3') # 為所有圖形項添加tag cv.addtag_all('t5') print(cv.gettags(1)) # ('tag4', 't2', 't3', 't5') print(cv.gettags(oval)) # ('tag4', 'g2', 'g3', 't5') # 為指定圖形項添加tag cv.addtag_withtag('t6', 'g2') # 獲取指定圖形項的所有tag print(cv.gettags(1)) # ('tag4', 't2', 't3', 't5') print(cv.gettags(oval)) # ('tag4', 'g2', 'g3', 't5', 't6') # 為指定圖形項上面的圖形項添加tag, t2上面的就是oval圖形項 cv.addtag_above('t7', 't2') print(cv.gettags(1)) # ('tag4', 't2', 't3', 't5') print(cv.gettags(oval)) # ('tag4', 'g2', 'g3', 't5', 't6', 't7') # 為指定圖形項下面的圖形項添加tag, g2下面的就是rt圖形項 cv.addtag_below('t8', 'g2') print(cv.gettags(1)) # ('tag4', 't2', 't3', 't5', 't8') print(cv.gettags(oval)) # ('tag4', 'g2', 'g3', 't5', 't6', 't7') # 為最接近指定點的圖形項添加tag,最接近360、90的圖形項是oval cv.addtag_closest('t9', 360, 90) print(cv.gettags(1)) # ('tag4', 't2', 't3', 't5', 't8') print(cv.gettags(oval)) # ('tag4', 'g2', 'g3', 't5', 't6', 't7', 't9') # 為位於指定區域內(幾乎覆蓋整個圖形區)的最上面的圖形項添加tag cv.addtag_closest('t10', 30, 30, 600, 240) print(cv.gettags(1)) # ('tag4', 't2', 't3', 't5', 't8') print(cv.gettags(oval)) # ('tag4', 'g2', 'g3', 't5', 't6', 't7', 't9', 't10') # 為與指定區域內重合的最上面的圖形項添加tag cv.addtag_closest('t11', 250, 30, 400, 240) print(cv.gettags(1)) # ('tag4', 't2', 't3', 't5', 't8') print(cv.gettags(oval)) # ('tag4', 'g2', 'g3', 't5', 't6', 't7', 't9', 't10', 't11') root.mainloop() |
上面程序示范了操作圖形項的 tag 的方法,而且列出了每次操作之后的輸出結果。因此,讀者可以結合程序的運行結果來理解 Canvas 是如何管理圖形項的 tag 的。
操作圖形項
在 Canvas 中獲取圖形項之后,接下來可通過 Canvas 提供的大量方法來操作圖形項。總結起來,Canvas 提供了如下方法在圖形項“堆疊”中查找圖形項:
find_above(self, tagOrId):返回 tagOrId 對應圖形項的上一個圖形項。
find_all(self):返回全部圖形項。
find_below(self, tagOrId):返回 tagOrId 對應圖形項的下一個圖形項。
find_closest(self, x, y):返回和 x 、y 點最接近的圖形項。
find_enclosed(self, x1, y1, x2, y2):返回位於指定矩形區域內最上面的圖形項。
find_overlapping(self, x1, y1, x2, y2):返回與指定矩形區域重疊的最上面的圖形項。
find_withtag(self, tagOrId):返回 tagOrId 對應的全部圖形項。
Canvas 提供了如下方法在圖形項“堆疊”中移動圖形項:
tag_lower(self, *args)|lower:將args的第一個參數對應的圖形項移到“堆疊”的最下面。也可額外指定一個參數,代表移動到指定圖形項的下面。
tag_raise(self, *args)|lift:將args的第一個參數對應的圖形項移到“堆疊”的最上面。也可額外指定一個參數,代表移動到指定圖形項的上面。
如果程序希望獲取或修改圖形項的選項,則可通過 Canvas 的如下方法來操作:
itemcget(self, tagOrId, option):獲取tagOrId 對應圖形項的option 選項值。
itemconfig(self, tagOrId, cnf=None, **kw):為tagOrId 對應圖形項配置選項。
itemconfigure:該方法與上一個方法完全相同。
Canvas 提供了如下方法來改變圖形項的大小和位置:
coords(self, *args):重設圖形項的大小和位置。
move(self, *args):移動圖形項,但不能改變大小。簡單來說,就是在圖形項的 x、y 基礎上加上新的 mx、my 參數。
scale(self, *args):縮放圖形項。該方法的 args 參數要傳入 4 個值,其中前兩個值指定縮放中心;后兩個值指定 x、y 方向的縮放比。
此外,Canvas 還提供了如下方法來刪除圖形項或文字圖形項(由 create_text 方法創建)中間的部分文字:
delete(self, *args):刪除指定 id 或 tag 對應的全部圖形項。
dchars(self, *args):刪除文字圖形項中間的部分文字。
下面程序示范了操作圖形項的方法:
from tkinter import * from tkinter import colorchooser import threading # 創建窗口 root = Tk() root.title('操作圖形項') # 創建並添加Canvas cv = Canvas(root, background='white', width=400, height=350) cv.pack(fill=BOTH, expand=YES) # 該變量用於保存當前選中的圖形項 current = None # 該變量用於保存當前選中的圖形項的邊框顏色 current_outline = None # 該變量用於保存當前選中的圖形項的邊框寬度 current_width = None # 該函數用於高亮顯示選中圖形項(邊框顏色會red、yellow之間切換) def show_current(): # 如果當前選中項不為None if current is not None: # 如果當前選中圖形項的邊框色為red,將它改為yellow if cv.itemcget(current, 'outline') == 'red': cv.itemconfig(current, width=2, outline='yellow') # 否則,將顏色改為red else: cv.itemconfig(current, width=2, outline='red') global t # 通過定時器指定0.2秒之后執行show_current函數 t = threading.Timer(0.2, show_current) t.start() # 通過定時器指定0.2秒之后執行show_current函數 t = threading.Timer(0.2, show_current) t.start() # 分別創建矩形、橢圓、和圓 rect = cv.create_rectangle(30, 30, 250, 200, fill='magenta', width='0') oval = cv.create_oval(180, 50, 380, 180, fill='yellow', width='0') circle = cv.create_oval(120, 150, 300, 330, fill='pink', width='0') bottomF = Frame(root) bottomF.pack(fill=X,expand=True) liftbn = Button(bottomF, text='向上', # 將橢圓移動到它上面的item之上 command=lambda : cv.tag_raise(oval, cv.find_above(oval))) liftbn.pack(side=LEFT, ipadx=10, ipady=5, padx=3) lowerbn = Button(bottomF, text='向下', # 將橢圓移動到它下面的item之下 command=lambda : cv.tag_lower(oval, cv.find_below(oval))) lowerbn.pack(side=LEFT, ipadx=10, ipady=5, padx=3) def change_fill(): # 彈出顏色選擇框,讓用戶選擇顏色 fill_color = colorchooser.askcolor(parent=root, title='選擇填充顏色', # 初始顏色設置為橢圓當前的填充色(fill選項值) color = cv.itemcget(oval, 'fill')) if fill_color is not None: cv.itemconfig(oval, fill=fill_color[1]) fillbn = Button(bottomF, text='改變填充色', # 該按鈕觸發change_fill函數 command=change_fill) fillbn.pack(side=LEFT, ipadx=10, ipady=5, padx=3) def change_outline(): # 彈出顏色選擇框,讓用戶選擇顏色 outline_color = colorchooser.askcolor(parent=root, title='選擇邊框顏色', # 初始顏色設置為橢圓當前的邊框色(outline選項值) color = cv.itemcget(oval, 'outline')) if outline_color is not None: cv.itemconfig(oval, outline=outline_color[1], width=4) outlinebn = Button(bottomF, text='改變邊框色', # 該按鈕觸發change_outline函數 command=change_outline) outlinebn.pack(side=LEFT, ipadx=10, ipady=5, padx=3) movebn = Button(bottomF, text='右下移動', # 調用move方法移動圖形項 command=lambda : cv.move(oval, 15, 10)) movebn.pack(side=LEFT, ipadx=10, ipady=5, padx=3) coordsbn = Button(bottomF, text='位置復位', # 調用coords方法重設圖形項的大小和位置 command=lambda : cv.coords(oval, 180, 50, 380, 180)) coordsbn.pack(side=LEFT, ipadx=10, ipady=5, padx=3) # 再次添加Frame容器 bottomF = Frame(root) bottomF.pack(fill=X,expand=True) zoomoutbn = Button(bottomF, text='縮小', # 調用scale方法對圖形項進行縮放 # 前面兩個坐標指定縮放中心,后面兩個參數指定橫向、縱向的縮放比 command=lambda : cv.scale(oval, 180, 50, 0.8, 0.8)) zoomoutbn.pack(side=LEFT, ipadx=10, ipady=5, padx=3) zoominbn = Button(bottomF, text='放大', # 調用scale方法對圖形項進行縮放 # 前面兩個坐標指定縮放中心,后面兩個參數指定橫向、縱向的縮放比 command=lambda : cv.scale(oval, 180, 50, 1.2, 1.2)) zoominbn.pack(side=LEFT, ipadx=10, ipady=5, padx=3) def select_handler(ct): global current, current_outline, current_width # 如果ct元組包含了選中項 if ct is not None and len(ct) > 0: ct = ct[0] # 如果current對應的圖形項不為空 if current is not None: # 恢復current對應的圖形項的邊框 cv.itemconfig(current, outline=current_outline, width = current_width) # 獲取當前選中圖形項的邊框信息 current_outline = cv.itemcget(ct, 'outline') current_width = cv.itemcget(ct, 'width') # 使用current保存當前選中項 current = ct def click_handler(event): # 獲取當前選中的圖形項 ct = cv.find_closest(event.x, event.y) # 調用select _handler處理選中圖形項 select_handler(ct) def click_select(): # 取消為“框選”綁定的兩個事件處理函數 cv.unbind('<B1-Motion>') cv.unbind('<ButtonRelease-1>') # 為“點選”綁定鼠標點擊的事件處理函數 cv.bind('<Button-1>', click_handler) clickbn = Button(bottomF, text='點選圖形項', # 該按鈕觸發click_select函數 command=click_select) clickbn.pack(side=LEFT, ipadx=10, ipady=5, padx=3) # 記錄鼠標拖動的第一個點的x、y坐標 firstx = firsty = None # 記錄前一次繪制的、代表選擇區的虛線框 prev_select = None def drag_handler(event): global firstx, firsty, prev_select # 剛開始拖動時,用鼠標位置為firstx、firsty賦值 if firstx is None and firsty is None: firstx, firsty = event.x, event.y leftx, lefty = min(firstx, event.x), min(firsty, event.y) rightx, righty = max(firstx, event.x), max(firsty, event.y) # 刪除上一次繪制的虛線選擇框 if prev_select is not None: cv.delete(prev_select) # 重新繪制虛線選擇框 prev_select = cv.create_rectangle(leftx, lefty, rightx, righty, dash=2) def release_handler(event): global firstx, firsty if prev_select is not None: cv.delete(prev_select) if firstx is not None and firsty is not None: leftx, lefty = min(firstx, event.x), min(firsty, event.y) rightx, righty = max(firstx, event.x), max(firsty, event.y) firstx = firsty = None # 獲取當前選中的圖形項 ct = cv.find_enclosed(leftx, lefty, rightx, righty) # 調用select _handler處理選中圖形項 select_handler(ct) def rect_select(): # 取消為“點選”綁定的事件處理函數 cv.unbind('<Button-1>') # 為“框選”綁定鼠標拖動、鼠標釋放的事件處理函數 cv.bind('<B1-Motion>', drag_handler) cv.bind('<ButtonRelease-1>', release_handler) rectbn = Button(bottomF, text='框選圖形項', # 該按鈕觸發rect_select函數 command=rect_select) rectbn.pack(side=LEFT, ipadx=10, ipady=5, padx=3) deletebn = Button(bottomF, text='刪除', # 刪除圖形項 command=lambda : cv.delete(oval)) deletebn.pack(side=LEFT, ipadx=10, ipady=5, padx=3) root.mainloop() |