获取列表框中选定项并调用另一个函数以存储选定项

5 投票
3 回答
69679 浏览
提问于 2025-04-17 03:27

我有一个画布,当我点击它时,会调用 createCategoryMeny(x) 这个函数。

这个函数只是创建了一个 Toplevel() 窗口,

def createCategoryMenu(tableNumber):

    ##Not interesting below:
    categoryMenu = Toplevel()
    categoryMenu.title("Mesa numero: " + str(tableNumber))
    categoryMenu.geometry("400x400+100+100")

    categoryMenu.focus()
    categoryMenu.grab_set()

    Label(categoryMenu, text="Elegir categoria \n Mesa numero: " + str(tableNumber)).pack()


    ## RELEVANT CODE BELOW:
    listbox = Listbox(categoryMenu, width=50, height=len(info.listaCategorias))
    listbox.pack(pady=20)

    for item in info.listaCategorias:
        listbox.insert(END, item)

    listbox.selection_set(first=0)

    ##My attempt to solve it
    callback = lambda event, tag= "ThisShouldBeTheSelected!!": do(event, tag)
    listbox.bind("<Double-Button-1>", callback)

然后是 do 函数:

def do(event, tag):
    print tag

这个函数成功地打印了 `"ThisShouldBeTheSelected!!"`。

但是我现在完全卡住了。

我无法获取到双击的元素(也就是选中的那个)。

我想把它作为 tag= 传递。

我尝试过:

listbox.curselection()

但是总是打印 ('0',)

如果我去掉 listbox.selection_set(first=0),我只会得到 ()

所以我的问题是:

  • 我该如何获取选中的项目(也就是双击的那个)
  • (不太重要)像我这样把它传递给另一个函数合理吗?

注意:

我发现了 这个链接

8.5. 为什么 .listbox 的 curselection 或 selection 在我给 listbox 绑定按钮时不返回正确的项目?

在 listbox 的按钮点击事件中获取选中项目的最好方法是使用以下代码:

bind .listbox { set item [%W get [%W nearest %y]] }

这确保了指针下的项目会被返回作为选中的项目。之所以 .listbox 的 curselection 可能会失败,是因为 curselection 中的项目在 Listbox 类绑定触发之前并不会被设置,而这个触发是在实例绑定之后的。这个原因也适用于 selection get 可能会失败的情况,但如果你把 -exportselection 选项设置为 0,它也会失败。

我不确定这是否有帮助,我其实也不太理解它在说什么。

3 个回答

0

对于Spyder和Python 3.6,下面的代码可以正常运行。

import tkinter as tk
root = tk.Tk()
root.geometry("612x417")
root.title("change label on listbox selection")
root.resizable(0,0)
root.configure(background='lightgrey')


#Show selected currency for from in label
frmcur_text = tk.StringVar()
frmcur = tk.Label(root, textvariable=frmcur_text, font="Helvetica 10 bold", anchor='w', background='lightgrey').place(x=195,y=50)

def onselect(evt):
    # Note here that Tkinter passes an event object to onselect()

    w = evt.widget
    index = int(w.curselection()[0])
    value = w.get(index)
#    print ('You selected item %d: "%s"' % (index, value))
    frmcur_text.set(value)

#Create listboxes for xurrency selection
listbox1 = tk.Listbox(root, font="Helvetica 11 bold", height=3, width=10)
listbox2 = tk.Listbox(root, font="Helvetica 11 bold", height=3, width=10)
listbox1.place(x=300,y=50)
listbox2.place(x=300,y=125)


for i in range(20):
    i = i + 1
    listbox1.insert(1, i)
    listbox2.insert(1, i)


listbox1.bind('<<ListboxSelect>>', onselect)    

cs = listbox1.curselection()

frmcur_text.set(cs)

root.mainloop()
2

当你只有一个列表框需要管理时,使用下面这样的代码是很不错的(Python 3):

import tkinter as tk

root = tk.Tk()
box = tk.Listbox(root)
box.insert(tk.END, 'First')
box.insert(tk.END, 'Second')
box.insert(tk.END, 'Third')
box.pack()


def onselect(event):
    w = event.widget
    idx = int(w.curselection()[0])
    value = w.get(idx)
    print(value)


box.bind('<<ListboxSelect>>', onselect)

root.mainloop()

但是当你添加了另一个列表框,或者遇到列表框失去选择的情况时,就会出现IndexError(索引错误)。为了避免这个问题,并且能够为不同的列表框管理不同的回调函数,我建议使用下面这样的代码:

import tkinter as tk

root = tk.Tk()
box = tk.Listbox(root)
box.insert(tk.END, 'First')
box.insert(tk.END, 'Second')
box.insert(tk.END, 'Third')
box.pack()

box2 = tk.Listbox(root)
box2.insert(tk.END, 'First')
box2.insert(tk.END, 'Second')
box2.insert(tk.END, 'Third')
box2.pack()


def on_first_box(idx, val):
    print('First box idx: %s, value: %s' % (idx, val))


def on_second_box(idx, val):
    print('Second box idx: %s, value: %s' % (idx, val))


def onselect(event, listbox):
    w = event.widget
    try:
        idx = int(w.curselection()[0])
    except IndexError:
        return
    if listbox is box:
        return on_first_box(idx, w.get(idx))
    if listbox is box2:
        return on_second_box(idx, w.get(idx))


box.bind('<<ListboxSelect>>', lambda e: onselect(e, box))
box2.bind('<<ListboxSelect>>', lambda e: onselect(e, box2))

root.mainloop()
17

首先,不要使用 lambda。它只适合解决一些特定的问题,而这不是其中之一。你应该创建一个正常的函数,这样写起来和维护起来都更简单。

一旦你这样做了,就可以调用 curselection 来获取当前的选择。你说你试过这个,但你的示例代码没有显示你是怎么做的,所以我只能假设你可能做错了。

至于那个比较特别的建议使用 nearest……它的意思是你在一个控件上设置的绑定会在默认的绑定之前执行。默认的绑定负责设置选择,所以当你绑定到一个 单个 按钮点击时,你的绑定会在默认绑定更新选择之前触发。有很多方法可以解决这个问题,最好的方法是 不要 在单击时绑定,而是绑定到 <<ListboxSelect>>,这样会在选择改变之后触发。

不过你并没有这个问题。因为你是绑定在双击上,选择会在默认的单击绑定设置好之后进行,所以 curselection 会返回正确的值。除非你有自己的单击绑定,这样会阻止默认绑定的执行。

这里有一个简单的例子,可以打印出选择的内容,让你看到它是正确的。请从命令行运行它,这样你可以看到输出:

import Tkinter as tk

class SampleApp(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        lb = tk.Listbox(self)
        lb.insert("end", "one")
        lb.insert("end", "two")
        lb.insert("end", "three")
        lb.bind("<Double-Button-1>", self.OnDouble)
        lb.pack(side="top", fill="both", expand=True)

    def OnDouble(self, event):
        widget = event.widget
        selection=widget.curselection()
        value = widget.get(selection[0])
        print "selection:", selection, ": '%s'" % value

if __name__ == "__main__":
    app = SampleApp()
    app.mainloop()

撰写回答