Python tkinter 列表框绑定 <Button-1> 仅适用于第二次点击
Python tkinter listbox bind on <Button-1> only works on second click
如果有另一个讨论已经解决了这个问题,但我找不到任何东西,我先道歉。我是 Python 的新手(就此而言,除了 90 年代的一点点 Pascal 编程之外)。
我正在构建一个带有 tk 输入框的 GUI,供用户输入值,然后将其存储在 sqlite 数据库中。我希望用户能够单击列表框中某个字段的值,然后使用所选记录的所有字段的值重新填充 tk 输入框。我已经能够完成这项工作,但它仅在第二次单击时有效,因为我将列表框绑定到函数以填充 Button-1 上的 tk 输入框。第一次点击会产生以下错误,我在其他问题中看到过该错误,但我无法将这些答案转化为我的情况:
错误:
Traceback (most recent call last):
File "...\Python\Python38-32\lib\tkinter__init__.py", line 1883, in call
return self.func(*args)
File "fe.py", line 108, in selectitem
selecteditem.append(lb1.get(ndex))
File "...\Python\Python38-32\lib\tkinter__init__.py", line 3182, in get
return self.tk.call(self._w, 'get', first)
_tkinter.TclError: bad listbox index "": must be active, anchor, end, @x,y, or a number
这里是复制错误的示例代码 - 要使用,首先通过设置 -> 项目菜单添加几个值,方法是填写 VAL 和 REF 框并单击添加项目按钮。然后单击列表框中的一项。第一次点击应该会产生上面的错误。第二次单击时,输入框应填充:
```
import sqlite3
import itertools
import tkinter as tk
from tkinter import ttk
INTRO_FONT = ("Arial", 72)
LARGE_FONT = ("Arial", 12)
NORMAL_FONT = ("Arial", 10)
SMALL_FONT = ("Arial", 8)
#Function to create database
def create_db():
conn = sqlite3.connect("demo.db")
cur = conn.cursor()
cur.execute("CREATE TABLE IF NOT EXISTS demotable (id INTEGER PRIMARY KEY, val TEXT, ref TEXT)")
conn.commit()
conn.close()
#Create database
create_db()
#Main Class to manage frames
class demo(tk.Tk):
def __init__(self,*args,**kwargs):
tk.Tk.__init__(self,*args,**kwargs)
container = tk.Frame(self)
container.pack(side="top",fill="both",expand=True)
container.grid_rowconfigure(0,weight=1)
container.grid_columnconfigure(0,weight=1)
menubar = tk.Menu(container)
filemenu = tk.Menu(menubar,tearoff=0)
filemenu.add_command(label="Exit", command=quit)
menubar.add_cascade(label="File", menu=filemenu)
setupmenu = tk.Menu(menubar, tearoff=0)
setupmenu.add_command(label="Items",command = lambda: self.show_frame(Itemsetuppage))
menubar.add_cascade(label="Setup", menu=setupmenu)
tk.Tk.config(self, menu=menubar)
self.frames={}
for F in (Itemsetuppage,Itemsetuppage):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(Itemsetuppage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
# Frame that inserts and loads values
class Itemsetuppage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
#Function to populate listbox with values
def popitemlist(self):
lb1.delete(0, tk.END)
for item in itemlist():
lb1.insert(tk.END, item)
#Function to add new item to database
def additem():
additem_db(val.get(), ref.get())
val_entry.delete(0, 'end')
ref_entry.delete(0, 'end')
popitemlist(self)
#Function used to populate tk.Entry boxes from database when item in listbox is clicked
def selectitem(event):
global selecteditem
selecteditem = []
ndex = lb1.curselection()
selecteditem.append(lb1.get(ndex))
itemquery = select_item(selecteditem)
val_entry.delete(0, 'end')
val_entry.insert(0, itemquery[1])
ref_entry.delete(0, 'end')
ref_entry.insert(0, itemquery[2])
#Function to query database for values to populate lb1
def itemlist():
conn = sqlite3.connect("demo.db")
cur = conn.cursor()
cur.execute("SELECT DISTINCT val FROM demotable")
results = cur.fetchall()
itemlist = list(itertools.chain(*results))
conn.commit()
conn.close()
return itemlist
#Function to insert values from tk.Entry boxes to database
def additem_db(val, ref):
conn = sqlite3.connect("demo.db")
cur = conn.cursor()
cur.execute("INSERT OR IGNORE INTO demotable VALUES (NULL, ?, ?)",(val,ref))
conn.commit()
conn.close()
#Function to query database for individual record to populate tk.Entry boxes when item is clicked in lb1
def select_item(val):
conn = sqlite3.connect("demo.db")
cur = conn.cursor()
cur.execute("SELECT * FROM demotable WHERE val=?",(val))
results = cur.fetchall()
itemdetail = list(itertools.chain(*results))
conn.commit()
conn.close()
return itemdetail
l1 = tk.Label(self, text="Values in database:")
l1.grid(row=0, column=0, padx=5, pady=5)
lb1 = tk.Listbox(self, selectmode=tk.SINGLE)
lb1.grid(row=1, column=0, padx=5, pady=5)
popitemlist(self)
lb1.bind("<Button-1>", selectitem)
l2 = tk.Label(self, text="Type val into entry box to store:")
l2.grid(row=0, column=1, padx=5, pady=5)
val = tk.StringVar(self)
val_entry = tk.Entry(self, textvariable=val)
val_entry.grid(row=0, column=2, padx=5, pady=5)
l2 = tk.Label(self, text="Type ref into entry box to store:")
l2.grid(row=1, column=1, padx=5, pady=5)
ref = tk.StringVar(self)
ref_entry = tk.Entry(self, textvariable=ref)
ref_entry.grid(row=1, column=2, padx=5, pady=5)
b1 = tk.Button(self, text="Add item",command=additem)
b1.grid(row=2, column=2, padx=5, pady=5)
app = demo()
app.geometry("480x240")
app.mainloop()
```
谢谢,如果我的代码冒犯了任何人,我们深表歉意!
Not having 运行 your code as I have no sqlite3 database I may be way off, but listbox generates a virtual event when a value is selected <<ListboxSelect>>
which you can bind to.尝试替换:
lb1.bind("<Button-1>", selectitem)
和
lb1.bind("<<ListboxSelect>>", selectitem)
此外,我认为 lb1.curselection()
returns 一个元组...,尝试打印它,看看会得到什么。在我检查过的代码中,我总是 lb1.curselection()[0]
.
如果有另一个讨论已经解决了这个问题,但我找不到任何东西,我先道歉。我是 Python 的新手(就此而言,除了 90 年代的一点点 Pascal 编程之外)。
我正在构建一个带有 tk 输入框的 GUI,供用户输入值,然后将其存储在 sqlite 数据库中。我希望用户能够单击列表框中某个字段的值,然后使用所选记录的所有字段的值重新填充 tk 输入框。我已经能够完成这项工作,但它仅在第二次单击时有效,因为我将列表框绑定到函数以填充 Button-1 上的 tk 输入框。第一次点击会产生以下错误,我在其他问题中看到过该错误,但我无法将这些答案转化为我的情况:
错误:
Traceback (most recent call last): File "...\Python\Python38-32\lib\tkinter__init__.py", line 1883, in call return self.func(*args) File "fe.py", line 108, in selectitem selecteditem.append(lb1.get(ndex)) File "...\Python\Python38-32\lib\tkinter__init__.py", line 3182, in get return self.tk.call(self._w, 'get', first) _tkinter.TclError: bad listbox index "": must be active, anchor, end, @x,y, or a number
这里是复制错误的示例代码 - 要使用,首先通过设置 -> 项目菜单添加几个值,方法是填写 VAL 和 REF 框并单击添加项目按钮。然后单击列表框中的一项。第一次点击应该会产生上面的错误。第二次单击时,输入框应填充:
```
import sqlite3
import itertools
import tkinter as tk
from tkinter import ttk
INTRO_FONT = ("Arial", 72)
LARGE_FONT = ("Arial", 12)
NORMAL_FONT = ("Arial", 10)
SMALL_FONT = ("Arial", 8)
#Function to create database
def create_db():
conn = sqlite3.connect("demo.db")
cur = conn.cursor()
cur.execute("CREATE TABLE IF NOT EXISTS demotable (id INTEGER PRIMARY KEY, val TEXT, ref TEXT)")
conn.commit()
conn.close()
#Create database
create_db()
#Main Class to manage frames
class demo(tk.Tk):
def __init__(self,*args,**kwargs):
tk.Tk.__init__(self,*args,**kwargs)
container = tk.Frame(self)
container.pack(side="top",fill="both",expand=True)
container.grid_rowconfigure(0,weight=1)
container.grid_columnconfigure(0,weight=1)
menubar = tk.Menu(container)
filemenu = tk.Menu(menubar,tearoff=0)
filemenu.add_command(label="Exit", command=quit)
menubar.add_cascade(label="File", menu=filemenu)
setupmenu = tk.Menu(menubar, tearoff=0)
setupmenu.add_command(label="Items",command = lambda: self.show_frame(Itemsetuppage))
menubar.add_cascade(label="Setup", menu=setupmenu)
tk.Tk.config(self, menu=menubar)
self.frames={}
for F in (Itemsetuppage,Itemsetuppage):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(Itemsetuppage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
# Frame that inserts and loads values
class Itemsetuppage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
#Function to populate listbox with values
def popitemlist(self):
lb1.delete(0, tk.END)
for item in itemlist():
lb1.insert(tk.END, item)
#Function to add new item to database
def additem():
additem_db(val.get(), ref.get())
val_entry.delete(0, 'end')
ref_entry.delete(0, 'end')
popitemlist(self)
#Function used to populate tk.Entry boxes from database when item in listbox is clicked
def selectitem(event):
global selecteditem
selecteditem = []
ndex = lb1.curselection()
selecteditem.append(lb1.get(ndex))
itemquery = select_item(selecteditem)
val_entry.delete(0, 'end')
val_entry.insert(0, itemquery[1])
ref_entry.delete(0, 'end')
ref_entry.insert(0, itemquery[2])
#Function to query database for values to populate lb1
def itemlist():
conn = sqlite3.connect("demo.db")
cur = conn.cursor()
cur.execute("SELECT DISTINCT val FROM demotable")
results = cur.fetchall()
itemlist = list(itertools.chain(*results))
conn.commit()
conn.close()
return itemlist
#Function to insert values from tk.Entry boxes to database
def additem_db(val, ref):
conn = sqlite3.connect("demo.db")
cur = conn.cursor()
cur.execute("INSERT OR IGNORE INTO demotable VALUES (NULL, ?, ?)",(val,ref))
conn.commit()
conn.close()
#Function to query database for individual record to populate tk.Entry boxes when item is clicked in lb1
def select_item(val):
conn = sqlite3.connect("demo.db")
cur = conn.cursor()
cur.execute("SELECT * FROM demotable WHERE val=?",(val))
results = cur.fetchall()
itemdetail = list(itertools.chain(*results))
conn.commit()
conn.close()
return itemdetail
l1 = tk.Label(self, text="Values in database:")
l1.grid(row=0, column=0, padx=5, pady=5)
lb1 = tk.Listbox(self, selectmode=tk.SINGLE)
lb1.grid(row=1, column=0, padx=5, pady=5)
popitemlist(self)
lb1.bind("<Button-1>", selectitem)
l2 = tk.Label(self, text="Type val into entry box to store:")
l2.grid(row=0, column=1, padx=5, pady=5)
val = tk.StringVar(self)
val_entry = tk.Entry(self, textvariable=val)
val_entry.grid(row=0, column=2, padx=5, pady=5)
l2 = tk.Label(self, text="Type ref into entry box to store:")
l2.grid(row=1, column=1, padx=5, pady=5)
ref = tk.StringVar(self)
ref_entry = tk.Entry(self, textvariable=ref)
ref_entry.grid(row=1, column=2, padx=5, pady=5)
b1 = tk.Button(self, text="Add item",command=additem)
b1.grid(row=2, column=2, padx=5, pady=5)
app = demo()
app.geometry("480x240")
app.mainloop()
```
谢谢,如果我的代码冒犯了任何人,我们深表歉意!
Not having 运行 your code as I have no sqlite3 database I may be way off, but listbox generates a virtual event when a value is selected <<ListboxSelect>>
which you can bind to.尝试替换:
lb1.bind("<Button-1>", selectitem)
和
lb1.bind("<<ListboxSelect>>", selectitem)
此外,我认为 lb1.curselection()
returns 一个元组...,尝试打印它,看看会得到什么。在我检查过的代码中,我总是 lb1.curselection()[0]
.