user8968949
user8968949

Reputation: 13

index out of range with list of tkinter buttons

I'm trying to adapt a code i made in python to a program using tkinter. I made a list for each char in the board (X and O) and a list of buttons. each button when pressed should update its text from " " to "X" or "O" depending on the situation. The rest of the code wasn't made yet, but thats not the problem.

I'm using marcar(i,j,texto) as the function to update the button text, using as reference the button position in the buttons list. but in line 25, the error of "list index out of range" shows up. I use the same command in the line 31. I suppose that it is because in the line 29 where I create the buttons, one of the things used to create them is the method marcar(), and as it hasn't been created any other in the list, I cant reference it.

I can't find any solution to making a button that when pressed it uses a method that references the same button, because to create it, I need the function.

from tkinter import *

lista = [[" ", " ", " "],
         [" ", " ", " "],
         [" ", " ", " "]]

buttons = [[], [], []]

vitx = 0
vito = 0

root = Tk()

turno = Label(root, text="Turno de X",font=("Arial",15))
turno.grid(row = 1,column = 1)

vitoriasx = Label(root, text="Vitórias de X:"+str(vitx), font=("Arial",8))
vitoriasx.grid(row = 1,column = 0)

vitoriaso = Label(root, text="Vitórias de O:"+str(vito), font=("Arial",8))
vitoriaso.grid(row = 1,column = 2)

def marcar(i,j,texto):
    lista[i][j] = texto
    buttons[i][j].config(text= texto)

for i in range(0,3):
    for j in range(0,3):
        buttons[i].append(Button(root,text=lista[i][j],command = marcar(i,j,"x")))
        buttons[i][j].grid(row = i+2,column = j)
        buttons[i][j].config(height=6, width=13)


root.mainloop()

Upvotes: 1

Views: 555

Answers (2)

Mike - SMT
Mike - SMT

Reputation: 15226

You need to build a proper lambda function for this set of loops in order for the buttons to send the correct information to the function and without causing the command to execute on initialization.

In python when you need to save a reference to a function you have 2 options.

For functions that do not take arguments you can leave off the parenthesis () to save a reference to the function or if you need to pass arguments you can use a lambda expression.

If you do use a lambda expression and you have changing variables in your loop like you do here then you need to define what each argument is per loop or else those values will all be equal to the very last loops values.

For example if you simple use a lambda without defining the variables each loop then you will end up with buttons that all send the same data.

Here is an example:

import tkinter as tk


root = tk.Tk()

def marcar(i, j, texto):
    print(i, j, texto)

for i in range(0, 3):
    for j in range(0, 3):
        tk.Button(root, text='button {}.{}'.format(i, j), command=lambda: marcar(i, j, "x")).grid(row=i, column=j)

root.mainloop()

Results:

enter image description here

However if you define the values in the lambda each loop you will get the correct values per button.

Example:

import tkinter as tk


root = tk.Tk()

def marcar(i, j, texto):
    print(i, j, texto)

for i in range(0, 3):
    for j in range(0, 3):
        tk.Button(root, text='button {}.{}'.format(i, j), command=lambda i=i, j=j: marcar(i, j, "x")).grid(row=i, column=j)

root.mainloop()

Results:

enter image description here

Upvotes: 2

Partho63
Partho63

Reputation: 3117

Problem is you can't pass arguments in command like this. you have to change command=marcar(i,j,"x") to command=lambda: marcar(i, j, "x"). Explanations you can find here and here. Try the following code:

from tkinter import *

lista = [[" ", " ", " "],
         [" ", " ", " "],
         [" ", " ", " "]]

buttons = [[], [], []]

vitx = 0
vito = 0

root = Tk()

turno = Label(root, text="Turno de X", font=("Arial", 15))
turno.grid(row=1, column=1)

vitoriasx = Label(root, text="Vitórias de X:"+str(vitx), font=("Arial", 8))
vitoriasx.grid(row=1, column=0)

vitoriaso = Label(root, text="Vitórias de O:"+str(vito), font=("Arial", 8))
vitoriaso.grid(row=1, column=2)

def marcar(i, j, texto):
    lista[i][j] = texto
    buttons[i][j].config(text=texto)

for i in range(0, 3):
    for j in range(0, 3):
        buttons[i].append(Button(root, text=lista[i][j], command=lambda: marcar(i, j, "x")))
        buttons[i][j].grid(row=i+2, column=j)
        buttons[i][j].config(height=6, width=13)

root.mainloop()

Upvotes: 0

Related Questions