How to use recursion with square? - python

I have a question how can you recursively draw a rectangle with the use of fractional coordinates? I have been trying to make the squares/rectangles get smaller by 10% each time until it either takes up the entire canvas(meaning it cannot go beyond the width and height of the board or it just get's to 0 meaning it won't draw anything or it's so small you won't see the final square). Or maybe is there another way that I can go about drawing squares in a recursive way that keeps decreasing by a certain percent?
I have this so far but I can't seem to think of who to get it to continue to recurse until it can no longer do it.
import tkinter
class draw_squares:
def __init__(self, squares: [(float, float, float, float)]):
self.squares = squares
self.root_window = tkinter.Tk()
self.canvas = tkinter.Canvas(master = self.root_window,
width = 500, height = 500,
background = 'blue')
self.canvas.grid(row=0, column = 0, padx=0, pady = 0,
sticky = tkinter.E + tkinter.W + tkinter.S + tkinter.N)
self.canvas.bind('<Configure>', self._resized)
self.root_window.rowconfigure(0, weight = 1)
self.root_window.columnconfigure(0, weight = 1)
def start(self):
self.root_window.mainloop()
def _resized(self, event: tkinter.Event):
self.draw_squares_recursively()
def draw_squares_recursively(self):
self.canvas.delete(tkinter.ALL)
for x, y, x2, y2 in self.squares:
if x != 0.5 and y != 0.5 and x2 != 0.5 and y2 != 0.5:
self._draw_square(self._draw_square(x-.1,y-.1,x2+.1,y2+.1))
def _draw_square(self, x: float, y:float, x2:float, y2:float):
width = self.canvas.winfo_width()
height = self.canvas.winfo_height()
self.canvas.create_rectangle(
x * width, y * height,
x2 * width, y2 *height, outline = 'grey')
if __name__ == '__main__':
squares = [(0.9, 0.9, 0.1, 0.1), (0.8,0.8,0.2,0.2), (0.7,0.7,0.3,0.3),
(0.6,0.6,0.4,0.4), (0.5,0.5,0.5,0.5)]
app = draw_squares(squares)
app.start()
Thank you very much in advance for the help!

First, you are using floating point numbers, so (not) equal is not a good idea http://www.lahey.com/float.htm draw_squares_recursively() can call itself until the limit is reached. You can work out the arithmetic to get the sizes and locations you want.
class DrawSquares:
def __init__(self, square):
## self.squares = squares
self.root_window = tkinter.Tk()
self.canvas = tkinter.Canvas(master = self.root_window,
width = 500, height = 500,
background = 'blue')
self.canvas.grid(row=0, column = 0, padx=0, pady = 0,
sticky = "nsew")
## self.canvas.bind('<Configure>', self._resized)
self.root_window.rowconfigure(0, weight = 1)
self.root_window.columnconfigure(0, weight = 1)
self.draw_squares_recursively(square)
self.root_window.mainloop()
def draw_squares_recursively(self, square):
##self.canvas.delete(tkinter.ALL)
for value in square:
if value < 0.05 or value > 100:
return
self.canvas.create_rectangle(square[0], square[1],
square[2], square[3],
outline = 'grey', width=2)
square[2] += 20
square[3] += 20
self.draw_squares_recursively(square)
if __name__ == '__main__':
squares = [0.9, 0.9, 10.1, 10.1]
app = DrawSquares(squares)

Related

how to design a board in tkinter canvas

I want to design a 9x9 board in tkinter canvas. Each rectangle should have a width and height of 30 (pixels?). Do I always have to use the pixel coordinates for drawing shapes onto the canvas or is there a more relative way possible? For example, my board looks like this:
class TkCanvas(tk.Canvas):
RECT_WIDTH = 30
def __init__(self, parent, width=600, height=600, columns=9, rows=9):
super().__init__(parent, width=width, height=height)
self.columns=columns
self.rows=rows
self.board = [[None for col in range(columns)] for row in range(rows)]
def draw_board(self, x1=0, x2=0,y1=RECT_WIDTH,y2=RECT_WIDTH):
for col in range(self.columns):
for row in range(self.rows):
x1 = col * self.RECT_WIDTH
y1 = (self.rows-1-row) * self.RECT_WIDTH
x2 = x1 + self.RECT_WIDTH
y2 = y1 + self.RECT_WIDTH
tag = f"tile{col}{row}"
self.board[row][col] = self.create_rectangle(x1, y1, x2, y2, fill="white", tags=tag, outline="black")
self.tag_bind(tag,"<Button-1>", lambda e, i=col, j=row: self.get_location(e,i,j))
def get_location(self, event, i, j):
print (i, j)
def get_x_coord(self, x):
return x * self.RECT_WIDTH
def get_y_coord(self, y):
return y * self.RECT_WIDTH
Now when I want to draw a shape I get the exact coordinates x0,y0 first with get_x_coord and get_y_coord and then calculate x1 and y1 by adding the RECT_WIDTH.
Is there a cleaner way to draw the shapes onto the canvas? Something where I would only have to pass in the coordinates, eg. (4,5) and it would automatically draw it in the right rectangle or do I always have to make these calculations?
There are many ways to produce a grid board and yours works fine.
Using relative offsets to position squares and pieces is easy in tkinter Canvas,
just use canvas.move(itemID, xoffset, yoffset). You can also move or scale the
entire board by using canvas.addtag_all('somename') then canvas.scale('somename', 0, 0, s, s). Where s is a float s > 0
The following code creates class drawBoard that can be instantiated using
just two values or by using many control values and demonstrates how to use
relative coordinates to build a scalable board.
The board is created by drawing all rectangles at (0, 0, w, h) then moving them
to location via relative values (x, y).
Once all squares have been created the entire board is scaled to size.
Method get_location displays user input.
import tkinter as tk
back, fore, draw, wall, light = "white", "blue", "red", "black", "yellow"
class drawBoard(tk.Tk):
def __init__(
self, w, h, columns = 9, rows = 9, scale = 1, line = 1, border = 1):
super().__init__()
self.withdraw()
self.configure(background = light, borderwidth = border)
# pre calculate sizes and set geometry
self.store = dict()
# small change to w, h and wide, high
x, y, w, h = 0, 0, w + line, h + line
wide = w * columns * scale + (line==1)
high = h * rows * scale + (line==1)
self.geometry(f"{wide + border * 2 }x{high + border * 2}")
# minimal Canvas
self.canvas = tk.Canvas(
self, width = wide, height = high, background = back,
borderwidth = 0, highlightthickness = 0, takefocus = 1)
self.canvas.grid(sticky = tk.NSEW)
# draw the board
for row in range(rows):
for column in range(columns):
item = self.canvas.create_rectangle(
0, 0, # removed line, line
w, h, width = line,
fill = back, outline = fore)
self.canvas.move(item, x, y)
self.store[item] = (row, column)
x = x + w
x, y = 0, y + h
# tag all items and scale them
self.canvas.addtag_all("A")
self.canvas.scale("A", 0, 0, scale, scale)
# bind user interaction
self.bind("<Button-1>", self.get_location)
self.after(500, self.complete)
def complete(self):
self.resizable(0, 0)
self.deiconify()
def get_location(self, ev):
# find user selection
item = self.canvas.find_closest(
self.canvas.canvasx(ev.x), self.canvas.canvasy(ev.y))[0]
# flip color for demo
fill = self.canvas.itemcget(item, "fill")
self.canvas.itemconfig(item, fill = [draw, back][fill == draw])
# extract and display info
row, column = self.store[item]
x, y, w, h = self.canvas.coords(item)
print(f"{row}, {column} >> {x}, {y}, {w-x}, {h-y}")
if True:
# the easiest way
app = drawBoard(30, 30)
else:
# Or with lots of control
app = drawBoard(
30, 30, columns = 40, rows = 20, scale = 1, line = 1, border = 1)

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.

Is there a way to centre a canvas widget in the window?

I am building a chess program using Tkinker/Python. I am trying to align the board in the centre of the window so that I can have the taken pieces placed alongside the board. Any ideas?
from tkinter import *
root=Tk()
class Board():
def drawboard():
dark="#643c22"
light="#faeac6"
canvas=Canvas(root, width=920, height=720,)
canvas.pack( fill=BOTH)
colour=light
for row in range(8):
if colour==dark:
colour=light
else:
colour=dark
for column in range(8):
x1 = (column * 90)
y1 = ((7-row)* 90)
x2 = x1 + 90
y2 = y1 + 90
canvas.create_rectangle(x1, y1, x2, y2, fill=colour)
if colour==dark:
colour=light
else:
colour=dark
Board.drawboard()
root.mainloop()
I expect it to lined up in the centre but it is aligned to the left.
The class you created is simply a container for some functions... You probably need to read a little bit about object oriented python, and get acquainted with its specifics. I rewrote your class Board as an example; it inherits from tk.Tk and therefore is a tkinter root.
As far as the placement of the various widgets, I added a right and left frame in order to center the canvas representing the checkers board.
it comes like this:
import tkinter as tk
class Board(tk.Tk):
colours = ["#643c22", "#faeac6"]
def __init__(self, n=8):
super().__init__()
self.n = n
self.left_frame = tk.Frame(self)
self.left_frame.grid(row=0, column=0, rowspan=8, padx=100)
self.right_frame = tk.Frame(self)
self.right_frame.grid(row=0, column=10, rowspan=8)
self.canvas = tk.Canvas(self, width=920, height=720, )
self.canvas.grid(row=0, column=1, columnspan=8, rowspan=8)
self.board = [[None for row in range(n)] for col in range(n)]
self.current_colour_ndx = 0
def _swap_colours(self):
self.current_colour_ndx = (self.current_colour_ndx + 1) % 2
def drawboard(self):
for col in range(self.n):
self._swap_colours()
for row in range(self.n):
x1 = col * 90
y1 = (7-row) * 90
x2 = x1 + 90
y2 = y1 + 90
colour = self.colours[self.current_colour_ndx]
self.board[row][col] = self.canvas.create_rectangle(x1, y1, x2, y2, fill=colour)
self._swap_colours()
if __name__ == '__main__':
board = Board()
board.drawboard()
board.mainloop()

How to add image in tkinter gui?

I picked up this code and wanted to mess with it, the main problem i'm having is being unable to add an image to the actual gui at a set location on the 2 dimensional array. I get no actual error but also get no output of the image on the gui. Please help! Thank you.
import tkinter as tk
class GameBoard(tk.Frame):
def __init__(self, parent, rows=9, columns=9, size=60, color1="light grey", color2="light grey"):
'''size is the size of a square, in pixels'''
self.rows = rows
self.columns = columns
self.size = size
self.color1 = color1
self.color2 = color2
self.pieces = {}
canvas_width = columns * size
canvas_height = rows * size
tk.Frame.__init__(self, parent)
self.canvas = tk.Canvas(self, borderwidth=0, highlightthickness=0,
width=canvas_width, height=canvas_height, background="white")
self.canvas.pack(side="top", fill="both", expand=True, padx=2, pady=2)
self.canvas.bind("<Configure>", self.refresh)
def addpiece(self, name, image, row=1, column=1):
bishop = tk.PhotoImage(file='C:\\Users\\Sharjeel Jan\\Desktop\\final shit man\\Pieces\\Bishop.gif')
self.canvas.create_image(1,1, image=bishop, tags=(name, "Bishop"), anchor= "c")
self.placepiece(name, row, column)
def placepiece(self, name, row, column):
'''Place a piece at the given row/column'''
self.pieces[name] = (row, column)
x0 = (column * self.size) + int(self.size/2)
y0 = (row * self.size) + int(self.size/2)
self.canvas.coords(name, x0, y0)
def placepiece(self, name, row, column):
'''Place a piece at the given row/column'''
self.pieces[name] = (row, column)
x0 = (column * self.size) + int(self.size/2)
y0 = (row * self.size) + int(self.size/2)
self.canvas.coords(name, x0, y0)
def refresh(self, event):
'''Redraw the board, possibly in response to window being resized'''
xsize = int((event.width-1) / self.columns)
ysize = int((event.height-1) / self.rows)
self.size = min(xsize, ysize)
self.canvas.delete("square")
color = self.color2
for row in range(self.rows):
color = self.color1 if color == self.color2 else self.color2
for col in range(self.columns):
x1 = (col * self.size)
y1 = (row * self.size)
x2 = x1 + self.size
y2 = y1 + self.size
self.canvas.create_rectangle(x1, y1, x2, y2, outline="black", fill=color, tags="square")
color = self.color1 if color == self.color2 else self.color2
for name in self.pieces:
self.placepiece(name, self.pieces[name][0], self.pieces[name][1])
self.canvas.tag_raise("piece")
self.canvas.tag_lower("square")
if __name__ == "__main__":
root = tk.Tk()
board = GameBoard(root)
board.pack(side="top", fill="both", expand="true", padx=4, pady=4)
# player1 = tk.PhotoImage(data=imagedata)
# board.addpiece("player1", player1, 0,0)
root.mainloop()
You have a single line that tries to put an image to GUI:
self.canvas.create_image(1,1, image=bishop, tags=(name, "Bishop"), anchor= "c")
which is under addpiece.
The line below saves the image in a reference:
bishop = tk.PhotoImage(file='C:\\Users\\Sharjeel Jan\\Desktop\\final shit man\\Pieces\\Bishop.gif')
which is defined exclusively for the scope of addpiece, when the method is finished, the image disappears if at all being displayed.
Which effectively makes this question a duplicate to Why does Tkinter image not show up if created in a function?
In order to prevent that, make the image reference available in the scope where mainloop is called.
Either attach the reference bishop to an object, for example self.canvas, whose reference is available in the scope of mainloop:
self.canvas.bishop = tk.PhotoImage(file='C:\\Users\\Sharjeel Jan\\Desktop\\final shit man\\Pieces\\Bishop.gif')
self.canvas.create_image(1,1, image=self.canvas.bishop, tags=(name, "Bishop"), anchor= "c")
or simply pass the image reference from the mainloop scope is in, and use that object reference:
self.canvas.create_image(1,1, image=image, tags=(name, "Bishop"), anchor= "c")

Categories