Error object has no attribute in Paint app - python

This is the code, I got it from here https://www.youtube.com/watch?v=uW-NLL9dlBs
After many attempts, I solved some issues, but I still couldnt get it to work.
Its supposed to be an app for painting. I wanted it to have very large pixels with invisible grid too, so painting in it would create low resolution pixel art, similar to this. I dont know how to do that, as I couldnt find it anywhere. pixel image
from tkinter import *
from tkinter.ttk import Scale
from tkinter import colorchooser,filedialog,messagebox
import PIL.ImageGrab as ImageGrab
class Paint():
def __init__(self,root):
self.root = root
self.root.title("Paint")
self.root.geometry("800x520")
self.root.configure(background='white')
self.root.resizable(0,0)
self.pen_color = "black"
self.eraser_color = "white"
self.color_frame = LabelFrame(self.root,text='Color',font = ('arial',15),bd=5,relief=RIDGE,bg='white')
self.color_frame.place(x=0,y=0,width=70,height=185)
colors = ['#ff0000','#ff4dd2','#ffff33','#000000','#0066ff','#660033','#4dff4d','#b300b3','#00ffff','#808080','#99ffcc','#336600','#ff9966','#ff99ff','#00cc99']
i=j=0
for color in colors:
Button(self.color_frame,bg=color,bd=2,relief=RIDGE,width=3,command=lambda col =color:self.select_color(col)).grid(row=i,column=j)
i+=1
if i==6:
i=0
j=1
self.eraser_button = Button(self.root,text="ERASER",bd=4,bg='white',command=self.eraser,width=8,relief=RIDGE)
self.eraser_button.place(x=0,y=187)
self.clear_button = Button(self.root, text="Clear",bd=4,bg='white',command=lambda : self.canvas.delete("all"),width=8,relief=RIDGE)
self.clear_button.place(x=0,y=217)
self.save_button = Button(self.root,text="Save",bd=4,bg='white',command=self.save_paint,width=8,relief=RIDGE)
self.save_button.place(x=0,y=247)
self.canvas_color_button = Button(self.root,text="Canvas", bd=4 , bg='white', command=self.canvas_color(),width=8,relief=RIDGE)
self.canvas_color_button.place(x=0, y=277)
self.pen_size_scale_frame = LabelFrame(self.root,text="size",bd=5,bg='white',font=('arial',15,'bold'),relief=RIDGE)
self.pen_size_scale_frame.place(x=0,y=310,height=200,width=70)
self.pen_size = Scale(self.pen_size_scale_frame,orient=VERTICAL,from_ = 50, to = 0,length=170)
self.pen_size.set(1)
self.pen_size.grid(row=0,column=1,padx=15)
self.canvas = Canvas(self.root,bg='white',bd=5,relief=GROOVE,height=500,width=700)
self.canvas.place(x=80,y=0)
self.canvas.bind("<B1-Motion>",self.paint)
def paint(self,event):
x1,y1 = (event.x-2),(event.y-2)
x2,y2 = (event.x + 2),(event.y + 2)
self.canvas.create_oval(x1,y1,x2,y2,fill=self.pen_color,outline=self.pen_color,width=self.pen_size.get())
def select_color(self,col):
self.pen_color = self.eraser_color
def eraser(self):
self.pen_color = "white"
def canvas_color(self):
color = colorchooser.askcolor()
self.canvas.configure(background=color[1])
self.eraser_color = color[1]
def save_paint(self):
try:
filename = asksaveasfilename(defaultextension='.jpg')
x = self.root.winfo_rooty() + self.canvas.winfo_x()
y = self.root.winfo_rooty() + self.canvas.winfo_y()
x1 = x + self.canvas.winfo_width()
y1 = y + self.canvas.winfo_height()
ImageGrab.grab().crop((x,y,x1,y1)).save(filename)
messagebox.showinfo('paint says','image is saved as ' + str(filename))
except:
messagebox.showerror("paint says","unable to save image ,\n something went wrong")
if __name__ == "__main__":
root = Tk()
p = Paint(root)
root.mainloop()

In order to get your paint program working, the following bugs need to be addressed.
The self.canvas_color_button command has a bug. Just remove the parens to make it work.
There's a problem with color selection methods.
Here is the solution.
def select_color(self,col):
self.pen_color = col
def eraser(self):
self.pen_color = self.eraser_color
When saving a drawing, the image is not cropped correctly due to other widgets.
self.color_frame has a width of 70.
Your canvas is defined with a borderwidth(5) and a highlightthickness(2).
These values must be taken into account.
Here is one solution to crop your saved images correctly.
xoffset = self.color_frame.winfo_width()
yoffset = int(self.canvas["borderwidth"]) + int(self.canvas["highlightthickness"])
x = self.root.winfo_rooty() + self.canvas.winfo_x()
y = self.root.winfo_rooty() + self.canvas.winfo_y() + yoffset
x1 = x + self.canvas.winfo_width() - xoffset
y1 = y + self.canvas.winfo_height() - (yoffset * 2)

Related

Canvas shows only one picture(the last) from the list

I'm trying to draw a chessboard by canvas, but I get only one piece at the end of the code. I tried to draw a line and it still exists, but piece no!
Here is the code:
from tkinter import *
class pawn:
def __init__(self,coords, color, board):
self.color = color
self.board = board
self.type = "pawn"
self.x = coords[0]
self.y = coords[1]
pass
def draw_board(board):
root = Tk()
canvas = Canvas(root, width=512, height=512)
canvas.pack()
for line in range(8):
for cell in range(8):
if board[line][cell] != "":
l = r"C:\python\Chess\images\ "[:-1]
l = l + board[line][cell].type + board[line][cell].color[0] + ".png"
print(l)
img = PhotoImage(file=l)
canvas.create_image(32 + line*64, 32 + cell*64, image=img)
root.mainloop()
board = [[""for j in range(8)] for k in range(8)]
pawn_1 = pawn([1,1],"White",board)
pawn_2 = pawn([2,2],"Black",board)
board[2][2] = pawn_1
board[1][1] = pawn_2
draw_board(board)
It's working with one piece, but if I create 2 in the list, it'll draw only the last.

Tkinter: changing canvas pixel size

Here is what I want to do:
I am designing an application that allows, among other things, to view DICOM images. What I will call "image" is actually a set of files that contain 2D arrays corresponding to slices. I am using Tkinter to provide an UI. My application only requires to display binary images.
In order to display the slices, I use tk.Canvas which allows very fast display of images. Indeed, I need the most optimised displaying device since I want to the user to be able to travel across slices using the mouse wheel.
The problem is: when displaying a slice, the canvas is always allocating the same dimensions to pixels and therefore, images with lower resolution appear very small. What I want to do is to prevent the user from killing his/her eyes by resizing the canvas.
I thought of course of using PIL.Image().resize() on the image that is then converted to PIL.ImageTk() but this causes two problems:
The greater the resizing, the more time is needed to perform the process and therefore the less the viewer is optimised
This resizing action actually modifies the number of pixels, which loses the original resolution. I do not want this to happen since I require to retrieve mouse position as it hovers over the canvas, in terms of pixels in the original resolution
The solution in my opinion would therefore be to modify the pixel size of the canvas. If it is possible to define it from the start, then resizing would not be necessary and there would be no optimisation problem.
But I have not been able to find a way to modify this. Would anyone have an idea?
I am providing only the frame and the imager of my project if it can help:
Frame:
import PIL.Image
import PIL.ImageTk
import numpy as np
from gui.statusbar import *
from tkinter.messagebox import showinfo
class DicomViewerFrame(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.image_ax = None
self.image_sag = None
self.photo_ax = None
self.photo_sag = None
self.canvas_axial = None
self.canvas_sagittal = None
self.imager = None
self.arr_axial = None
self.arr_sagittal = None
self.index_axial = None
self.index_sagittal = None
self.status_axial = None
self.status_sagittal = None
self.roi_definition = True
self.upper = 0
self.lower = 0
self.selection_axial = None
self.selection_sagittal = None
self.start_x = self.start_y = self.start_z = 0
self.end_x = self.end_y = self.end_z = 0
self.roi = None
self.offset = 700
self.init_viewer_frame()
def init_viewer_frame(self):
# Image canvas
self.canvas_axial = Canvas(self, bd=0, highlightthickness=0)
self.canvas_axial.grid(row=0, column=0, sticky="nw")
self.canvas_axial.bind("<MouseWheel>", self.scroll_axial_images)
self.canvas_axial.config(width=self.offset)
if self.roi_definition:
self.canvas_axial.bind("<B1-Motion>", self.on_move_press_axial)
self.canvas_axial.bind("<ButtonPress-1>", self.on_button_press_axial)
self.canvas_axial.bind("<ButtonRelease-1>", self.on_button_release)
self.canvas_sagittal = Canvas(self, bd=0, highlightthickness=0)
self.canvas_sagittal.grid(row=0, column=1, sticky="nw")
self.canvas_sagittal.bind("<MouseWheel>", self.scroll_sagittal_images)
if self.roi_definition:
self.canvas_sagittal.bind("<B1-Motion>", self.on_move_press_sagittal)
self.canvas_sagittal.bind("<ButtonPress-1>", self.on_button_press_sagittal)
self.canvas_sagittal.bind("<ButtonRelease-1>", self.on_button_release)
# Status bar
self.status_axial = StatusBar(self)
self.status_axial.grid(row=3, column=0, sticky="w")
self.status_sagittal = StatusBar(self)
self.status_sagittal.grid(row=3, column=1, sticky="w")
self.canvas_axial.bind('<Motion>', self.motion_axial)
self.canvas_sagittal.bind('<Motion>', self.motion_sagittal)
def on_button_press_axial(self, event):
self.canvas_axial.delete(self.selection_axial)
self.canvas_sagittal.delete(self.selection_sagittal)
# save mouse drag start position
self.start_x = event.x
self.start_y = event.y
self.selection_axial = self.canvas_axial.create_rectangle(self.end_x, self.end_y, 0, 0, outline="green")
self.selection_sagittal = self.canvas_sagittal.create_rectangle(0, self.start_x, self.arr_sagittal.shape[1],
self.end_x, outline="green")
def on_button_press_sagittal(self, event):
self.canvas_sagittal.delete(self.selection_sagittal)
# save mouse drag start position
self.start_z = event.x
self.selection_sagittal = self.canvas_sagittal.create_rectangle(self.start_z, self.start_x, 0,
self.end_x, outline="green")
def on_move_press_axial(self, event):
curX, curY = (event.x, event.y)
self.end_x = curX
self.end_y = curY
self.motion_axial(event)
# expand rectangle as you drag the mouse
self.canvas_axial.coords(self.selection_axial, self.start_x, self.start_y, curX, curY)
self.canvas_sagittal.coords(self.selection_sagittal, 0, self.start_x, self.arr_sagittal.shape[1], curX)
def on_move_press_sagittal(self, event):
curZ = event.x
self.end_z = curZ
self.motion_sagittal(event)
# expand rectangle as you drag the mouse
self.canvas_sagittal.coords(self.selection_sagittal, self.start_z, self.start_x, curZ, self.end_x)
def on_button_release(self, event):
roi_axial = self.canvas_axial.bbox(self.selection_axial)
roi_sagittal = self.canvas_sagittal.bbox(self.selection_sagittal)
self.roi = ((roi_axial[0], roi_axial[1], roi_sagittal[0]), (roi_axial[2], roi_axial[3], roi_sagittal[2]))
def show_image(self, array_axial, index_axial, array_sagittal, index_sagittal):
self.upper = int(self.parent.pcd_preparer.get_current_upper().get())
self.lower = int(self.parent.pcd_preparer.get_current_lower().get())
if array_axial is None:
return
if array_sagittal is None:
return
# Convert numpy array into a PhotoImage and add it to canvas
self.image_ax = PIL.Image.fromarray(array_axial)
self.photo_ax = PIL.ImageTk.PhotoImage(self.image_ax)
self.image_sag = PIL.Image.fromarray(array_sagittal)
self.photo_sag = PIL.ImageTk.PhotoImage(self.image_sag)
self.canvas_axial.delete("IMG")
self.canvas_axial.create_image(0, 0, image=self.photo_ax, anchor=NW, tags="IMG")
self.canvas_axial.create_text(40, 10, fill="green", text="Slice " + str(index_axial), font=10)
self.canvas_axial.create_text(40, 40, fill="green", text="Axial", font=10)
self.canvas_sagittal.delete("IMG")
self.canvas_sagittal.create_image(0, 0, image=self.photo_sag, anchor=NW, tags="IMG")
self.canvas_sagittal.create_text(40, 10, fill="green", text="x = " + str(index_sagittal), font=10)
self.canvas_sagittal.create_text(40, 40, fill="green", text="Sagittal", font=10)
width_ax = self.image_ax.width
height_ax = self.image_ax.height
width_sag = self.image_sag.width
height_sag = self.image_sag.height
self.canvas_axial.configure(width=width_ax, height=height_ax)
self.canvas_sagittal.configure(width=width_sag, height=height_sag)
# We need to at least fit the entire image, but don't shrink if we don't have to
width_ax = max(self.parent.winfo_width(), width_ax)
height_ax = max(self.parent.winfo_height(), height_ax + StatusBar.height)
width_sag = max(self.parent.winfo_width(), width_sag)
height_sag = max(self.parent.winfo_height(), height_sag + StatusBar.height)
# Resize root window and prevent resizing smaller than the image
newsize = "{}x{}".format(width_ax + width_sag, height_ax + StatusBar.height)
self.parent.geometry(newsize)
# self.parent.minsize(width_ax + width_sag, height_ax + height_sag)
if self.selection_axial is not None:
self.selection_axial = self.canvas_axial.create_rectangle(self.start_x, self.start_y, self.end_x,
self.end_y, outline="green")
if self.selection_sagittal is not None:
self.selection_sagittal = self.canvas_sagittal.create_rectangle(self.start_z, self.start_x, self.end_z,
self.end_x, outline="green")
def scroll_sagittal_images(self, e):
self.imager.index_sagittal += int(e.delta / 120)
self.arr_sagittal, self.index_sagittal = self.imager.get_current_sagittal_image(self.upper, self.lower)
self.show_image(self.arr_axial, self.index_axial, self.arr_sagittal, self.index_sagittal)
def scroll_axial_images(self, e):
self.imager.index_axial += int(e.delta / 120)
self.arr_axial, self.index_axial = self.imager.get_current_axial_image(self.upper, self.lower)
self.show_image(self.arr_axial, self.index_axial, self.arr_sagittal, self.index_sagittal)
def change_range(self):
self.arr_axial, self.index_axial = self.imager.get_current_axial_image(self.upper, self.lower)
self.arr_sagittal, self.index_sagittal = self.imager.get_current_sagittal_image(self.upper, self.lower)
self.show_image(self.arr_axial, self.index_axial, self.arr_sagittal, self.index_sagittal)
def set_imager(self, im):
self.imager = im
def motion_axial(self, event):
x, y = event.x, event.y
self.status_axial.set('x = {}, y = {}'.format(x, y))
def motion_sagittal(self, event):
z, y = event.x, event.y
self.status_sagittal.set('y = {}, z = {}'.format(y, z))
Imager:
import numpy as np
class DicomImager:
def __init__(self, datasets):
self.values = None
self.datasets = datasets
self._index_axial = 0
self._index_sagittal = 0
self._window_width = 1
self._window_center = 0
self.size = (int(datasets[0].Rows), int(datasets[0].Columns), len(datasets))
self.spacings = (float(datasets[0].PixelSpacing[0]),
float(datasets[0].PixelSpacing[1]),
float(datasets[0].SliceThickness))
self.axes = (np.arange(0.0, (self.size[0] + 1) * self.spacings[0], self.spacings[0]),
np.arange(0.0, (self.size[2] + 1) * self.spacings[2], self.spacings[2]),
np.arange(0.0, (self.size[1] + 1) * self.spacings[1], self.spacings[1]))
# Load pixel data
self.values = np.zeros(self.size, dtype='int32')
for i, d in enumerate(datasets):
# Also performs rescaling. 'unsafe' since it converts from float64 to int32
np.copyto(self.values[:, :, i], d.pixel_array, 'unsafe')
self.max_value = np.amax(self.values)
self.min_value = np.amin(self.values)
#property
def index_sagittal(self):
return self._index_sagittal
#index_sagittal.setter
def index_sagittal(self, value):
while value < 0:
value += self.size[0]
self._index_sagittal = value % self.size[0]
#property
def index_axial(self):
return self._index_axial
#index_axial.setter
def index_axial(self, value):
while value < 0:
value += self.size[2]
self._index_axial = value % self.size[2]
#property
def window_width(self):
return self._window_width
#window_width.setter
def window_width(self, value):
self._window_width = max(value, 1)
#property
def window_center(self):
return self._window_center
#window_center.setter
def window_center(self, value):
self._window_center = value
def get_sagittal_image(self, index, upper, lower):
# int32 true values (HU or brightness units)
img = self.values[index, :, :]
res1 = np.zeros(img.shape)
res1[img < upper] = 1
res1[img < lower] = 0
# Cast to RGB image so that Tkinter can handle it
res = np.zeros((img.shape[0], img.shape[1], 3), dtype=np.uint8)
res[:, :, 0] = res[:, :, 1] = res[:, :, 2] = res1 * 255
return res
def get_axial_image(self, index, upper, lower):
# int32 true values (HU or brightness units)
img = self.values[:, :, index]
res1 = np.zeros(img.shape)
res1[img < upper] = 1
res1[img < lower] = 0
# Cast to RGB image so that Tkinter can handle it
res = np.zeros((img.shape[0], img.shape[1], 3), dtype=np.uint8)
res[:, :, 0] = res[:, :, 1] = res[:, :, 2] = res1 * 255
return res
def get_current_sagittal_image(self, upper, lower):
return self.get_sagittal_image(self._index_sagittal, upper, lower), self._index_sagittal
def get_current_axial_image(self, upper, lower):
return self.get_axial_image(self._index_axial, upper, lower), self._index_axial
The solution in my opinion would therefore be to modify the pixel size of the canvas... But I have not been able to find a way to modify this. Would anyone have an idea?
There is no way to modify the size of a pixel in the canvas. Your only option is to resize the image.

Is there a way to update Tkinter canvas live with text?

I am trying to visualise the backtracking algorithm to solve sudoku puzzles using Tkinter (Example video: https://www.geeksforgeeks.org/building-and-visualizing-sudoku-game-using-pygame/)
def play_puzzle(self):
self.play_frame.pack_forget()
self.home_frame.pack_forget()
self.play_frame.pack(fill=BOTH, expand=1)
self.canvas = Canvas(self.play_frame, width=WIDTH, height=HEIGHT)
self.canvas.grid(row=0, column=0, columnspan=9, rowspan=9)
self.canvas.bind("<Button-1>", self.cell_clicked)
self.canvas.bind("<Key>", self.key_pressed)
solution_btn = ttk.Button(self.play_frame, text='Solution', command=self.solve_puzzle)
home_btn = ttk.Button(self.play_frame, text='Home', command=lambda: self.return_home('play'))
clear = ttk.Button(self.play_frame, text='clear', command = lambda: self.canvas.delete('numbers'))
view_solution_btn = ttk.Button(self.play_frame, text='View Solution', command=self.view_solution)
solution_btn.grid(row= 1, column = 11)
home_btn.grid(row = 3, column = 11)
clear.grid(row=5, column = 11)
view_solution_btn.grid(row=7, column = 11)
self.draw_grid()
self.draw_puzzle()
def view_solution(self):
find = self.game.find_empty()
if not find:
print('Solution found')
return True
else:
e_row, e_col = find
for i in range(1,10):
if self.game.is_valid(i, e_row, e_col):
self.game.puzzle[e_row][e_col] = i
self.play_puzzle()
time.sleep(1)
if self.view_solution():
return True
self.game.puzzle[e_row][e_col] = 0
return False
def draw_puzzle(self):
self.canvas.delete("numbers")
for i in range(9):
for j in range(9):
answer = self.game.puzzle[i][j]
if answer != 0:
x = MARGIN + j * SIDE + SIDE / 2
y = MARGIN + i * SIDE + SIDE / 2
original = self.game.start_puzzle[i][j]
color = "black" if answer == original else "sea green"
self.canvas.create_text(x, y, text=answer, tags="numbers", fill=color)
def draw_grid(self):
for i in range(10):
color = 'blue' if i%3==0 else 'gray'
x0 = MARGIN + i*SIDE
y0 = MARGIN
x1 = MARGIN + i*SIDE
y1 = HEIGHT - MARGIN
self.canvas.create_line(x0,y0,x1,y1, fill=color)
x0 = MARGIN
y0 = MARGIN + i*SIDE
x1 = WIDTH-MARGIN
y1 = MARGIN + i*SIDE
self.canvas.create_line(x0,y0,x1,y1, fill=color)
When I call the view_solution function in the above snippet(by clicking the view solution button), it doesn't update the canvas every time it runs but outputs the answer/fills the puzzle with solution after it completes the entire loop. Is there a way that I could make this work like the one in the video shown?
I have tried using .after() function in Tkinter but I am not sure how to implement it perfectly.
Entire code here - https://github.com/ssram4298/sudoku_gui_tkinter
There are a few ways to update the canvas while processing it.
root.update() #self.parent.update() in your code
root.update_idletasks() #self.parent.update_idletasks() in your code
You can also update individual widgets by calling update() on them (myButton.update()).
If you need to process a change on a widget it needs to be updated before it will be rendered.

Efficiently create a grid in tkinter

I am currently using the following code to create a grid the size of the window within python using the tkinter module
import tkinter as tk
class Frame():
def __init__(self, *args, **kwargs):
# Setup Canvas
self.c = tk.Canvas(root, height=500, width=500, bg='white')
self.c.pack(fill=tk.BOTH, expand=True)
self.c.bind('<Configure>', self.createGrid)
self.pixel_width = 20
self.pixel_height = 20
# Setup binds
self.c.bind("<ButtonPress-1>", self.leftClick)
def leftClick(self, event):
items = self.c.find_closest(event.x, event.y)
if items:
rect_id = items[0]
self.c.itemconfigure(rect_id, fill="red")
def createGrid(self, event=None):
for x in range(0, self.c.winfo_width()):
for y in range(0, self.c.winfo_height()):
x1 = (x * self.pixel_width)
x2 = (x1 + self.pixel_width)
y1 = (y * self.pixel_height)
y2 = (y1 + self.pixel_height)
self.c.create_rectangle(x1,y1,x2,y2)
self.c.update()
root = tk.Tk()
gui = Frame(root)
root.mainloop()
If I set the canvas height and width to something like 50 this loads quite quickly, although when the size is increased to 500 x 500 like is set here it takes about 5 seconds to create the grid. I have tried creating the grid with lines but the problem with that is I need squares as I am then planning to change the colour of a square that is selected. Is there any way I can make this more efficient?
I think you created way more rectangles than you need. The below two lines:
for x in range(0, self.c.winfo_width()):
for y in range(0, self.c.winfo_height()):
Will create 504x504 rectangles = 254016. It will work fine if you reduce it to just fill your current screen:
def createGrid(self, event=None):
for x in range(0, int(self.c.winfo_width()/20+1)):
for y in range(0, int(self.c.winfo_height()/20+1)):
x1 = (x * self.pixel_width)
x2 = (x1 + self.pixel_width)
y1 = (y * self.pixel_height)
y2 = (y1 + self.pixel_height)
self.c.create_rectangle(x1,y1,x2,y2)
self.c.update()

Generator for placing images on tkinter canvas

I have a list of paths to around 90 images, now I want to place all of them on the canva,but only let's say 30 in one "row", but if I use
from tkinter import *
def createCanvaImages(paths):
paths = ['list with the paths']
mainWin = Tk()
canva = Canvas(mainWin, width = 900, height = 300).pack()
for x in range(0, len(paths),):
if x <= 30: #not sure if this places only 30 in one row
y=x/3
elif x > 30
y=(x+24)/3
elif x >= 60
y=(x+48)/3
img = PhotoImage(file = paths[x])
canva.create_image(x+24, y, image = img)
mainWin.mainloop()
it only shows the image from the last path
EDIT
now shows all images on the canvas if the canvas isn't in a frame(thanks to Novel) but doesn't work if the canva is in a frame
from tkinter import *
def createImagePaths(dct):
paths=[]
for i in range(len(masteries)):
if dct.get(masteries[i]) == 0:
file = masteries[i]+'.png'
path = os.path.join(path_gray, file)
paths.append(path)
#create canvas image fnc
if dct.get(masteries[i]) != 0:
file = masteries[i]+'.png'
path = os.path.join(path_colored, file)
paths.append(path)
return createCanvaImages(paths)
def createCanvaImages(paths):
img_refs = []
canva = Canvas(masteryFrame, height = 400).pack()
for i, path in enumerate(paths):
col,row = divmod(i,30)
img = PhotoImage(file=path)
canva.create_image( row*24, col*24, image = img, anchor = 'nw')
img_refs.append(img)
root = Tk()
mainFrame = Frame(root)
mainFrame.grid(column=0,row=0, sticky=(N,W,E,S))
masteryFrame = Frame(root)
masteryFrame.grid(row=1,column=0, sticky=(N,W,E,S))
root.mainloop()
You need to save your image references. The easiest way to do that is to just add them to a list. As a guess:
from tkinter import *
def createCanvaImages(paths):
canva = Canvas(masteryFrame, width = 900, height = 300)
canva.pack()
canva.img_refs = []
for i, path in enumerate(paths):
row, col = divmod(i, 30)
img = PhotoImage(file = path)
canva.create_image(col*24, row*24, image = img, anchor='nw') # each image is 24x24
canva.img_refs.append(img)
Also, make sure you never put a widget initialization and layout on the same line. IOW, don't ever do this: Widget(master).pack(). Always put those on separate lines.
You should also learn about OOP and classes very soon. Using functions to build the UI like this will get very messy and buggy very quickly.

Categories