When I am trying to save an image from a tkinter canvas onto an external file, it doesn't keep the 16x16 resolution I would like which makes it blurry.
I was expecting no blur and it to keep the 16x16 resolution. I tried two methods of saving the file
The first method I tried is this:
self.sprite = tk.Canvas(self.root, width=16, height=16)
def get_clicked_pos(self, event):
y, x = event.y, event.x
gap = 288//16
row = y//gap # row and column are the wrong way but i found it easier to keep it that way instead of changing it all
col = x//gap
self.spriteGrid[row][col].tile = self.colour
self.spriteDraw(gap) # draws on the large canvas
self.sprite.create_rectangle(col, row, col+1, row+1, fill=self.colour, outline="")
def save_as_png1(self):
ps = self.sprite.postscript(width=16, height=16)
image = PIL.Image.open(io.BytesIO(ps.encode('utf-8')))
image.save('sprite.png')
In this method I made two canvases. One that is large and visible but a 16x16 grid so you can draw and one that is the correct size and not visible. When you draw on the large canvas it mimics it on the small canvas and when you save it turns the correct size canvas into a postscript and makes it a sprite. However, it doesn't work and creates this:
enter image description here method 1
The second method I tried is:
self.image1 = PIL.Image.new("RGB", (16, 16), "white")
self.draw = PIL.ImageDraw.Draw(self.image1)
def get_clicked_pos(self, event):
y, x = event.y, event.x
gap = 288//16
row = y//gap # row and column are the wrong way but i found it easier to keep it that way instead of changing it all
col = x//gap
self.spriteGrid[row][col].tile = self.colour
self.spriteDraw(gap)
self.draw.rectangle([(col, row), (col+1, row+1)], fill=self.colour)
self.sprite.create_rectangle(col, row, col+1, row+1, fill=self.colour, outline="")
def save_as_png2(self):
self.image1.save("alternate.png")
In this method, it copies what you draw onto a PIL image which then gets saved when you call save_as_image2. This provides similar results but in a 16x16 file
enter image description here method 2
The whole code is:
import tkinter as tk
from tkinter.colorchooser import askcolor
import PIL.Image
import PIL.ImageDraw
import io
class DrawWindow():
def __init__(self):
self.root = tk.Tk()
self.colour = "black"
self.menubar = tk.Menu(self.root)
self.optionsmenu = tk.Menu(self.menubar, tearoff=0)
self.optionsmenu.add_command(label="Save v1", command=self.save_as_png1)
self.optionsmenu.add_command(label="Save v2", command=self.save_as_png2)
self.menubar.add_cascade(menu=self.optionsmenu, label="Options")
self.root.config(menu=self.menubar)
self.sprFrame = tk.Frame(self.root)
self.sprFrame.pack()
self.spriteCanvas = tk.Canvas(self.sprFrame, height=288, width=288, bg="white")
self.spriteCanvas.pack(pady=5, padx=5)
self.pickColour = tk.Button(self.sprFrame, text="Pick colour", command=self.changeColour)
self.pickColour.pack(pady=5)
self.spriteGrid = []
for i in range(16):
self.spriteGrid.append([])
for j in range(16):
spot = Spot(i, j, 1, 16)
self.spriteGrid[i].append(spot)
self.sprite = tk.Canvas(self.root, width=16, height=16)
self.image1 = PIL.Image.new("RGB", (16, 16), "white")
self.draw = PIL.ImageDraw.Draw(self.image1)
self.spriteCanvas.bind("<Button-1>", self.get_clicked_pos)
self.root.mainloop()
def get_clicked_pos(self, event):
y, x = event.y, event.x
gap = 288//16
row = y//gap # row and column are the wrong way but i found it easier to keep it that way instead of changing it all
col = x//gap
self.spriteGrid[row][col].tile = self.colour
self.spriteDraw(gap)
self.draw.rectangle([(col, row), (col+1, row+1)], fill=self.colour)
self.sprite.create_rectangle(col, row, col+1, row+1, fill=self.colour, outline="")
def changeColour(self):
self.colour = askcolor(title="Sprite Colour")[1]
def save_as_png1(self):
ps = self.sprite.postscript(width=16, height=16)
image = PIL.Image.open(io.BytesIO(ps.encode('utf-8')))
image.save('sprite.png')
def save_as_png2(self):
self.image1.save("alternate.png")
def spriteDraw(self, gap):
for i in self.spriteGrid:
for j in i:
if not(j.tile==None):
self.spriteCanvas.create_rectangle(j.y*gap, j.x*gap, j.y*gap+gap, j.x*gap+gap, fill=j.tile)
class Spot:
def __init__(self, row, col, width, total_rows):
self.row = row
self.col = col
self.x = row * width
self.y = col * width
self.tile = None
self.width = width
self.total_rows = total_rows
DrawWindow()