I want to create two frames next to each other with widgets in a mainframe, but I don't know how to make them both clickable, and I'm sure my code can be cleaned up. Any help is appreciated!
I started off with just making a single frame with widgets and than go from there, but my main problem is that the list constructor can only take one argument. I want to be able to use the self.squares variable for both frames but I'm not sure how to do that so I set up another square dictionary for the second frame but I think that makes things unnecessarily long. Here is my code below. The #here is where I'm having the most trouble.
class Board(tk.Frame):
def __init__(self, parent, length, width): # self=Frame, parent=root
tk.Frame.__init__(self, parent)
self.parent = parent
self.length = length
self.width = width
self.config(height=100 * self.length, width=100 * self.width)
self.frame1 = tk.Frame()
self.frame2 = tk.Frame()
self.pack()
self.squares = {}
self.squares2 = {}
self.ranks = string.ascii_lowercase
self.buttons_pressed = 0
self.turns = 0
self.sq1 = None # first square clicked
self.sq2 = None
self.sq1_button = None # button associated with the square clicked
self.sq2_button = None
self.set_squares()
def select_piece(self, button):
if self.buttons_pressed == 0:
self.sq1 = list(self.squares.keys())[list(self.squares.values()).index(button)] #here
self.sq1_button = button
self.buttons_pressed += 1
elif self.buttons_pressed == 1: # stores square and button of second square selected
self.sq2 = list(self.squares.keys())[list(self.squares.values()).index(button)]
self.sq2_button = button
if self.sq2 == self.sq1: # prevents self-destruction and allows the user to choose a new piece
self.buttons_pressed = 0
return
if True:
self.squares[self.sq2].config(image=self.sq1_button["image"])
self.squares[self.sq2].image = self.sq1_button["image"]
self.squares[self.sq1].config(image=self.white_images["blank.png"]) # clears sq1
self.squares[self.sq1].image = self.white_images["blank.png"]
self.buttons_pressed = 0
return
def set_squares(self): # fills frame with buttons representing squares
for x in range(5):
for y in range(5):
b = tk.Button(self.frame1, bg=self.square_color, activebackground="lawn green")
b.grid(row=8 - x, column=y)
b2 = tk.Button(self.frame2, bg=self.square_color, activebackground="lawn green")
b2.grid(row=8 - x, column=y)
pos = self.ranks[x] + str(y + 1)
self.squares.setdefault(pos, b,) # creates list of square positions
self.squares2.setdefault(pos, b2)
self.squares[pos].config(command=lambda key=self.squares[pos]: self.select_piece(key))
self.squares2[pos].config(command=lambda key=self.squares2[pos]: self.select_piece(key))
def set_pieces(self): # places pieces in starting positions
dict_rank1_pieces = {"a1": "dirt.png", "b1": "fire.png", "c1": "metal.png", "d1": "water.png", "e1": "wood.png"}
for key in dict_rank1_pieces: # inserts images into buttons
starting_piece = dict_rank1_pieces[key]
self.squares[key].config(image=self.white_images[starting_piece])
self.squares[key].image = self.white_images[starting_piece]
self.squares2[key].config(image=self.white_images[starting_piece])
self.squares2[key].image = self.white_images[starting_piece]
self.frame1.pack(side='left')
self.frame2.pack(side='right')
for rank in range(2, 6): # fill rest with blank pieces
for file in range(5):
starting_piece = "blank.png"
pos = self.ranks[file] + str(rank)
self.squares[pos].config(image=self.white_images[starting_piece])
self.squares[pos].image = self.white_images[starting_piece]
self.squares2[pos].config(image=self.white_images[starting_piece])
self.squares2[pos].image = self.white_images[starting_piece]
root = tk.Tk()
root.geometry("800x800")
board = Board(root, 5, 5)
board.import_pieces()
board.set_pieces()
board.mainloop()
If you want to use the same self.squares
for both the frames, you need to make the keys (currently they are something like "a1", "b1", etc) unique. One of the suggestion is to use a tuple of (idx, key)
instead as the new key:
idx
is 0 for the first frame, 1 for the second framekey
is the current key used, i.e. "a1", "b1", etcBelow is the required modifications:
class Board(tk.Frame):
...
def set_squares(self): # fills frame with buttons representing squares
for x in range(5):
for y in range(5):
pos = self.ranks[x] + str(y + 1)
for i, frame in enumerate((self.frame1, self.frame2)):
b = tk.Button(frame, bg=self.square_color, activebackground="lawn green")
b.grid(row=8 - x, column=y, sticky="nsew")
b.config(command=lambda key=b: self.select_piece(key))
# use tuple as key
self.squares[i,pos] = b
def set_pieces(self): # places pieces in starting positions
dict_rank1_pieces = {"a1": "dirt.png", "b1": "fire.png", "c1": "metal.png", "d1": "water.png", "e1": "wood.png"}
for key in dict_rank1_pieces: # inserts images into buttons
starting_piece = dict_rank1_pieces[key]
# use tuple as key
self.squares[0,key].config(image=self.white_images[starting_piece])
self.squares[1,key].config(image=self.white_images[starting_piece])
# suggest to move below two lines to __init__()
self.frame1.pack(side='left')
self.frame2.pack(side='right')
for rank in range(2, 6): # fill rest with blank pieces
for file in range(5):
starting_piece = "blank.png"
pos = self.ranks[file] + str(rank)
# use tuple as key
self.squares[0,pos].config(image=self.white_images[starting_piece])
self.squares[1,pos].config(image=self.white_images[starting_piece])
...
Note that currently you have created the two frames with buttons as children of root window, not Board
. Is it what you want actually?
Updated set_pieces()
to put images at the very left column of left frame and the very right column of right frame:
def set_pieces(self): # places pieces in starting positions
dict_rank_pieces = {"a": "dirt.png", "b": "fire.png", "c": "metal.png", "d": "water.png", "e": "wood.png"}
blank_piece = "blank.png"
for rank in range(1, 6): # fill rest with blank pieces
for file in range(5):
starting_piece = dict_rank_pieces[self.ranks[file]]
pos = self.ranks[file] + str(rank)
self.squares[0,pos].config(image=self.white_images[starting_piece if rank == 1 else blank_piece])
self.squares[1,pos].config(image=self.white_images[starting_piece if rank == 5 else blank_piece])
self.frame1.pack(side='left')
self.frame2.pack(side='right')