Question

I have problem with drawing cards on canvas. When I press deal button nothing happens. I dont know where am I making mistake. I tried to add .pack() function at the end of all canvas.create_image.... then i get one card and error: 'int' object has no attribute 'pack'

Please help.

This is my code so far:

from Tkinter import *
from PIL import Image, ImageTk
import random

HEIGHT = 860
WIDTH = 1024

CARD_SIZE = (73, 98)


CARD_BACK_SIZE = (71, 96)


DECK_POS = [400,800]
PLAYER_POS = [WIDTH/2,700]
DEALER_POS = [WIDTH/2,90]


player_hand = []
dealer_hand = []
deck = []
in_play = False
outcome = ""
score = 0

SUITS = ['C', 'S', 'H', 'D']
RANKS = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']
VALUES = {'A':1, '2':2, '3':3, '4':4, '5':5, '6':6, '7':7, '8':8, '9':9, '10':10, 'J':10, 'Q':10, 'K':10}



class Cards:
    def __init__(self,suit,rank):
        self.suit = suit
        self.rank = rank  

    def get_suit(self):
        return self.suit

    def get_rank(self):
        return self.rank

    def __str__(self):
        return self.suit + self.rank

    def draw(self,position):
        CARD = Image.open ("C:\Users\Petar\Desktop\cards.png")
        box = [RANKS.index(self.rank) * CARD_SIZE[0], SUITS.index(self.suit) * CARD_SIZE[1], CARD_SIZE[0] * (RANKS.index(self.rank)+1) , CARD_SIZE[1] * (SUITS.index(self.suit)+1)]
        cropped = CARD.crop(box)
        karta = ImageTk.PhotoImage(cropped)
        canvas.create_image(position, image=karta)


class Hand:
    def __init__(self ):
        self.card_list=[]

    def add_card(self,card):
        self.card_list.append(card)

    def get_value(self):
        global VALUES
        self.hand_value = 0
        aces = 0
        for c in self.card_list:
            self.hand_value += VALUES.get(c.get_rank())
            if c.get_rank() == "A":
                aces += 1
        if aces == 0:
            return self.hand_value
        else:
            if self.hand_value +10 <= 21:
                return self.hand_value +10
            else:
                return self.hand_value

    def __str__(self):
        self.hand_str = ""
        for c in self.card_list:
            self.hand_str += str(c)+" "
        return "Hand contains " + self.hand_str


    def draw(self,position):
        pos_h = position
        for c in self.card_list:
            c.draw(pos_h)
            pos_h[0] = pos_h[0] + CARD_SIZE[0] + 3




class Deck:
    def __init__(self):
        self.deck_list = []
        # standard 52 card deck
        global SUITS
        global RANKS
        for s in SUITS:
            for r in RANKS:
                self.deck_list.append(Cards(s, r))            

    def shuffle(self):
        # add cards back to deck and shuffle
        # self = Deck()
        return random.shuffle(self.deck_list)

        #adding back this way did not work

    def deal_card(self):
        return self.deck_list.pop(0)

    def __str__(self):
        self.deck_str = ""
        for c in self.deck_list:
            self.deck_str += str(c)+" "
        return "Deck contains " + self.deck_str



def deal():
    global outcome, score, in_play
    global player_hand, dealer_hand, game_deck
    if in_play:
        outcome = "You forfeit this hand."
        in_play = False
        score -= 1
        return

    #shuffle the deck
    game_deck = Deck()
    game_deck.shuffle()

    #create new hands
    player_hand = Hand()
    dealer_hand = Hand()
    #add two cards to each hand
    player_hand.add_card(game_deck.deal_card())
    dealer_hand.add_card(game_deck.deal_card())
    player_hand.add_card(game_deck.deal_card())
    dealer_hand.add_card(game_deck.deal_card())
    in_play = True
    #print "Player", player_hand
    #print "Dealer", dealer_hand
    outcome = "Hit or stand?"
    draw()
    player_hand.draw(PLAYER_POS)
    dealer_hand.draw(DEALER_POS)

def hit():
    global in_play, player_hand, dealer_hand, outcome, score
    # if the hand is in play, hit the player
    if in_play:
        player_hand.add_card(game_deck.deal_card())
        #print "Player", player_hand

    # if busted, assign a message to outcome, update in_play and score
        if player_hand.get_value() > 21:
            outcome = "Sorry, you busted!"
            #print outcome
            in_play = False
            score -= 1
    player_hand.draw(PLAYER_POS)

def stand():
    global in_play, player_hand, dealer_hand, outcome, score
    if not in_play:
        outcome = "Too, late, your hand is already busted."
        return
    else:
        while dealer_hand.get_value() < 17:
            dealer_hand.add_card(game_deck.deal_card())
            print "Dealer", dealer_hand
    if dealer_hand.get_value() > 21:
        outcome = "Dealer busted! You win!"
        in_play = False
        score += 1
    else:
        player_value = player_hand.get_value()
        dealer_value = dealer_hand.get_value()
        print "Player has ",player_value, "Dealer has ",dealer_value
        if player_value <= dealer_value:
            outcome = "Dealer wins!"
            score -= 1
        else:
            outcome = "Player wins!"
            score += 1
        in_play = False
    print player_hand.get_value()
    print dealer_hand.get_value()


def draw():
    if in_play:
        back = Image.open("C:\Users\Petar\Desktop\card_back.png")
        CARD_BACK = ImageTk.PhotoImage(back)
        canvas.create_image(DEALER_POS,image=CARD_BACK)





root=Tk()

canvas=Canvas(root,height=HEIGHT,width=WIDTH)
canvas.pack()


Deal=Button(text="Deal",command=deal).pack()
Hit=Button(text="Hit",command=hit).pack()
Stand=Button(text="Stand",command=stand).pack()



root.mainloop()
Was it helpful?

Solution

One problem that I see is in the Card.draw method.

There's a little trick to using Tkinter's PhotoImage class. You must keep your own reference to each PhotoImage instance, or python's garbage collector will destroy it. I suspect that's why you never see your cards appear when you press the "deal" button.

To fix it, add this to your Card.__init__ method:

class Cards:
    def __init__(self,suit,rank):
        self.suit = suit
        self.rank = rank  
        self.karta = None
#       ^^^^^^^^^^^^^^^^^

And make these changes to your Card.draw method:

    def draw(self,position):
        CARD = Image.open ("C:\Users\Petar\Desktop\cards.png")
        box = [RANKS.index(self.rank) * CARD_SIZE[0], SUITS.index(self.suit) * CARD_SIZE[1], CARD_SIZE[0] * (RANKS.index(self.rank)+1) , CARD_SIZE[1] * (SUITS.index(self.suit)+1)]
        cropped = CARD.crop(box)
        self.karta = ImageTk.PhotoImage(cropped)
#       ^^^^^
        canvas.create_image(position, image=self.karta) 
#                                           ^^^^^

That way your Card objects will maintain a reference to their PhotoImage instances, and python will not garbage collect them.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top