I'm trying to display an image, procedurally generated by my code, into a canvas object. I don't want to use the canvas functions to generate these images because there's no good way (the screenshot and ghostcript methods are no good) to get pixel data out of the canvas.
I looked around for ways to generate the image pixels and settled on scikit-image, and then you can load the array as data in an image object in tkinter.
On the left is the pixel data displayed with matplotlib, on the right is the tkinter canvas with the same data displayed as a PhotoImage.
It's clear something is wrong.
This is my code:
from skimage.draw import polygon
from PIL import Image, ImageTk
import matplotlib.pyplot as plt
import tkinter as tk
import numpy as np
def generate():
# clear canvas
canvas.delete("all")
# make clean array
img = np.ones((width, height, 3), dtype=np.double) * 0.75
# randomly generate positions
x_generated = np.random.uniform(0, width, 5)
y_generated = np.random.uniform(0, height, 5)
for x, y in zip(x_generated, y_generated):
base_height = 70
base_width = 70
# generate the corners
x0 = x - base_width/2
x1 = x + base_width/2
y0 = y - base_height/2
y1 = y + base_height/2
# create rectangle
poly = np.array((
(x0, y0),
(x1, y0),
(x1, y1),
(x0, y1),
))
rr, cc = polygon(poly[:, 0], poly[:, 1], img.shape)
img[rr, cc, :] = 0.5
# # canvas method. I don't want to use this!
# canvas.create_rectangle(x0, y0, x1, y1, fill="red", outline="")
# display generated image in tkinter canvas
scaled_to_8bit = img * 255
imgarray = ImageTk.PhotoImage(image=Image.fromarray(scaled_to_8bit, 'RGB'))
# create the canvas image object from the numpy array
canvas.create_image(20, 20, anchor="nw", image=imgarray)
# display pixel data for debugging
plt.imshow(img)
plt.show()
if __name__ == "__main__":
width = 1000
height = 1000
# Create a root window
root = tk.Tk()
root.configure(background="green")
# Create a canvas widget
canvas = tk.Canvas(root, width=width, height=height)
canvas.pack()
# Create a button widget
button = tk.Button(root, text="Generate", command=generate)
button.pack()
# start main tk loop
root.mainloop()
The datatype of the blank image array was a double, but the image object doesn't support that format. I converted it to an 8 bit datatype just before displaying the image and this fixed the issue.
Note: there's still an issue where the image disappears when the matplotlib window is closed, but its a separate issue
from skimage.draw import polygon
from PIL import Image, ImageTk
import matplotlib.pyplot as plt
import tkinter as tk
import numpy as np
def generate():
# clear canvas
canvas.delete("all")
# make clean array
img = np.ones((width, height, 3), dtype=np.float32) * 0.75
# randomly generate positions
x_generated = np.random.uniform(0, width, 5)
y_generated = np.random.uniform(0, height, 5)
for x, y in zip(x_generated, y_generated):
base_height = 70
base_width = 70
# generate the corners
x0 = x - base_width/2
x1 = x + base_width/2
y0 = y - base_height/2
y1 = y + base_height/2
# create rectangle
poly = np.array((
(x0, y0),
(x1, y0),
(x1, y1),
(x0, y1),
))
rr, cc = polygon(poly[:, 0], poly[:, 1], img.shape)
img[rr, cc, :] = 0.5
# # canvas method. I don't want to use this!
# canvas.create_rectangle(x0, y0, x1, y1, fill="red", outline="")
# display generated image in tkinter canvas
scaled_to_8bit = img * 255
newarr = scaled_to_8bit.astype(np.uint8)
imgarray = ImageTk.PhotoImage(image=Image.fromarray(newarr, 'RGB'))
# create the canvas image object from the numpy array
canvas.create_image(20, 20, anchor="nw", image=imgarray)
# display pixel data for debugging
plt.imshow(img)
plt.show()
if __name__ == "__main__":
width = 1000
height = 1000
# Create a root window
root = tk.Tk()
root.configure(background="green")
# Create a canvas widget
canvas = tk.Canvas(root, width=width, height=height)
canvas.pack()
# Create a button widget
button = tk.Button(root, text="Generate", command=generate)
button.pack()
# start main tk loop
root.mainloop()