我想將我的繪圖保存在 tkinter 畫布上作為影像,以便我可以打開它以供以后使用。我目前使用這篇
實際上只有畫布的一部分是可見的。如果我打開保存的影像,這就是看起來只有可見的實際存在的樣子(整個影像在保存之前是黃色的)。
保存影像的代碼。
def save(widget(canvas), filelocation):
x=root.winfo_rootx() widget.winfo_x() 74
y=root.winfo_rooty() widget.winfo_y() 109
x1=x widget.winfo_width()
y1=y widget.winfo_height()
ImageGrab.grab().crop((x,y,x1,y1)).save(filelocation)
主意
從這篇
小問題
回復@Claudio:我現在正在使用螢屏截圖技術將 Canvas 作為影像保存到檔案中。我注意到保存的畫布影像在角落看起來像這樣,在保存并重新打開影像后它看起來像這樣
(畫布的邊框增加了畫布影像的大小)。
更新 2. 2022 年 6 月:已接受答案中提供的更新代碼解決了小問題。
uj5u.com熱心網友回復:
如何將 tkinter Canvas 圖形保存為影像?
tkinter 似乎沒有提供直接的方法來獲取 Canvas 圖形的影像以將其保存到影像檔案中,這似乎是一個事實。有兩種方法可以解決這個問題,只需要匯入 Python PIL 模塊(Pillow)。
其中一種方法是在 Canvas 區域上執行繪畫的螢屏截圖,這可以使用PIL.ImageGrab.grab()
或任何其他執行(裁剪)螢屏截圖并將它們保存到影像檔案的各種方法來完成(參見例如快速螢屏截圖的一小部分Python螢屏
截圖模塊的 Python 螢屏足夠快,可以在 Canvas 上制作正在進行的繪畫的視頻)。
另一種方法是在 Python PIL 影像上繪畫,使用修改后的 PIL 影像更新 tkinter 畫布,然后使用.save()
可用于保存 PIL 影像物件的方法將其保存到檔案中。
save()
如果同時使用 Frame ( stageframe
) 和 Canvas ( ) 小部件,則問題中提供的代碼通常按預期作業,stage
以獲取正確的 x,y 值以裁剪螢屏截圖,以防 Canvas 放置在 Frame 內并且如果邊界用于裁剪螢屏截圖的框考慮到 tkinter Canvas 小部件大小包括 Canvas 邊框和 Canvas 高亮邊框。
下面的代碼是問題中提供的代碼,帶有一些添加的注釋和適當的修改。它不需要鍵盤模塊,并通過單擊該函式pencilbutton
處理
的最左上角將修改后的 Canvas 保存為影像檔案。pencil_click()
它提供了將 tkinter Canvas 的圖形保存到影像檔案的兩種方法。method
通過為全域變數(method = 'screenshot'
或
)分配適當的值來選擇其中之一method = 'imagepaint'
:
# https://stackoverflow.com/questions/72459847/how-to-save-a-tkinter-canvas-as-an-image
from tkinter import Tk, colorchooser, Canvas, N, PhotoImage
from tkinter.ttk import Style, Frame, Button, Label, Scale
from PIL import Image, ImageTk # required to load images in tkinter
# method = 'screenshot' or 'imagepaint'
method = 'screenshot'
borderthickness_bd = 2
highlightthickness = 1
if method == 'imagepaint':
from PIL import ImageDraw # required to draw on the image
if method == 'screenshot':
from PIL import ImageGrab # required for the screenshot
filelocation = 'Test.png'
savelocation = 'Test_.png'
def save(stageframe, stage, savelocation):
if method == 'imagepaint':
global image
image.save(savelocation)
if method == 'screenshot':
global borderthickness_bd, highlightthickness
brdt = borderthickness_bd highlightthickness
# 1 and -2 because of thicknesses of Canvas borders (bd-border and highlight-border):
x=root.winfo_rootx() stageframe.winfo_x() stage.winfo_x() 1*brdt
y=root.winfo_rooty() stageframe.winfo_y() stage.winfo_y() 1*brdt
x1=x stage.winfo_width() -2*brdt
y1=y stage.winfo_height()-2*brdt
ImageGrab.grab().crop((x,y,x1,y1)).save(savelocation)
def type_of(color):
type_pen = 'marker'
if type_pen == 'marker':
pencil_motion_marker(color = color)
#pixel pen
def pencil_motion_marker(color):
stage.bind('<Button-1>' , get_pos_marker)
stage.bind('<B1-Motion>', lambda event, color = color: pencil_draw_marker(event, color))
def get_pos_marker(event):
global lastx, lasty
lastx, lasty = event.x, event.y
def pencil_draw_marker(event, color):
global method, lastx, lasty, draw, image, img_id
# print( (lastx, lasty, event.x, event.y), color, int(width.get()) )
if method == 'screenshot':
stage.create_line((lastx, lasty, event.x, event.y), width = width.get(), fill = color, capstyle = 'round')
get_pos_marker(event)
if method == 'imagepaint':
w12 = int(width.get()/2)
draw.ellipse( (event.x-w12, event.y-w12, event.x w12, event.y w12), fill=color )
imgtk = ImageTk.PhotoImage(image)
stage.itemconfig(img_id, image=imgtk)
stage.image = imgtk
def choose_pen_color():
pencilcolor = colorchooser.askcolor(title = 'Pencil Color')
type_of(pencilcolor[1])
##
def pencil_click():
global width, opacity, stageframe, stage, savelocation
# imgToSave = stage.image # gives a PhotoImage object
# imgToSave._PhotoImage__photo.write("Test.gif", format='gif') # which can be saved, but ...
# ^--- ... with no painting done on Canvas - only the image.
save(stageframe, stage, savelocation)
Whitepencolb = Button(optionsframe, text = 'Whitepencolimg', style = 'COLBG.TButton', command = lambda m = 'White': type_of(m))
Whitepencolb.grid(row = 0, column = 0, padx = 10, pady = 1)
Redpencolb = Button(optionsframe, text = 'Redpencolimg', style = 'COLBG.TButton', command = lambda m = 'Red': type_of(m))
Redpencolb.grid(row = 1, column = 0, padx = 10, pady = 1)
Magentapencolb = Button(optionsframe, text = 'Magentapencolimg', style = 'COLBG.TButton', command = lambda m = 'Magenta': type_of(m))
Magentapencolb.grid(row = 0, column = 1, padx = 10, pady = 1)
Limegreenpencolb = Button(optionsframe, text = 'Limegreenpencolimg', style = 'COLBG.TButton', command = lambda m = 'Lime': type_of(m))
Limegreenpencolb.grid(row = 1, column = 1, padx = 10, pady = 1)
Greenpencolb = Button(optionsframe, text = 'Greenpencolimg', style = 'COLBG.TButton', command = lambda m = 'Green': type_of(m))
Greenpencolb.grid(row = 0, column = 2, padx = 10, pady = 1)
Bluepencolb = Button(optionsframe, text = 'Bluepencolimg', style = 'COLBG.TButton', command = lambda m = 'Blue': type_of(m))
Bluepencolb.grid(row = 1, column = 2, padx = 10, pady = 1)
Cyanpencolb = Button(optionsframe, text = 'Cyanpencolimg', style = 'COLBG.TButton', command = lambda m = 'Cyan': type_of(m))
Cyanpencolb.grid(row = 0, column = 3, padx = 10, pady = 1)
Yellowpencolb = Button(optionsframe, text = 'Yellowpencolimg', style = 'COLBG.TButton', command = lambda m = 'Yellow': type_of(m))
Yellowpencolb.grid(row = 1, column = 3, padx = 10, pady = 1)
Orangepencolb = Button(optionsframe, text = 'Orangepencolimg', style = 'COLBG.TButton', command = lambda m = 'Orange': type_of(m))
Orangepencolb.grid(row = 0, column = 4, padx = 10, pady = 1)
Graypencolb = Button(optionsframe, text = 'Graypencolimg', style = 'COLBG.TButton', command = lambda m = 'Gray': type_of(m))
Graypencolb.grid(row = 1, column = 4, padx = 10, pady = 1)
Blackpencolb = Button(optionsframe, text = 'Blackpencolimg', style = 'COLBG.TButton', command = lambda m = 'Black': type_of(m))
Blackpencolb.grid(row = 0, column = 5, padx = 10, pady = 1)
Createnewpencolb = Button(optionsframe, text = 'Createnewpencolimg', style = 'COLBG.TButton', command = choose_pen_color)
Createnewpencolb.grid(row = 1, column = 5, padx = 10, pady = 1)
widthlabel = Label(optionsframe, text = 'Width: ', style = 'LABELBG.TLabel')
width = Scale(optionsframe, from_ = 1, to = 100, style = 'SCALEBG.Horizontal.TScale')
widthlabel.grid(row = 0, column = 6)
width.grid(row = 0, column = 7)
width.set(20)
opacitylabel = Label(optionsframe, text = 'Opacity: ', style = 'LABELBG.TLabel')
opacity = Scale(optionsframe, from_ = 0, to = 1.0, style = 'SCALEBG.Horizontal.TScale')
opacitylabel.grid(row = 1, column = 6)
opacity.grid(row = 1, column = 7)
opacity.set(1.0)
def setup(filelocation):
global stage, stageframe, img_id, optionsframe, draw, image, img_id, method
global borderthickness_bd, highlightthickness
for widgets in root.winfo_children():
widgets.destroy()
root.config(bg = '#454545')
iconsframewidth = int(screen_width / 20)
frames = Style()
frames.configure('FRAMES.TFrame', background = '#2a2a2a')
sep = Style()
sep.configure('SEP.TFrame', background = '#1a1a1a')
style = Style()
style.configure('STAGE.TFrame', background = '#454545')
icon = Style()
icon.configure('ICON.TButton', background = '#2a2a2a', foreground = '#2a2a2a')
iconsframe = Frame(root, width = iconsframewidth, style = 'FRAMES.TFrame')
iconsframe.pack(side = 'left', expand = False, fill = 'y')
iconsframe.pack_propagate(0)
sep1frame = Frame(root, style = 'SEP.TFrame', width = 5)
sep1frame.pack(side = 'left', expand = False, fill = 'y')
optionsframe = Frame(root, style = 'FRAMES.TFrame', height = 100)
optionsframe.pack(side = 'top', expand = False, fill = 'x')
optionsframe.pack_propagate(0)
sep2frame = Frame(root, style = 'SEP.TFrame', height = 5)
sep2frame.pack(side = 'top', expand = False, fill = 'x')
propertyframe = Frame(root, style = 'FRAMES.TFrame', width = 150)
propertyframe.pack(side = 'right', expand = False, fill = 'y')
propertyframe.pack_propagate(0)
sep3frame = Frame(root, style = 'SEP.TFrame', width = 5)
sep3frame.pack(side = 'right', expand = False, fill = 'y')
stageframe = Frame(root, style = 'STAGE.TFrame')
stageframe.pack(side = 'top', expand = True, fill = 'both')
stageframe.pack_propagate(0)
image = Image.open(filelocation)
width, height = image.size
if method == 'imagepaint':
draw = ImageDraw.Draw(image)
imgtk = ImageTk.PhotoImage(image)
# width, height = imgtk._PhotoImage__size
# imgtk = PhotoImage(filelocation)
# ^--- no width, hight information ???
stage = Canvas(stageframe, width = width, height = height, bd=borderthickness_bd, highlightthickness=highlightthickness) # default: bd=2, highlightthickness=1
stage.pack(side="top", anchor = 'c', expand=True)
root.update()
# keyboard.add_hotkey("ctrl s", lambda widget = stageframe, filelocation = filelocation: save(widget, filelocation))
pencilbutton = Button(iconsframe, text = 'pencilimg', command = pencil_click, style = 'ICON.TButton')
pencilbutton.pack(anchor = N, pady = 10)
img_id = stage.create_image(stage.winfo_width() / 2, stage.winfo_height() / 2, image = imgtk)
stage.image = imgtk
root = Tk()
root.title('App')
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
w = 1150
h = 600
x = (screen_width / 2) - (w / 2)
y = (screen_height / 2) - (h / 2)
root.geometry('%dx%d %d %d' % (w, h, x, y))
root.minsize(1150, 600)
setup(filelocation)
root.mainloop()
裁剪螢屏截圖作為保存 tkinter Canvas 圖形的一種方式比在更新 tkinter Canvas 的 PIL 影像上繪畫更可取,因為后者具有減慢圖形速度的副作用,因此繪畫平滑度會受到影響。
要查看如何更改 tkinter 中按鈕的外觀(在第一次單擊后更改pencilbutton
為 a savebutton
),請查看
Python tkinter: error _tkinter.TclError: bad window path name ".!button2"以了解如何完成。
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/486931.html
標籤:Python python-3.x tkinter 帆布
上一篇:獲取樣式字體(Ttk)
下一篇:如何正確使用.pack()方法?