【问题标题】:index out of range with list of tkinter buttonstkinter 按钮列表的索引超出范围
【发布时间】:2019-07-18 11:12:12
【问题描述】:

我正在尝试将我在 python 中制作的代码改编为使用 tkinter 的程序。 我为板上的每个字符(X 和 O)和按钮列表制作了一个列表。每个按钮在按下时应根据情况将其文本从“”更新为“X”或“O”。剩下的代码还没有写出来,但这不是问题。

我使用marcar(i,j,texto) 作为更新按钮文本的函数,使用按钮列表中的按钮位置作为参考。但在第 25 行,出现“列表索引超出范围”的错误。我在第 31 行中使用了相同的命令。我想这是因为在我创建按钮的第 29 行中,用于创建它们的方法之一是方法marcar(),因为它还没有被创建列表中的其他,我无法引用它。

我找不到任何解决方案来制作一个按钮,当按下它时,它使用引用相同按钮的方法,因为要创建它,我需要这个函数。

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()

【问题讨论】:

    标签: python tkinter


    【解决方案1】:

    您需要为这组循环构建适当的 lambda 函数,以便按钮向函数发送正确的信息,并且不会导致命令在初始化时执行。

    在 python 中,当您需要保存对函数的引用时,您有 2 个选项。

    对于不带参数的函数,您可以去掉括号 () 以保存对该函数的引用,或者如果您需要传递参数,您可以使用 lambda 表达式。

    如果您确实使用了 lambda 表达式,并且像这里一样在循环中更改了变量,那么您需要定义每个循环的每个参数是什么,否则这些值将全部等于最后一个循环的值。

    例如,如果您简单地使用 lambda 而不在每个循环中定义变量,那么您最终会得到所有发送相同数据的按钮。

    这是一个例子:

    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()
    

    结果:

    但是,如果您在每个循环的 lambda 中定义值,您将获得每个按钮的正确值。

    例子:

    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()
    

    结果:

    【讨论】:

      【解决方案2】:

      问题是你不能像这样在command 中传递参数。您必须将command=marcar(i,j,"x") 更改为command=lambda: marcar(i, j, "x")。您可以找到herehere 的说明。试试下面的代码:

      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()
      

      【讨论】:

      • 不完全。代码必须是lambda i=i, j=j: marcar(i, j, 'x')。在这样的 for 循环中使用 lambda 时,您需要为每个循环定义不断变化的变量,否则所有按钮都会以循环的最后一个值结束。所以基本上现在你的代码将命令marcar(2, 2, 'x') 分配给所有按钮。
      • @Mike-SMT Yap 我知道!我在运行该程序时看到了这一点。但我想让问题在这里,以便提问者可以尝试自己解决问题。然后,如果他/她失败了,我很乐意提供帮助。但首先让他们面对问题并学习。顺便说一句,我为您的回答 +1(赞成)。 :)
      猜你喜欢
      • 1970-01-01
      • 2020-07-19
      • 1970-01-01
      • 2018-12-16
      • 2011-06-14
      • 2016-06-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多