【问题标题】:How to save drawing on canvas as png file (linux)?如何将画布上的绘图保存为 png 文件(linux)?
【发布时间】:2023-02-06 10:41:24
【问题描述】:

我正在创建一个绘画应用程序,我想在我的计算机上将画布小部件上的绘图保存为 png 文件。这是我的代码:

from tkinter import *
from tkinter.filedialog import *
from functools import partial
from tkinter import Menu
from tkinter import filedialog,messagebox
from PIL import Image
from tkinter.colorchooser import askcolor
import pyscreenshot as ImageGrab
import pyautogui

class PaintingApp:
    x=y=None
    
    def __init__(self,window):
        self.window = window
        self.upper_frame = Frame(window)
        self.upper_frame.grid(row=0,column=0, padx=10, pady=5,sticky="ew")
        self.lower_frame = Frame(window)
        self.lower_frame.grid(row=2, column=0, padx=10, pady=5,sticky="ew")
        self.canvas= Canvas(self.lower_frame,width=500,height=530,bg="white")
        self.canvas.grid()
        self.objects = [] #objects on canvas
        self.pen_size = 2
        self.pcolor = "black"
        self.pen = Button(self.upper_frame,text="Pen",command=partial(self.pen_draw,thickness=self.pen_size)) 
        self.pen.grid(row=0,column=3,padx=(10,160))
        self.bg = Button(self.upper_frame,text="Background",command= self.bgcolor) #change bg color
        self.bg.grid(row=2,column=1,padx=(100,10))
        
        self.upper_menu()
        self.canvas.bind("<Button-1>", self.get_x_and_y)
        self.canvas.bind("<B1-Motion>", lambda event, b=self.pen_size: self.pen_draw(b,event))
        self.im = None
        
    def save_pic(self,event=None):
        file = asksaveasfilename(defaultextension=".png")
        x = self.canvas.winfo_rootx() + self.canvas.winfo_x()
        y = self.canvas.winfo_rooty() + self.canvas.winfo_y()
        x1 = x + self.canvas.winfo_width()
        y1 = y + self.canvas.winfo_height()
        self.im=ImageGrab.grab(bbox=(x,y,x1,y1))
        self.im.save(file[19:])
        
    def pen_color(self,color):
        self.pcolor= color 
            
    def get_x_and_y(self,event):
        global x,y
        x, y = event.x, event.y
        
    def pen_draw(self,thickness,event=None):
        global x,y
        self.canvas.bind("<Button-1>", self.get_x_and_y) # Bind to pen_draw function
        self.canvas.bind("<B1-Motion>", lambda event, b=self.pen_size: self.pen_draw(b,event))        
        if event != None:
            self.objects.append(self.canvas.create_line((x, y, event.x, event.y), fill=self.pcolor,width=self.pen_size,capstyle=ROUND,smooth=True))
            x, y = event.x, event.y
    
    def upper_menu(self):
        self.menubar = Menu(window)
        self.menu1 = Menu(self.menubar, tearoff=0)
        self.menu1.add_command(label="Save pic", command=self.save_pic)
        self.menu1.add_separator()
        self.menu1.add_command(label="Exit", command=window.destroy)
        self.menubar.add_cascade(label="Settings", menu=self.menu1)
        self.menu2 = Menu(self.menubar, tearoff=0)
        self.menu2.add_command(label="Open pic")
        self.menubar.add_cascade(label="Image", menu=self.menu2)
        self.window.config(menu=self.menubar)

    def bgcolor(self):
        chosen_color = askcolor(color=self.canvas["bg"])[1]
        self.canvas.configure(bg=chosen_color)
    
        
window = Tk()
window.geometry("500x450")
p = PaintingApp(window)

window.mainloop()     

现在我已经尝试了很多代码,但它不起作用。我上面提供的代码保存了一张没有任何意义的全黑图片。我也尝试过使用模块 pyautogui,但我仍然得到相同的结果。

def save_pic(self,event=None):
        file = asksaveasfilename(defaultextension=".png")
        x = self.canvas.winfo_rootx() + self.canvas.winfo_x()
        y = self.canvas.winfo_rooty() + self.canvas.winfo_y()
        x1 = x + self.canvas.winfo_width()
        y1 = y + self.canvas.winfo_height()
        self.im=pyautogui.screenshot(region=(x,y,x1,y1))
        self.im.save(file[19:])

【问题讨论】:

  • 您是否尝试检查(x, y, x1, y1)是否是您想要的区域?
  • @acw1668 我认为这是正确的区域。无论哪种方式,我唯一得到的是一张黑色图片。
  • 无法重现黑色图像问题。实际上使用.winfo_rootx().winfo_rooty()就足够了,不需要添加.winfo_x().winfo_y().winfo_x().winfo_y()为您的案例返回0)。你可以通过在self.canvas.grid(...)中添加padx=...pady=...来测试它,你会得到一个错误区域的图像。并且窗口大小不足以显示所有画布,因此窗口外的区域也会被捕获。另外我不明白为什么使用file[19:]而不是file

标签: python tkinter tkinter-canvas


【解决方案1】:

如果您使用屏幕截图库,您应该等到后端 UI 框架(在您的情况下为 X11)完成绘图。您也可以在 Pillow 中使用 PIL.ImageGrab.grab() 而不是 pyscreenshot。

这样做吧。(我修复了原始示例中的几个错误,例如不正确的路径。)

...
from PIL import ImageGrab
...

class PaintingApp:
    ...
    def save_pic(self,event=None):
        file = asksaveasfilename(defaultextension=".png")
        def grab_and_save():
            x = self.canvas.winfo_rootx()
            y = self.canvas.winfo_rooty()
            x1 = x + self.canvas.winfo_width()
            y1 = y + self.canvas.winfo_height()
            self.im = ImageGrab.grab(bbox=(x,y,x1,y1))
            self.im.save(file)
        self.window.update()
        self.window.after(1000, grab_and_save) # This waits 1000ms.
    ...
...

更好的方法是安装 Tkimg 并直接在画布上导出位图,但这需要一些努力。你可以从一个无人维护的项目开始,python-tkimg

【讨论】:

    猜你喜欢
    • 2019-10-19
    • 2013-03-26
    • 2021-11-18
    • 1970-01-01
    • 2012-11-12
    • 2016-01-28
    • 2021-06-24
    • 1970-01-01
    • 2023-03-23
    相关资源
    最近更新 更多