Reputation: 43
I have three MultiListbox items and two entries. When I will double click on the MultiListbox item then MultiListbox selected row information will come to the entries. For this reason, I'm trying to add a function in my MultiListbox Class and called this function in another Class but I don't know how can I do this. Heare is My code:
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()
Upvotes: 0
Views: 616
Reputation: 385970
I can think of two ways to solve this problem, though there are probably others.
First, you can use a virtual event. The MultiListbox
can generate the event and FormAddProduct
can bind to it. Or, FormAddProduct
can pass a callback to MultiListbox
to tell it what to call when a double-click occurs.
Start by adding a function in MultiListbox
to generate the event:
class MultiListbox(Frame):
...
def _double1(self, event):
self.event_generate("<<DoubleClick>>")
...
Next, bind to this function in your listboxes:
class MultiListbox(Frame):
def __init__(self, master, lists):
...
for num, (l, w) in enumerate(lists):
...
lb.bind('<Double-1>', self._double1)
Finally, in FormAddProduct
, add a binding to this event to call your function:
class FormAddProduct:
def _init_widgets(self):
...
self.mlb.bind("<<DoubleClick>>", self.do_something)
def do_something(self, event):
print("Double-click!")
To use a callback, the __init__
of MultiListbox
needs to accept the callback as an argument, and save it to an instance variable:
class MultiListbox(Frame):
def __init__(self, master, lists, callback=None):
Frame.__init__(self, master)
self.callback = callback
...
Next, we can create a function that calls the callback on a double click. You need to decide on what information to pass to the callback. For example, you could pass in the selected product name and amount. The following example passes the instance of MultiListbox
so that the callback can be used with multiple instances of MultiListbox
:
class MultiListbox(Frame):
...
def _double1(self):
if self.callback is not None:
self.callback(listbox=self)
Next, we need to define the callback in FormAddProduct
. Remember, it must accept the instance of the MultiListbox
as an argument. This example uses that to get the current selection and print it out:
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}")
Finally, pass this function as the callback when creating the MultiListbox
:
class FormAddProduct:
def _init_widgets(self):
...
self.mlb = MultiListbox(self.frame1, (
('Product Name', 3), ('Amount $', 1)), callback=self.do_something)
Upvotes: 1