将画布从 tkinter 保存到文件

Posted

技术标签:

【中文标题】将画布从 tkinter 保存到文件【英文标题】:Saving canvas from tkinter to file 【发布时间】:2017-06-15 21:47:07 【问题描述】:

所以我关注了这个帖子: How can I convert canvas content to an image?

当我尝试按照上一个建议中提到的那样做时,我遇到了以下问题:

当我这样称呼它时,图像/屏幕截图拍摄得太早,因此无法捕获所需的图像。代码如下:

from tkinter import *
from PIL import ImageGrab
root = Tk()
cv = Canvas(root)

cv.pack()

cv.create_rectangle(10,10,50,50)
#cv.create_line([0, 10, 10, 10], fill='green')


cv.update()


#print(root.winfo_width())

def getter(widget):
    x=root.winfo_rootx()+widget.winfo_x()
    print(x)
    y=root.winfo_rooty()+widget.winfo_y()
    print(y)
    x1=x+widget.winfo_width()
    print(x1)
    y1=y+widget.winfo_height()
    print(y1)
    ImageGrab.grab().crop((x,y,x1,y1)).save("em.jpg")

getter(cv)
root.mainloop()

顺便说一句,如果有更简单的解决方案,我将不胜感激! 问题是保存部分将在以后动态添加到代码中,因此解决方案应该尽可能轻。

提前致谢!

PS:也许甚至可以在不预先显示的情况下保存画布?就凭代码?

【问题讨论】:

使用root.after(miliseconds, callback) 延迟执行。 O 使用PIL 而不是Tkinter 来绘制它。 PIL 有很多绘制对象的函数。 延迟时我应该调用哪种方法? root.after(3000, root.mainloop())? root.after(3000, getter)callback 表示“没有() 的函数名” - 或者您必须使用lambda 添加参数root.after(3000, lambda:getter(cv)) 或者只是将参数添加到 root.after 调用:root.after(3000, getter, cv). I suspect that root.update()` 在 getter 调用之前或内部(而不是 root.after 和 mainloop)也可以解决您的问题这个特定的代码。 【参考方案1】:

以下是仅截取 tkinter 画布屏幕截图的代码。 PIL.ImageGrab 模块在 Linux 中不起作用;将其替换为pyscreenshot。在 Ubuntu 16.04 中测试了此代码。您可能需要检查它是否在 Windows/OSx 中运行。请注意函数self._grabtofile 中的备注。

备注: 在 Ubuntu 中,此脚本必须直接在命令行/终端上执行才能工作。从 IDLE for python3 执行时它不起作用。

总结:

    能够显示 tkinter 画布的屏幕截图并将其保存到文件 使用两个事件。 能够截取 tkinter 画布(不显示)并将其保存到 使用一个事件的文件。

工作代码:

#!/usr/bin/python3
# -*- coding: utf-8 -*-

try:
    import tkinter as tk # Python 3 tkinter modules
except ImportError:
    import Tkinter as tk # Python 2 tkinter modules

from PIL import Image, ImageTk 
#from PIL import Image, ImageTk, ImageGrab  # For Windows & OSx
import pyscreenshot as ImageGrab # For Linux

class App(tk.Frame):
    def __init__(self, parent):
        tk.Frame.__init__(self, parent)
        self.parent=parent

        file = 'images.jpg'
        self.img = Image.open(file)
        #self.img.show() #Check to proof image can be read in and displayed correctly.
        self.photo = ImageTk.PhotoImage(self.img)
        print('size of self.img =', self.img.size)
        centerx= self.img.size[0]//2
        centery= self.img.size[1]//2
        print ('center of self.img = ', centerx, centery)

        self.cv = tk.Canvas(self)
        self.cv.create_image(centerx, centery, image=self.photo)
        self.cv.create_rectangle(centerx*0.5,centery*0.5,centerx*1.5,centery*1.5,
                                 outline='blue')
        self.cv.grid(row=0, column=0, columnspan=3, sticky='nsew') 

        self.snappic=tk.Button(self, text='SNAP', command=self._snapCanvas)
        self.snappic.grid(row=1, column=0, sticky='nsew')

        self.savepic=tk.Button(self, text='SAVE', command=self._save)
        self.savepic.grid(row=1, column=1, sticky='nsew')

        self.directsavepic=tk.Button(self, text='Grab_to_File', command=self._grabtofile)
        self.directsavepic.grid(row=1, column=2, sticky='nsew')

        self.snapsave=tk.Button(self, text='SNAP & SAVE', command=self._snapsaveCanvas)
        self.snapsave.grid(row=2, column=0, columnspan=2, sticky='nsew')

    def _snapCanvas(self):
        print('\n def _snapCanvas(self):')
        canvas = self._canvas() # Get Window Coordinates of Canvas
        self.grabcanvas = ImageGrab.grab(bbox=canvas)
        self.grabcanvas.show()

    def _save(self):
        self.grabcanvas.save("out.jpg")
        print('Screenshoot of tkinter.Canvas saved in "out.jpg"')

    def _grabtofile(self):
        '''Remark: The intension was to directly save a screenshoot of the canvas in
                   "out_grabtofile.png".
                   Issue 1: Only a full screenshot was save.
                   Issue 2: Saved image format defaults to .png. Other format gave errors. 
                   Issue 3: "ImageGrab.grab_to_file" only able to return full screenshoot
                            and not just the canvas. '''
        print('\n def _grabtofile(self):')
        canvas = self._canvas()  # Get Window Coordinates of Canvas
        print('canvas = ', canvas)
        ImageGrab.grab_to_file("out_grabtofile.png", ImageGrab.grab(bbox=canvas))
        print('Screenshoot of tkinter.Canvas directly saved in "out_grabtofile.png"')

    def _snapsaveCanvas(self):
        print('\n def _snapsaveCanvas(self):')
        canvas = self._canvas()  # Get Window Coordinates of Canvas
        self.grabcanvas = ImageGrab.grab(bbox=canvas).save("out_snapsave.jpg")
        print('Screencshot tkinter canvas and saved as "out_snapsave.jpg w/o displaying screenshoot."')

    def _canvas(self):
        print('  def _canvas(self):')
        print('self.cv.winfo_rootx() = ', self.cv.winfo_rootx())
        print('self.cv.winfo_rooty() = ', self.cv.winfo_rooty())
        print('self.cv.winfo_x() =', self.cv.winfo_x())
        print('self.cv.winfo_y() =', self.cv.winfo_y())
        print('self.cv.winfo_width() =', self.cv.winfo_width())
        print('self.cv.winfo_height() =', self.cv.winfo_height())
        x=self.cv.winfo_rootx()+self.cv.winfo_x()
        y=self.cv.winfo_rooty()+self.cv.winfo_y()
        x1=x+self.cv.winfo_width()
        y1=y+self.cv.winfo_height()
        box=(x,y,x1,y1)
        print('box = ', box)
        return box


if __name__ == '__main__':
    root = tk.Tk()
    root.title('App'), root.geometry('300x300')
    app = App(root)
    app.grid(row=0, column=0, sticky='nsew')

    root.rowconfigure(0, weight=1)
    root.columnconfigure(0, weight=1)

    app.rowconfigure(0, weight=10)
    app.rowconfigure(1, weight=1)
    app.columnconfigure(0, weight=1)
    app.columnconfigure(1, weight=1)
    app.columnconfigure(2, weight=1)

    app.mainloop()

界面截图:

GUI 的 tk.Canvas 截图:

【讨论】:

谢谢!你能否也请给我一个如何使用它的例子?例如我的 tkinter 代码。 @EerikMuuli 对不起,我不明白你的问题。请详细说明。如果您询问如何执行此代码,则可以将该文件保存为文件名“test.py”,打开终端并键入 python3 test.py 以执行它。照原样,如果您研究代码,它将向您展示如何应用您在代码中编写的内容(带有某些更正或修正)如何截取您的 GUI tkinter 画布。我添加了很多打印语句和cmets来帮助你学习代码。 抱歉没有详细说明。我让它工作 - 它打开一个带有按钮的屏幕。但是是否可以自动将 Canvas 保存到图像文件中。例如,如果我有一个代码(绘制画布)并且我想将其自动保存到图像中。因此,例如,如果我将“画布绘图代码”添加到您的代码中,那么从命令行执行 test.py 会将画布的输出保存到文件中。 (在此过程中没有任何用户输入)。我希望这能让它更清楚。 您能解释一下“自动”是什么意思吗?照原样,代码已经生成了一个带有 tk.Canvas 和按钮的 GUI,供您在需要时对 tk.Canvas(甚至监视器屏幕)进行截图;你只需要点击它们。 @EerikMuuli 您需要在代码的第 3 行添加 from PIL import Image。第 20 行的保存命令来自 PIL.Image。另一件事,我注意到您在代码的第 9 行中使用了cv.update()。你有需要它的理由吗?请阅读here。我觉得cv.update_idletasks()比较合适。【参考方案2】:

我知道这是一篇旧帖子,但我只想分享对我有用的东西,希望它可以帮助遇到类似问题的人。

import tkinter as tk
from PIL import Image
import io
import os
import subprocess

root = Tk()

cv = Canvas(root)
cv.pack()

cv.create_rectangle(10,10,50,50)

ps = cv.postscript(colormode='color')
img = Image.open(io.BytesIO(ps.encode('utf-8')))
img.save('filename.jpg', 'jpeg')

root.mainloop()

在此解决方案中,您必须安装 ghostscript 和 Pillow。 对于 MacOS,安装 ghostscript 与

brew install ghostscript

和枕头 pip install Pillow

【讨论】:

由于 img.save 行,我收到错误“raise WindowsError('Unable to locate Ghostscript on paths')”。有什么想法吗?

以上是关于将画布从 tkinter 保存到文件的主要内容,如果未能解决你的问题,请参考以下文章

Python Tkinter - 保存画布 - tkinter 崩溃

如何将点击事件绑定到 Tkinter 中的画布? [关闭]

如何将整个tkinter画布截图为png或pdf

将matplotlib嵌入到tkinter画布中会打开两个窗口

如何从锚点旋转 tkinter 画布小部件?

如何从启动页面在不同的 tkinter 画布之间切换并返回子画布中的启动页面