Why Is My Function Not Affecting A Button In Tkinter?
Solution 1:
The argument to playerCheck()
is a button, not a function. You should change the button's text and color, not try to call it.
defplayerCheck(button):
global turn
button['text'] = turn
button['fg'] = "white"if turn == Player1:
turn = Player2
else:
turn = Player1
Solution 2:
Here is an example of a completely working tic-tac-toe made with tkinter
. I used your Button
approach. If you are going to make anything with tkinter
it should look a lot more like the below code. I commented it all over the place to give you a bit of a guide to what everything is doing, but I did not dumb down the code. Some parts may be very hard for you to understand (like the logic in win_or_block()
). That's a good thing. It will force you to research and get better.
tips:
- Learn how to create and use classes, so you can separate and organize your code into meaningful chunks.
- Your approach is to manually define each individual thing, even if they are the exact same thing. You should consider a more dynamic approach where you create something once and let a loop create and position a bunch of copies/instances of it.
- If your code was written properly, according to the design that you laid out, you would technically have a
Button
that recreates itself when it is clicked. This type of circular logic is bad. - Worry less about
Buttons
and more about game logic. Your graphics should simply express a condition. You're on your way to making the graphics serve as the logic behind your conditions, and that is a terrible approach.
#python 3.8+from tkinter import Tk, Frame, Button, Entry, StringVar, IntVar, Checkbutton
from statistics import mean
from random import choice
classSplash(Frame):
WIDTH = 300
HEIGHT = 190def__init__(self, master, callback, *args, **kwargs):
Frame.__init__(self, master, *args, **kwargs)
self.callback = callback
self.message = StringVar()
self.tricky = IntVar()
#just to make some lines shorter
e = dict(font='calibri 18 bold', bg=self['bg'], border=0, justify='center', textvariable=self.message)
b = dict(font='calibri 18 bold', bg='black', fg='white')
c = dict(font='calibri 16 bold', bg=self['bg'])
Entry(self, **e).place(width=280, height=40, x=10, y=5)
Checkbutton(self, text="tricky", variable=self.tricky, **c).place(width=200, height=30, x=50, y=50)
Button(self, text='1 Player' , command=lambda: self.remove(True) , **b).place(width=200, height=45, x=50, y=90)
Button(self, text='2 Players', command=lambda: self.remove(False), **b).place(width=200, height=45, x=50, y=140)
defremove(self, auto):
self.place_forget()
self.callback(auto)
classBoard(Frame):
#if you change this you need to change the font size in self.default_cell and vise-versa
WIDTH = 450
HEIGHT = 393def__init__(self, master, callback, *args, **kwargs):
Frame.__init__(self, master, *args, **kwargs)
#cell default properties
self.default_cell = dict(
foreground = 'black',
background = 'black',
activebackground = 'black',
disabledforeground = 'black',
text = "",
font = "consolas 48 bold",
relief = 'sunken',
state = 'normal',
width = 4,
height = 1,
)
#make boardfor i inrange(9):
cell = Button(self, **self.default_cell)
cell.config(command=lambda c=cell, i=i: callback(c, i))
cell.grid(row=int(i//3), column=i%3, sticky='nswe')
defreset(self):
for cell in self.winfo_children():
cell.config(**self.default_cell)
#used to stop cell clicks while splash screen is visibledeflock(self):
for cell in self.winfo_children():
cell.config(state='disabled')
classGame(Tk):
def__init__(self, *args, **kwargs):
Tk.__init__(self, *args, **kwargs)
#current player
self.player = 0#True: against computer - False: against 2nd human
self.auto = False#stores moves
self.moves = [0]*9#player colors
self.color = ['red', 'green']
# [crossed swords, flag outline]
self.marks = [chr(9876), chr(9872)]
#positions
self.edge = {1, 7, 3, 5}
self.corner = {0, 6, 2, 8}
#True = hard | False = easy
self.tricky = True'''
numbers that can't average together with 0 to equal each other
where [n, n, n] is any given winning combination in tic-tac-toe
consider the mean of: [1, 2, 0] vs [1, 1, 1]
against the mean of: [1, 4, 0] vs [1, 1, 1]
'''
self.pids = [1, 4]
#init board
self.board = Board(self, self.move)
self.board.grid(row=0, column=0, sticky='nswe')
#init "game over" screen and place() properties
self.splash = Splash(self, callback=self.reset, bg='gray92')
self.splashprops = dict(
x = (Board.WIDTH-Splash.WIDTH)/2,
y = (Board.HEIGHT-Splash.HEIGHT)/2,
width = Splash.WIDTH,
height = Splash.HEIGHT
)
self.show_splash("Connect 3 And Conquer")
'''
This method is tricky. It is used to check:
1: if the current player has won (id=current_id, n=4)
2: if the computer can win (id=player2_id, n=3)
3: if the computer can block a win (id=player1_id, n=3)
'''defwin_or_block(self, id, n=3):
best = -1try:
for a inrange(3):
#vertical
m = self.moves[a::3]
l = list(filter(lambda i: i>0, m))+[id]
iflen(l) == n and mean(l) == id:
best = (m.index(0) * 3) + a
break#horizontal
m = self.moves[a*3:a*3+3]
l = list(filter(lambda i: i>0, m))+[id]
iflen(l) == n and mean(l) == id:
best = m.index(0) + (a * 3)
break#back slash if best < 0:
m = self.moves[0::4]
l = list(filter(lambda i: i>0, m))+[id]
iflen(l) == n and mean(l) == id:
best = m.index(0) * 4#forward slash if best < 0:
m = self.moves[2::2][0:3]
l = list(filter(lambda i: i>0, m))+[id]
iflen(l) == n and mean(l) == id:
best = (m.index(0) + 1) * 2except ValueError:
#m.index(0) does not exist, current player has won
best = 0return best
defbest(self, index):
best = -1#computer checks if it can win, and if not, then if it can blockfor i inrange(1, -1, -1):
best = self.win_or_block(self.pids[i])
if best > -1:
break#if the computer cannot win or there is nothing to blockif best < 0:
avail = {i for i, v inenumerate(self.moves) if v == 0}
c = list(self.corner.intersection(avail)) #corners
e = list(self.edge.intersection(avail)) #edges#choose the middle or mirror opposite of the last moveif self.tricky:
b = 4if4in avail elseabs(index - 8)
if b in avail:
best = b
#attempt a random choice in this order: corner, center, edgeif best < 0:
iflen(c):
best = choice(c)
elif4in avail:
best = 4else:
best = choice(e)
return best
defmove(self, button, index):
#mark the square
button.config(bg=self.color[self.player], text=self.marks[self.player], state='disabled')
#record the move
self.moves[index] = self.pids[self.player]
#game over screensif self.win_or_block(self.pids[self.player], 4) > -1: #check win
self.show_splash(f'Player {self.player+1} Has Conquered!')
returnelif self.moves.count(0) == 0: #check "no more moves"
self.show_splash('Stalemate!')
return#derive next player ~ Player1 = 0, Player2 = 1
self.player = (self.player + 1)%2if self.auto and self.player == 1:
#invoke the button with the child index that corresponds to the "best" move
self.board.winfo_children()[self.best(index)].invoke()
defshow_splash(self, msg):
self.board.lock()
self.splash.message.set(msg)
self.splash.place(**self.splashprops)
defreset(self, auto):
self.tricky = bool(self.splash.tricky.get())
self.auto = auto
self.player = 0
self.moves = [0]*9
self.board.reset()
if __name__ == '__main__':
app = Game()
app.title("Connect 3 And Conquer")
#lock down the window size
app.minsize(Board.WIDTH, Board.HEIGHT)
app.maxsize(Board.WIDTH, Board.HEIGHT)
app.mainloop()
aside:
I understand you are new. Don't take my criticisms to heart. The point is to make you better. Anybody could come in here and tell you how to make poor code work. My agenda is to stop you from writing poor code in the first place - by giving you an excellent example and explaining where you went wrong.
Cheers
Post a Comment for "Why Is My Function Not Affecting A Button In Tkinter?"