Reputation: 2661
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?
Upvotes: 0
Views: 359
Reputation: 2244
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)
Upvotes: 1