Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to convert a Python tkinter canvas postscript file to an image file readable by the PIL?

So I have created a function in my program that allows the user to save whatever he/she draws on the Turtle canvas as a Postscript file with his/her own name. However, there have been issues with some colors not appearing in the output as per the nature of Postscript files, and also, Postscript files just won't open on some other platforms. So I have decided to save the postscript file as a JPEG image since the JPEG file should be able to be opened on many platforms, can hopefully display all the colors of the canvas, and it should have a higher resolution than the postscript file. So, to do that, I have tried, using the PIL, the following in my save function:

def savefirst():
    cnv = getscreen().getcanvas() 
    global hen
    fev = cnv.postscript(file = 'InitialFile.ps', colormode = 'color')
    hen = filedialog.asksaveasfilename(defaultextension = '.jpg')
    im = Image.open(fev)
    print(im)
    im.save(hen + '.jpg')

However, whenever I run this, I get this error:

line 2391, in savefirst
    im = Image.open(fev)
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages/PIL/Image.py", line 2263, in open
    fp = io.BytesIO(fp.read())
AttributeError: 'str' object has no attribute 'read'

Apparently it cannot read the postscript file since it's not, according to what I know, an image in itself, so it has to first be converted into an image, THEN read as an image, and then finally converted and saved as a JPEG file. The question is, how would I be able to first convert the postscript file to an image file INSIDE the program possibly using the Python Imaging Library? Looking around SO and Google has been no help, so any help from the SO users is greatly appreciated!

EDIT: Following unubuntu's advice, I have now have this for my save function:

def savefirst():
    cnv = getscreen().getcanvas() 
    global hen
    ps = cnv.postscript(colormode = 'color')
    hen = filedialog.asksaveasfilename(defaultextension = '.jpg')
    im = Image.open(io.BytesIO(ps.encode('utf-8')))
    im.save(hen + '.jpg')

However, now whenever I run that, I get this error:

line 2395, in savefirst
    im.save(hen + '.jpg')
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages/PIL/Image.py", line 1646, in save
    self.load()
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages/PIL/EpsImagePlugin.py", line 337, in load
    self.im = Ghostscript(self.tile, self.size, self.fp, scale)
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages/PIL/EpsImagePlugin.py", line 143, in Ghostscript
    stdout=subprocess.PIPE)
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/subprocess.py", line 950, in __init__
    restore_signals, start_new_session)
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/subprocess.py", line 1544, in _execute_child
    raise child_exception_type(errno_num, err_msg)
FileNotFoundError: [Errno 2] No such file or directory: 'gs'

What is 'gs' and why am I getting this error now?

like image 918
R. Kap Avatar asked Feb 09 '23 01:02

R. Kap


1 Answers

If you don't supply the file parameter in the call to cnv.postscript, then a cnv.postscript returns the PostScript as a (unicode) string. You can then convert the unicode to bytes and feed that to io.BytesIO and feed that to Image.open. Image.open can accept as its first argument any file-like object that implements read, seek and tell methods.

import io
def savefirst():
    cnv = getscreen().getcanvas() 
    global hen
    ps = cnv.postscript(colormode = 'color')
    hen = filedialog.asksaveasfilename(defaultextension = '.jpg')
    im = Image.open(io.BytesIO(ps.encode('utf-8')))
    im.save(hen + '.jpg')

For example, borrowing heavily from A. Rodas' code,

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

class App(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        self.line_start = None
        self.canvas = tk.Canvas(self, width=300, height=300, bg="white")
        self.canvas.bind("<Button-1>", lambda e: self.draw(e.x, e.y))
        self.button = tk.Button(self, text="save",
                                command=self.save)
        self.canvas.pack()
        self.button.pack(pady=10)

    def draw(self, x, y):
        if self.line_start:
            x_origin, y_origin = self.line_start
            self.canvas.create_line(x_origin, y_origin, x, y)
        self.line_start = x, y

    def save(self):
        ps = self.canvas.postscript(colormode='color')
        img = Image.open(io.BytesIO(ps.encode('utf-8')))
        img.save('/tmp/test.jpg')

app = App()
app.mainloop()
like image 133
unutbu Avatar answered Feb 12 '23 12:02

unutbu