下面代碼簡單實現了手繪畫板功能,其實也是Canvas的教程,後面都給加了註釋。
這裏通過列表來存儲筆畫,實現撤銷恢復功能,右鍵有菜單。
import tkinter as tk
from tkinter import ttk
class tkinter_example(object):
def __init__(s):
s.win = tk.Tk() # 創建主窗口
s.win.title("tkinter Canvas") # 窗口標題
s.win.withdraw() # 隱藏窗口
s.win.update_idletasks() # 刷新窗口
s.width, s.height = 600,400 #獲取此時窗口大小
s.Canvas() # 添加Canvas界面
#窗口位置居中
s.win.geometry('%dx%d+%d+%d' % (s.width, s.height,
(s.win.winfo_screenwidth() - s.width)/2,
(s.win.winfo_screenheight() - s.height)/2))
s.win.resizable(0,0) # 阻止GUI大小調整
s.win.deiconify() # 顯示窗口
s.win.mainloop() # 顯示主窗口
def Canvas(s):
#新建畫布界面
canvas = tk.Canvas(s.win, width=s.width, height=s.height, highlightthickness=0, bg='#AFEEEE')
canvas.grid()
#鼠標中鍵滾動事件
canvas.bind("<MouseWheel>", lambda event: print("向上滾動") if event.delta > 0 else print("向下滾動"))
#指定tag點擊事件響應
canvas.tag_bind('other','<Button-1>', lambda event: print("other 點擊相應"))
#畫一條直線提供所要繪製的直線連接的兩個點座標
canvas.create_line(0, 30, s.width, 30, fill="#476042", dash = (4, 4)) #加了dash就是虛線,不加就是實線
#畫一個矩形,提供兩個點的座標: 第一個點爲左上角座標, 第二個點爲右下角座標, outline邊框顏色,fill填充顏色
canvas.create_rectangle(5, 5, 45, 25, outline="#476042", fill="#476042", tags = "other")
#寫文字,文字將以此座標爲中心進行繪製,也寫 anchor 屬性來改變文字繪製的對齊方式. 比如:anchor = 'nw'
canvas.create_text(25, 15, text="Python", fill="#ffffff")
#畫一個橢圓,提供橢圓外切矩形兩個頂點,同畫矩形
canvas.create_oval(50, 5, 100, 25, fill="#476042", tags = "other")
#畫一個正圓,提供正圓外切正方形兩個頂點,同畫矩形
canvas.create_oval(105, 5, 125, 25, fill="#476042", tags = "other")
#繪製多邊形
points1 = [130, 25, 160, 25, 145, 5]#三角形
canvas.create_polygon(points1, outline="#ff0000", fill='#ff0000', width=1, tags = "other")
#繪製圖片,貼圖(這裏的貼圖必須是 全局 或者和 mainloop在同一個函數下,否則會被清除導致不顯示)
s.image = tk.PhotoImage(file = tk.__file__.split("tkinter")[0] + 'test\\imghdrdata\\python.gif')
canvas.create_image(180, 10, anchor = 'nw', image = s.image, tags = "other")
#畫弧線(座標,start = 開始方向角,extent = 結束方向角)
canvas.create_arc((200, 5, 245, 50), start = 0, extent = 120, fill = "blue", tags = "other")
#繪製Bitmap
bitmaps = ["error", "gray75", "gray50", "gray25", "gray12", "hourglass", "info", "questhead", "question", "warning"]
nsteps = len(bitmaps)
step_x = int((s.width-250) / nsteps)
for i in range(0, nsteps):
canvas.create_bitmap(250 + (i+1)*step_x - step_x/2,15, bitmap=bitmaps[i])
#創建一個可在 canvas 上手動繪圖的效果,通過兩點畫線段的方式
draw_point = ['', ''] #用於儲存拖拉鼠標時的點
revoke = [ ] #用於儲存每次鼠標繪圖操作的ID供撤銷用[[...],[...],[...]]
recover = [ ] #用於儲存每次鼠標繪圖的點構成的列表供恢復
clear = [ ] #用於記錄是否使用過清空,因爲列表可變,支持全局修改,所以用列表記錄
def _canvas_draw(event):
if not event: #鬆開鼠標左鍵時執行,清空記錄點
draw_point[:] = ['',''] #[:]只改變draw_point指向的列表的內容,不是重新賦值一個新的列表所以修改值全局通用
return
point = [event.x, event.y] #此次傳遞的點座標
if draw_point==['','']: #按下鼠標左鍵開始拖動時執行
draw_point[:] = point #保存拖動的第一個點
if len(revoke) < len(recover):
recover[len(revoke):] = [] #用於使用過撤銷後再繪圖,清除撤銷點後的恢復數據
clear[:] = []
revoke.append([]) #新建一個撤銷記錄列表
recover.append([]) #新建一個恢復記錄列表
recover[-1].extend(point)#在新建的恢復記錄列表裏記錄第一個點
else:
revoke[-1].append(
canvas.create_line(draw_point[0], draw_point[1], event.x, event.y, fill="#476042", width=1,
tags = "line")
) #繪製的線段並保存到撤銷記錄的末次列表
draw_point[:] = point #保存拖動點,覆蓋上一次
recover[-1].extend(point)#保存此次傳遞的點座標到恢復記錄的末次列表
canvas.bind("<B1-Motion>", _canvas_draw) #設定拖動鼠標左鍵畫線段
canvas.bind("<ButtonRelease-1>", lambda event:_canvas_draw(0)) #設定鬆開鼠標左鍵清除保存的點
#添加撤銷和恢復功能rev撤銷,rec恢復
def _canvas_re(rev=0, rec=0):
if rev and revoke: #撤銷執行
for i in revoke.pop(-1): canvas.delete(i) #pop彈出最後一個撤銷列表,刪除圖像
elif rec and recover and (len(revoke) != len(recover)): #恢復執行,恢復列表需要大於撤銷列表
if clear:
for i in recover: revoke.append([canvas.create_line(i , fill="#476042", width=1, tags = "line")])
clear[:] = []
else:
revoke.append([canvas.create_line(recover[len(revoke)], fill="#476042", width=1, tags = "line")])
#清空功能
def _canvas_clear():
canvas.delete("line") #清除 tags = "line"的圖像
revoke[:] = []
clear.append(1)
#添加右鍵菜單
menu = tk.Menu(s.win, tearoff=0) #不加 tearoff=0 的會出現可彈出選項
menu.add_command(label="撤銷", command = lambda:_canvas_re(rev=1))
menu.add_command(label="恢復", command = lambda:_canvas_re(rec=1))
menu.add_command(label="清空", command = _canvas_clear)
canvas.bind("<Button-3>", lambda event: menu.post(event.x_root,event.y_root))#右鍵激活菜單
# 創建一個Button對象,默認設置爲居中對齊
bt1 = ttk.Button(canvas,text = '撤銷',command = lambda:_canvas_re(rev=1))
#修改button在canvas上的對齊方式
canvas.create_window((5, s.height-20), window = bt1, anchor = 'w')
bt2 = ttk.Button(canvas,text = '恢復',command = lambda:_canvas_re(rec=1))
canvas.create_window((s.width-90, s.height-20), window = bt2, anchor = 'w')
bt3 = ttk.Button(canvas,text = "清空", command = _canvas_clear)
canvas.create_window((s.width/2-43, s.height-20), window = bt3, anchor = 'w')
tkinter_example()