0

I'm writing a simple little slapjack game to get acquainted with Python and Tkinter. I've got a button for the player to take their turn, but I'm stuck on how to get the computer to take its turn.

This will have the computer take its turn, but does not update the text on lbl_btn until deal() returns, meaning that I never see the card that the human played:

lbl_btn = Button(root, textvariable=varCard, command=lambda: slap()).pack()
deal_btn = Button(root, text="deal", command=lambda: deal()).pack()

def deal():
    varTurn.get() % 2 == 0
    pile.append(human.deck[0])
    human.deck.remove(human.deck[0])
    update_label()
    root.after(2000)
    pile.append(compy.deck[0])
    compy.deck.remove(compy.deck[0])
    update_label()


def update_label():
    varTurn.set(varTurn.get()+1)
    print(pile[len(pile)-1].name)
    varCard.set(pile[len(pile)-1].name)

This version of deal() shows the correct label after each button click, but is non-optimal because the computer's turn requires a separate click:

def deal():
if varTurn.get() % 2 == 0:
    pile.append(human.deck[0])
    human.deck.remove(human.deck[0])
else:
    pile.append(compy.deck[0])
    compy.deck.remove(compy.deck[0])
varTurn.set(varTurn.get()+1)
print(pile[len(pile)-1].name)
varCard.set(pile[len(pile)-1].name)

I know that the label button isn't updating until my lambda completes, but in that case, how can I create a separate call to deal after a delay that will result in updating the value of lbl_btn?

nwhaught
  • 1,562
  • 1
  • 15
  • 36

1 Answers1

3

If I understand what you're asking, you might be looking for Tk.update(). If you call root.update() right before root.after(2000), this will do a force refresh and cause Tkinter to update the UI.

As an example (I modified your code):

from Tkinter import *

deck1 = ['A', 'B', 'C']
deck2 = ['D', 'E', 'F']
pile = []

def deal():
    varTurn.get() % 2 == 0
    pile.append(deck1[0])
    deck1.remove(deck1[0])
    update_label()
    root.update()
    root.after(2000)
    pile.append(deck2[0])
    deck2.remove(deck2[0])
    update_label()


def update_label():
    varTurn.set(varTurn.get()+1)
    print(pile[len(pile)-1])
    varCard.set(pile[len(pile)-1])

root = Tk()
varCard = StringVar()
varTurn = IntVar()
lbl_btn = Button(root, textvariable=varCard, command=lambda: slap()).pack()
deal_btn = Button(root, text="deal", command=lambda: deal()).pack()
root.mainloop()

This causes the top button text to be refreshed immediately after the new card is drawn. Note that this ultimately might not be the best way to implement your game because even though the button updates to display the new card, the GUI will be unresponsive while waiting for the deal() function to complete.

Jason S
  • 129
  • 3
  • Correct on both counts. This does produce the updating behavior that I was looking for, and does also make the button unresponsive until deal() returns. Any thoughts on how to restructure? I've thought about multithreading the computer's turn and the "slap" mechanism (which will be another first for me...) – nwhaught Jul 10 '17 at 03:22
  • You may want to consider adding a second parameter to your call to after(). Look at this question [here](https://stackoverflow.com/questions/25753632/tkinter-how-to-use-after-method). This way, you could separate your turn deal logic into two functions and call the computer deal function after the same two seconds, but you would be giving control back to the tkinter main loop during the wait. Keep in mind you would then have to have handle what happens if the user clicks deal again before the two seconds expires, so it gets a bit more complicated. Let me know if this helps. – Jason S Jul 10 '17 at 14:00