为什么将光标放在文本框中会给出"索引超出范围"错误?

Jer*_*ith 0 textbox tkinter python-2.7

我一直在研究Chris Meyers Tkinter教程并注意到一个奇怪的例外.当我运行程序并"加载"列表框中的一个项目并更新信息时,如果我将光标留在框中的任何位置,我会收到以下错误:

IndexError:元组索引超出范围

但是,如果我在框外单击并单击更新按钮,它可以正常工作.关于为什么会发生这种情况以及如何防范这种情况的任何想法?这是完整的程序:

    from Tkinter import *
from phones  import *

def whichSelected () :
    print "At %s of %d" % (select.curselection(), len(phonelist))
    return int(select.curselection()[0])

def addEntry () :
    phonelist.append ([nameVar.get(), phoneVar.get()])
    setSelect ()

def updateEntry() :
    phonelist[whichSelected()] = [nameVar.get(), phoneVar.get()]
    setSelect ()

def deleteEntry() :
    del phonelist[whichSelected()]
    setSelect ()

def loadEntry  () :
    name, phone = phonelist[whichSelected()]
    nameVar.set(name)
    phoneVar.set(phone)

def makeWindow () :
    global nameVar, phoneVar, select
    win = Tk()

    frame1 = Frame(win)
    frame1.pack()

    Label(frame1, text="Name").grid(row=0, column=0, sticky=W)
    nameVar = StringVar()
    name = Entry(frame1, textvariable=nameVar)
    name.grid(row=0, column=1, sticky=W)

    Label(frame1, text="Phone").grid(row=1, column=0, sticky=W)
    phoneVar= StringVar()
    phone= Entry(frame1, textvariable=phoneVar)
    phone.grid(row=1, column=1, sticky=W)

    frame2 = Frame(win)       # Row of buttons
    frame2.pack()
    b1 = Button(frame2,text=" Add  ",command=addEntry)
    b2 = Button(frame2,text="Update",command=updateEntry)
    b3 = Button(frame2,text="Delete",command=deleteEntry)
    b4 = Button(frame2,text=" Load ",command=loadEntry)
    b1.pack(side=LEFT); b2.pack(side=LEFT)
    b3.pack(side=LEFT); b4.pack(side=LEFT)

    frame3 = Frame(win)       # select of names
    frame3.pack()
    scroll = Scrollbar(frame3, orient=VERTICAL)
    select = Listbox(frame3, yscrollcommand=scroll.set, height=6)
    scroll.config (command=select.yview)
    scroll.pack(side=RIGHT, fill=Y)
    select.pack(side=LEFT,  fill=BOTH, expand=1)
    return win

def setSelect () :
    phonelist.sort()
    select.delete(0,END)
    for name,phone in phonelist :
        select.insert (END, name)

win = makeWindow()
setSelect ()
win.mainloop()
Run Code Online (Sandbox Code Playgroud)

这是phones.py的内容

    phonelist = [
  ['Meyers, Chris',  '343-4349'],
  ['Smith, Robert',  '689-1234'],
  ['Jones, Janet',   '483-5432'],
  ['Barnhart, Ralph','683-2341'],
  ['Nelson, Eric',   '485-2689'],
  ['Prefect, Ford',  '987-6543'],
  ['Zigler, Mary',   '567-8901'],
  ['Smith, Bob',     '689-1234']
]
Run Code Online (Sandbox Code Playgroud)

Sre*_*ary 6

这是因为select.curselection() - 返回列表框中所选项的索引 - 一旦离开记录列表,就会返回一个空元组 - ().

因此,在这种情况下,一旦您单击文本框以更新某些内容,您将清除所选的记录列表.

def whichSelected () :
    print "At %s of %d" % (select.curselection(), len(phonelist))
    return int(select.curselection()[0])
Run Code Online (Sandbox Code Playgroud)

这就是为什么,在你得到IndexError:tuple索引超出范围错误之前,你也看到了

At () of 8
Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Python27\lib\lib-tk\Tkinter.py", line 1536, in __call__
    return self.func(*args)
  File "main.py", line 13, in updateEntry
    phonelist[whichSelected()] = [nameVar.get(), phoneVar.get()]
  File "main.py", line 6, in whichSelected
    return int(select.curselection()[0])
IndexError: tuple index out of range
Run Code Online (Sandbox Code Playgroud)

按"加载"后,记录的详细信息将显示在相应的输入字段中(本例中为文本框).您选择的记录仍处于选中状态(因此select.curselection()仍具有项目索引).

但是一旦更新了记录,你也会调用setSelect()来刷新记录列表.

def updateEntry() :
    phonelist[whichSelected()] = [nameVar.get(), phoneVar.get()]
    setSelect ()

def setSelect () :
    phonelist.sort()
    select.delete(0,END)
    for name,phone in phonelist :
        select.insert (END, name)
Run Code Online (Sandbox Code Playgroud)

要更新与先前相同的记录,需要再次单击相关记录,以便select.curselection()现在知道要更新的记录.

要避免手动执行此操作,可以尝试使用selection_set()来保存以前的已知位置.例如,像这样:

def updateEntry() :
    phonelist[whichSelected()] = [nameVar.get(), phoneVar.get()]
    setSelect (index=whichSelected())

def setSelect (index=0) :
    if not len(phonelist):
        index = None
    phonelist.sort()
    select.delete(0,END)
    for name,phone in phonelist :
        select.insert (END, name)
    if index is not None:
        select.selection_set(index) 
Run Code Online (Sandbox Code Playgroud)

这将设置仍然选择的先前已知项目.它还在每个其他操作中选择列表中的第一个项目.如果phonelist是空的,那么当然我们不会选择任何东西(因此,index = None)