MultiListbox 上的双击事件
Double Click Event on MultiListbox
我有三个 MultiListbox 项目和两个条目。当我双击 MultiListbox 项目时,MultiListbox 选定的行信息将出现在条目中。出于这个原因,我试图在我的 MultiListbox Class 中添加一个函数,并在另一个 Class 中调用这个函数,但我不知道我该怎么做。
这是我的代码:
from tkinter import *
from tkinter.ttk import *
import tkinter as tk
import sqlite3
from tkinter import messagebox
class MultiListbox(Frame):
def __init__(self, master, lists):
Frame.__init__(self, master)
self.grid(sticky="nsew")
self.select_index = None
i = 2
for num, (l, w) in enumerate(lists):
if num == 0:
continue
self.grid_columnconfigure(num, weight=w, uniform='fred')
i += num
self.grid_rowconfigure(1, weight=1)
self.lists = []
for num, (l, w) in enumerate(lists):
frame = Frame(self, borderwidth=0)
frame.grid_columnconfigure(0, weight=1)
frame.grid_rowconfigure(1, weight=1)
frame.grid(row=1, column=num, sticky="nsew")
lbl = Label(frame, text=l, font=("Vrinda (Body CS)", 11), borderwidth=1, relief=SUNKEN, anchor="center", justify="center")
lbl.grid(row=0, column=0, sticky="nsew")
lb = Listbox(frame, font=("Vrinda (Body CS)", 9), height=5, borderwidth=1, selectborderwidth=1, exportselection=FALSE)
lb.grid(row=1, column=0, sticky="nsew")
self.lists.append(lb)
lb.bind('<B1-Motion>', lambda e, s=self: s._select(e.y))
lb.bind('<Button-1>', lambda e, s=self: s._select(e.y))
lb.bind('<Leave>', lambda e: 'break')
lb.bind('<B2-Motion>', lambda e, s=self: s._b2motion(e.x, e.y))
lb.bind('<Button-2>', lambda e, s=self: s._button2(e.x, e.y))
lb.bind('<Button-4>', lambda e, s=self: s._scroll(SCROLL, 1, PAGES))
lb.bind('<Button-5>', lambda e, s=self: s._scroll(SCROLL, -1, PAGES))
lb.bind("<MouseWheel>", self.OnMouseWheel)
sb_y = Scrollbar(self, orient=VERTICAL, command=self._yscroll)
sb_y.grid(row=1, rowspan=2, column=i, sticky="nsew")
self.lists[0]['yscrollcommand'] = sb_y.set
sb_x = Scrollbar(self, orient=HORIZONTAL, command=self._xscroll)
sb_x.grid(row=3, column=0, columnspan=i, sticky="ew")
self.lists[0]['xscrollcommand'] = sb_x.set
def _button2(self, x, y):
for l in self.lists: l.scan_mark(x, y)
return 'break'
def _b2motion(self, x, y):
for l in self.lists: l.scan_dragto(x, y)
return 'break'
def _yscroll(self, *args):
for l in self.lists:
l.yview(*args)
def _xscroll(self, *args):
for l in self.lists:
l.xview(*args)
def curselection(self):
return self.lists[0].curselection()
def delete(self, first, last=None):
for l in self.lists:
l.delete(first, last)
def get(self, first, last=None):
result = []
for l in self.lists:
result.append(l.get(first,last))
if last: return list(map(*[None] + result))
return result
def index(self, index):
self.lists[0].index(index)
def insert(self, index, *elements):
for e in elements:
i = 0
for l in self.lists:
l.insert(index, e[i])
i = i + 1
def size(self):
return self.lists[0].size()
def see(self, index):
for l in self.lists:
l.see(index)
def _select(self, y):
row = self.lists[0].nearest(y)
self.selection_clear(0, END)
self.selection_set(row)
return 'break'
def selection_anchor(self, index):
for l in self.lists:
l.selection_anchor(index)
def selection_clear(self, first, last=None):
for l in self.lists:
l.selection_clear(first, last)
def selection_includes(self, index):
return self.lists[0].selection_includes(index)
def selection_set(self, first, last=None):
self.select_index =[first]
for l in self.lists:
l.selection_set(first, last)
def OnMouseWheel(self, event):
for l in self.lists:
l.yview("scroll", event.delta,"units")
# this prevents default bindings from firing, which
# would end up scrolling the widget twice
return "break"
class FormAddProduct:
def __init__(self, master):
self.frame = master
self.frame.configure(padx=5)
for i in range(0, 1):
self.frame.grid_columnconfigure(i, weight=1)
self.frame.grid_rowconfigure (1, weight = 1)
self._init_widgets()
def _init_widgets(self):
self.frame1 = tk.Frame(self.frame, relief=FLAT, borderwidth=1)
for i in range(0, 2):
self.frame1.grid_columnconfigure(i, weight=1)
for i in range(0, 4):
self.frame1.grid_rowconfigure(i, weight=1)
lbl = Label(self.frame1, text="Product Name").grid(row=0, column=0)
self.ent_item_code = Entry(self.frame1)
self.ent_item_code.grid(row=0, column=1, sticky="nesw")
lbl = Label(self.frame1, text="Amount").grid(row=1, column=0)
self.ent_item_code = Entry(self.frame1)
self.ent_item_code.grid(row=1, column=1, sticky="nesw")
self.mlb = MultiListbox(self.frame1, (
('Product Name', 3), ('Amount $', 1)))
self.mlb.grid(row=2, column=0, columnspan=2, sticky="nsew")
a1 = ("Apple", 10)
a2 = ("Orange", 25)
a3 = ("Banana", 25)
self.mlb.insert(END, a1)
self.mlb.insert(END, a2)
self.mlb.insert(END, a3)
self.frame1.grid(row=1, column=0, sticky="nsew", padx=1, pady=5)
def main():
app = Tk()
FormAddProduct(app)
app.mainloop()
if __name__=='__main__':
main()
我能想到两种方法来解决这个问题,尽管可能还有其他方法。
首先,您可以使用虚拟事件。 MultiListbox
可以生成事件并且 FormAddProduct
可以绑定到它。或者,FormAddProduct
可以将回调传递给 MultiListbox
以告诉它在发生双击时调用什么。
使用虚拟事件
首先在MultiListbox
中添加一个函数来生成事件:
class MultiListbox(Frame):
...
def _double1(self, event):
self.event_generate("<<DoubleClick>>")
...
接下来,在您的列表框中绑定到此函数:
class MultiListbox(Frame):
def __init__(self, master, lists):
...
for num, (l, w) in enumerate(lists):
...
lb.bind('<Double-1>', self._double1)
最后,在 FormAddProduct
中,添加一个绑定到此事件以调用您的函数:
class FormAddProduct:
def _init_widgets(self):
...
self.mlb.bind("<<DoubleClick>>", self.do_something)
def do_something(self, event):
print("Double-click!")
使用回调
要使用回调,MultiListbox
的 __init__
需要接受回调作为参数,并将其保存到实例变量中:
class MultiListbox(Frame):
def __init__(self, master, lists, callback=None):
Frame.__init__(self, master)
self.callback = callback
...
接下来,我们可以创建一个在双击时调用回调的函数。您需要决定将哪些信息传递给回调。例如,您可以传入选定的产品名称和数量。以下示例传递 MultiListbox
的实例,以便回调可以与 MultiListbox
的多个实例一起使用:
class MultiListbox(Frame):
...
def _double1(self):
if self.callback is not None:
self.callback(listbox=self)
接下来,我们需要在FormAddProduct
中定义回调。请记住,它必须接受 MultiListbox
的实例作为参数。此示例使用它来获取当前选择并将其打印出来:
class FormAddProduct:
...
def do_something(self, listbox):
curselection = listbox.curselection()
value = None
if curselection:
item = curselection[0]
value = listbox.get(item)
print(f"callback was called: {value}")
最后,在创建 MultiListbox
时将此函数作为回调传递:
class FormAddProduct:
def _init_widgets(self):
...
self.mlb = MultiListbox(self.frame1, (
('Product Name', 3), ('Amount $', 1)), callback=self.do_something)
我有三个 MultiListbox 项目和两个条目。当我双击 MultiListbox 项目时,MultiListbox 选定的行信息将出现在条目中。出于这个原因,我试图在我的 MultiListbox Class 中添加一个函数,并在另一个 Class 中调用这个函数,但我不知道我该怎么做。 这是我的代码:
from tkinter import *
from tkinter.ttk import *
import tkinter as tk
import sqlite3
from tkinter import messagebox
class MultiListbox(Frame):
def __init__(self, master, lists):
Frame.__init__(self, master)
self.grid(sticky="nsew")
self.select_index = None
i = 2
for num, (l, w) in enumerate(lists):
if num == 0:
continue
self.grid_columnconfigure(num, weight=w, uniform='fred')
i += num
self.grid_rowconfigure(1, weight=1)
self.lists = []
for num, (l, w) in enumerate(lists):
frame = Frame(self, borderwidth=0)
frame.grid_columnconfigure(0, weight=1)
frame.grid_rowconfigure(1, weight=1)
frame.grid(row=1, column=num, sticky="nsew")
lbl = Label(frame, text=l, font=("Vrinda (Body CS)", 11), borderwidth=1, relief=SUNKEN, anchor="center", justify="center")
lbl.grid(row=0, column=0, sticky="nsew")
lb = Listbox(frame, font=("Vrinda (Body CS)", 9), height=5, borderwidth=1, selectborderwidth=1, exportselection=FALSE)
lb.grid(row=1, column=0, sticky="nsew")
self.lists.append(lb)
lb.bind('<B1-Motion>', lambda e, s=self: s._select(e.y))
lb.bind('<Button-1>', lambda e, s=self: s._select(e.y))
lb.bind('<Leave>', lambda e: 'break')
lb.bind('<B2-Motion>', lambda e, s=self: s._b2motion(e.x, e.y))
lb.bind('<Button-2>', lambda e, s=self: s._button2(e.x, e.y))
lb.bind('<Button-4>', lambda e, s=self: s._scroll(SCROLL, 1, PAGES))
lb.bind('<Button-5>', lambda e, s=self: s._scroll(SCROLL, -1, PAGES))
lb.bind("<MouseWheel>", self.OnMouseWheel)
sb_y = Scrollbar(self, orient=VERTICAL, command=self._yscroll)
sb_y.grid(row=1, rowspan=2, column=i, sticky="nsew")
self.lists[0]['yscrollcommand'] = sb_y.set
sb_x = Scrollbar(self, orient=HORIZONTAL, command=self._xscroll)
sb_x.grid(row=3, column=0, columnspan=i, sticky="ew")
self.lists[0]['xscrollcommand'] = sb_x.set
def _button2(self, x, y):
for l in self.lists: l.scan_mark(x, y)
return 'break'
def _b2motion(self, x, y):
for l in self.lists: l.scan_dragto(x, y)
return 'break'
def _yscroll(self, *args):
for l in self.lists:
l.yview(*args)
def _xscroll(self, *args):
for l in self.lists:
l.xview(*args)
def curselection(self):
return self.lists[0].curselection()
def delete(self, first, last=None):
for l in self.lists:
l.delete(first, last)
def get(self, first, last=None):
result = []
for l in self.lists:
result.append(l.get(first,last))
if last: return list(map(*[None] + result))
return result
def index(self, index):
self.lists[0].index(index)
def insert(self, index, *elements):
for e in elements:
i = 0
for l in self.lists:
l.insert(index, e[i])
i = i + 1
def size(self):
return self.lists[0].size()
def see(self, index):
for l in self.lists:
l.see(index)
def _select(self, y):
row = self.lists[0].nearest(y)
self.selection_clear(0, END)
self.selection_set(row)
return 'break'
def selection_anchor(self, index):
for l in self.lists:
l.selection_anchor(index)
def selection_clear(self, first, last=None):
for l in self.lists:
l.selection_clear(first, last)
def selection_includes(self, index):
return self.lists[0].selection_includes(index)
def selection_set(self, first, last=None):
self.select_index =[first]
for l in self.lists:
l.selection_set(first, last)
def OnMouseWheel(self, event):
for l in self.lists:
l.yview("scroll", event.delta,"units")
# this prevents default bindings from firing, which
# would end up scrolling the widget twice
return "break"
class FormAddProduct:
def __init__(self, master):
self.frame = master
self.frame.configure(padx=5)
for i in range(0, 1):
self.frame.grid_columnconfigure(i, weight=1)
self.frame.grid_rowconfigure (1, weight = 1)
self._init_widgets()
def _init_widgets(self):
self.frame1 = tk.Frame(self.frame, relief=FLAT, borderwidth=1)
for i in range(0, 2):
self.frame1.grid_columnconfigure(i, weight=1)
for i in range(0, 4):
self.frame1.grid_rowconfigure(i, weight=1)
lbl = Label(self.frame1, text="Product Name").grid(row=0, column=0)
self.ent_item_code = Entry(self.frame1)
self.ent_item_code.grid(row=0, column=1, sticky="nesw")
lbl = Label(self.frame1, text="Amount").grid(row=1, column=0)
self.ent_item_code = Entry(self.frame1)
self.ent_item_code.grid(row=1, column=1, sticky="nesw")
self.mlb = MultiListbox(self.frame1, (
('Product Name', 3), ('Amount $', 1)))
self.mlb.grid(row=2, column=0, columnspan=2, sticky="nsew")
a1 = ("Apple", 10)
a2 = ("Orange", 25)
a3 = ("Banana", 25)
self.mlb.insert(END, a1)
self.mlb.insert(END, a2)
self.mlb.insert(END, a3)
self.frame1.grid(row=1, column=0, sticky="nsew", padx=1, pady=5)
def main():
app = Tk()
FormAddProduct(app)
app.mainloop()
if __name__=='__main__':
main()
我能想到两种方法来解决这个问题,尽管可能还有其他方法。
首先,您可以使用虚拟事件。 MultiListbox
可以生成事件并且 FormAddProduct
可以绑定到它。或者,FormAddProduct
可以将回调传递给 MultiListbox
以告诉它在发生双击时调用什么。
使用虚拟事件
首先在MultiListbox
中添加一个函数来生成事件:
class MultiListbox(Frame):
...
def _double1(self, event):
self.event_generate("<<DoubleClick>>")
...
接下来,在您的列表框中绑定到此函数:
class MultiListbox(Frame):
def __init__(self, master, lists):
...
for num, (l, w) in enumerate(lists):
...
lb.bind('<Double-1>', self._double1)
最后,在 FormAddProduct
中,添加一个绑定到此事件以调用您的函数:
class FormAddProduct:
def _init_widgets(self):
...
self.mlb.bind("<<DoubleClick>>", self.do_something)
def do_something(self, event):
print("Double-click!")
使用回调
要使用回调,MultiListbox
的 __init__
需要接受回调作为参数,并将其保存到实例变量中:
class MultiListbox(Frame):
def __init__(self, master, lists, callback=None):
Frame.__init__(self, master)
self.callback = callback
...
接下来,我们可以创建一个在双击时调用回调的函数。您需要决定将哪些信息传递给回调。例如,您可以传入选定的产品名称和数量。以下示例传递 MultiListbox
的实例,以便回调可以与 MultiListbox
的多个实例一起使用:
class MultiListbox(Frame):
...
def _double1(self):
if self.callback is not None:
self.callback(listbox=self)
接下来,我们需要在FormAddProduct
中定义回调。请记住,它必须接受 MultiListbox
的实例作为参数。此示例使用它来获取当前选择并将其打印出来:
class FormAddProduct:
...
def do_something(self, listbox):
curselection = listbox.curselection()
value = None
if curselection:
item = curselection[0]
value = listbox.get(item)
print(f"callback was called: {value}")
最后,在创建 MultiListbox
时将此函数作为回调传递:
class FormAddProduct:
def _init_widgets(self):
...
self.mlb = MultiListbox(self.frame1, (
('Product Name', 3), ('Amount $', 1)), callback=self.do_something)