0
votes

I'm trying to create a memory game in Python Tkinter. I'm fairly new to tkinter. I created sixteen buttons displaying the cards using a nested for loop and adding each button to a list. My issue is with replacing the card image with a princess image (it's a disney princess memory game). It says the list index is out of range, although it's not supposed to be. Can someone please help me fix it? I've tried solving it in multiple ways with no success. This is my code so far. I haven't finished making the game so that's why some of the variables aren't really used.

from tkinter import *
from random import choice

screen = Tk()
screen.title("Disney Princesses Memory Game")
width = screen.winfo_screenwidth()
height = screen.winfo_screenheight()
screen.geometry("%dx%d" % (width, height))
screen.configure(bg="#e0bce5")

title = Label(screen, text="Memory Game", font=("David", 50, "underline", "bold"), bg="#e0bce5")
title.place(x=400, y=20)

images_list = [
    PhotoImage(file="images/aurora.png"),
    PhotoImage(file="images/belle.png"),
    PhotoImage(file="images/cinderella.png"),
    PhotoImage(file="images/jasmine.png"),
    PhotoImage(file="images/mulan.png"),
    PhotoImage(file="images/rapunzel.png"),
    PhotoImage(file="images/snow white.png"),
    PhotoImage(file="images/tiana.png")
]

buttons_list = []

num_list = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]


def replace_card(c, d):
    chosen_image = choice(images_list)
    check = num_list[images_list.index(chosen_image)]
    check += 1

    buttons_list[c][d].configure(image=chosen_image)

    if check > 2:
        num_list.remove(num_list[check])


card = PhotoImage(file="images/card.png")
x = 100
y = 250
for i in range(2):
    for j in range(8):
        a = Button(screen, image=card, command=replace_card(i, j))
        x += 100
        a.place(x=x, y=y)
        buttons_list.append([a])
    y += 100
    x = 100

screen.mainloop()

Here's the error:

Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Users\Meirom\AppData\Local\Programs\Python\Python39\lib\tkinter\__init__.py", line 1892, in __call__
    return self.func(*args)
  File "C:\Users\Meirom\PycharmProjects\memorygame\main.py", line 46, in <lambda>
    a = Button(screen, image=card, command=lambda: replace_card(i, j))
  File "C:\Users\Meirom\PycharmProjects\memorygame\main.py", line 35, in replace_card
    buttons_list[c][d].configure(image=chosen_image)
IndexError: list index out of range

The output that I expect needs to show the generated image on the button that the card is shown on when the card is clicked

Thank you so much to Shoaib Ahmed and to everyone else for all the help! This is the completed program (I need to add more features so it's not exactly finished, but the original error was solved):

from tkinter import *
from random import choice

screen = Tk()
screen.title("Disney Princesses Memory Game")
width = screen.winfo_screenwidth()
height = screen.winfo_screenheight()
screen.geometry("%dx%d" % (width, height))
screen.configure(bg="#e0bce5")

title = Label(screen, text="Memory Game", font=("David", 50, "underline", "bold"), bg="#e0bce5")
title.place(x=400, y=20)

images_list = [
    PhotoImage(file="images/aurora.png"),
    PhotoImage(file="images/belle.png"),
    PhotoImage(file="images/cinderella.png"),
    PhotoImage(file="images/jasmine.png"),
    PhotoImage(file="images/mulan.png"),
    PhotoImage(file="images/rapunzel.png"),
    PhotoImage(file="images/snow white.png"),
    PhotoImage(file="images/tiana.png")
]

buttons_list = []

chosen_images = []

flipped = []

num_list = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

card = PhotoImage(file="images/card.png")


def choose_images():
    count = 0
    for num in range(16):
        chosen_image = choice(images_list)
        count += 1

        if count > 2:
            count = 0
            reset()
        else:
            chosen_images.append(chosen_image)


def replace_card(c, d):
    global flipped
    buttons_list[c][d].configure(image=chosen_images[d])
    flipped.append(buttons_list[c][d])


def reset():
    global card
    for element in flipped:
        element.configure(image=card)


choose_images()
x = 100
y = 250
for i in range(2):
    buttons_list.append([])
    for j in range(8):
        a = Button(screen, image=card, command=lambda i=i, j=j: replace_card(i, j))
        x += 100
        a.place(x=x, y=y)
        buttons_list[i].append(a)
    y += 100
    x = 100

screen.mainloop()
1
in a = Button(screen, image=card, command=replace_card(i, j)) you can not call the function with parameters. you need to give a function name only, like a = Button(screen, image=card, command=replace_card) Check out this link to understand command binding better: pythontutorial.net/tkinter/tkinter-command - shoaib30
Hi @Roni welcome to stackoverflow. Could you please add the actual error message to your post please? It'll make it easier for people to help - Dan
What are the rules of the game? The player clicks on the card, and what should happen to the lists and on the screen? - 8349697
The implementation details depend on what the program is supposed to do. But it seems to me that there is no need to add buttons to the list and get them from the list. You can bind some kind of event to them. You can reset all button images if these buttons are children of some widget. - 8349697
@8349697, can you please show me an example of what you mean? I don't really understand - Roni

1 Answers

0
votes

I don't have the images to try the code, but this should work. You need a Lambda function to pass arguments to your replace_card function

Source: https://www.pythontutorial.net/tkinter/tkinter-command/

from tkinter import *
from random import choice

screen = Tk()
screen.title("Disney Princesses Memory Game")
width = screen.winfo_screenwidth()
height = screen.winfo_screenheight()
screen.geometry("%dx%d" % (width, height))
screen.configure(bg="#e0bce5")

title = Label(screen, text="Memory Game", font=("David", 50, "underline", "bold"), bg="#e0bce5")
title.place(x=400, y=20)

images_list = [
    PhotoImage(file="images/aurora.png"),
    PhotoImage(file="images/belle.png"),
    PhotoImage(file="images/cinderella.png"),
    PhotoImage(file="images/jasmine.png"),
    PhotoImage(file="images/mulan.png"),
    PhotoImage(file="images/rapunzel.png"),
    PhotoImage(file="images/snow white.png"),
    PhotoImage(file="images/tiana.png")
]

buttons_list = []

num_list = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]


def replace_card(c, d):
    chosen_image = choice(images_list)
    check = num_list[images_list.index(chosen_image)]
    check += 1

    buttons_list[c][d].configure(image=chosen_image)

    if check > 2:
        num_list.remove(num_list[check])


card = PhotoImage(file="images/card.png")
x = 100
y = 250
for i in range(2):
    buttons_list.append([])
    for j in range(8):
        a = Button(screen, image=card, command=lambda i=i, j=j: replace_card(i, j))
        x += 100
        a.place(x=x, y=y)
        buttons_list[i].append(a)
    y += 100
    x = 100

screen.mainloop()