Programatically add and remove tkinter python labels causes IndexError: list index out of range

Programatically add and remove tkinter python labels causes IndexError: list index out of range

抱歉标题含糊不清,但我不知道如何更好地解释自己。基本上我在这里尝试在 tkinter 中做的是添加和删除标签。标签值得到更新,因此即使我在开始时删除了标签,我的增量也始终为 1。如果我生成标签并从下往上删除它们我没有问题但是我从中间删除一个然后尝试清理我的列表我得到一个错误:

Exception in Tkinter callback
Traceback (most recent call last):
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk/Tkinter.py", line 1536, in __call__
    return self.func(*args)
  File "/Users/XXXX/Helper/development/dynamicListLabels.py", line 21, in <lambda>
    labelList[index].append(ttk.Button(root, text="Remove", command=lambda: removeLabel(labelList[index][0], index)))
IndexError: list index out of range

我的 python 代码如下所示:

#!/usr/bin/python
from Tkinter import *
import ttk


def removeLabel(labelToRemove, bla):
    labelList[labelToRemove.get()][1].destroy()
    labelList[labelToRemove.get()][2].destroy()
    del labelList[labelToRemove.get()]
    for label in labelList:
        index = labelList.index(label)
        label[0].set(index)


def addNewLabel():
    labelList.append([IntVar()])
    index = len(labelList) - 1
    labelList[index][0].set(index)
    labelList[index].append(ttk.Label(root, textvariable=labelList[index][0]))
    labelList[index].append(ttk.Button(root, text="Remove", command=lambda: removeLabel(labelList[index][0], index)))
    labelList[index][1].grid(column=0)
    labelList[index][2].grid(column=1, row=labelList[index][1].grid_info()['row'])


root = Tk()
labelList = []
ttk.Button(root, text="add label", command=addNewLabel).grid(column=1, row=0)
root.mainloop()

我的 GUI 如下所示:

感谢您的帮助!

设计

  1. 主要问题出现在处理不同的索引时。尝试小心地操作它们会导致复杂的操作,从而导致代码冗长且效率低下。要解决此问题,我们只需摆脱它们并利用您已经在使用的标签 class 变量 Tkinter.IntVar()。这使我们可以完全控制标签和关联的小部件。

  2. 另一个避免令人头疼的有效决策是将每个(标签、按钮)对小部件附加到一个唯一的 Tkinter.Frame() 实例。这提供了使用 destroy() method leading automatically to the destruction of the widgets it contains. In the same time, this keeps the look of your GUI and makes your it scalable 删除框架的优势,因为它为您提供了添加更多小部件的可能性。

设计addNewLabel()

除了我在 2 中所说的,与您的原始代码相比,这里没有任何新内容。每个(标签,按钮)对将被绘制到一个唯一的 Tkinter.Frame() 实例中。当然,必须在这个方法中将列表框架声明为全局。

设计removeLabel()

从 1. 我们需要传递给 removeLabel() 的唯一参数是我们要删除的标签固有的 Tkinter 变量(下面代码中的 var)。

然后我们需要使用 winfo_children() 遍历帧列表(下面代码中的 frames)来寻找具有我们正在寻找的文本变量的标签。

请注意 because 我在各个框架内的按钮之前绘制标签,winfo_children() returns 作为第一个小部件列表元素标签

winfo_children():

Returns a list containing the path names of all the children of window. Top-level windows are returned as children of their logical parents. The list is in stacking order, with the lowest window first, except for Top-level windows which are not returned in stacking order. Use the wm stackorder command to query the stacking order of Top-level windows.

这就是为什么这样写是正确的:if frame.winfo_children()[0].var == var 并销毁包含满足此条件的标签的框架。

解决方案

这是程序。我评论了我认为值得评论的台词:

'''
Created on Jun 25, 2016

@author: billal begueradj
'''

from Tkinter import *
import ttk


def removeLabel(var):
    global frames
    z = -1
    # Loop over the list of rames
    for frame in frames:
        z = z + 1
        # Check the text variable of the label of this frame
        if frame.winfo_children()[0].var == var:
           # Destroy the related frame
           frame.destroy()
           # Update the size of the list of frames
           frames = frames[:z] + frames[z+1:]
           # Do not forget to always rest this flag back to -1
           z = -1 

    # Update the labels' numbers       
    r = 0
    for frame in frames:
        frame.winfo_children()[0].var.set(r)
        r = r + 1


def addNewLabel():
    global  frames, i
    var = IntVar()
    frame = Frame(root)
    i = i + 1
    frame.grid(row=i, column=0)    
    var.set(len(frames))
    l = ttk.Label(frame, textvariable=var)
    l.grid(row=0, column=0)
    l.var = var
    b = ttk.Button(frame, text="Remove", command=lambda: removeLabel(var))    
    b.grid(row=0, column=1)
    frames.append(frame)


if __name__ == '__main__':
   root = Tk()
   frames = []
   i = 1
   ttk.Button(root, text="add label", command=addNewLabel).grid(column=0, row=0)
   root.mainloop()

演示

让我们创建 6 个标签:

现在让我们删除3号标签,可以看到标签的编号自动更新了:

现在让我们添加一个新标签。可以看到新添加的标签有一个编号与列表中最后一个存在的标签编号是连续的:

请注意,列表的长度会根据需要随时更新。