T7x-Workshop-Downloader/CTkListbox/ctk_listbox.py

235 lines
8.6 KiB
Python

"""
Custom ListBox for customtkinter
Author: Akash Bora
"""
import customtkinter
class CTkListbox(customtkinter.CTkScrollableFrame):
def __init__(self,
master: any,
height: int = 100,
width: int = 200,
hightlight_color: str = "default",
fg_color: str = "transparent",
bg_color: str = None,
text_color: str = "default",
select_color: str = "default",
hover_color: str = "default",
border_width: int = 3,
font: tuple = "default",
multiple_selection: bool = False,
listvariable = None,
hover: bool = True,
command = None,
justify = "left",
**kwargs):
super().__init__(master, width=width, height=height, fg_color=fg_color, border_width=border_width, **kwargs)
self._scrollbar.grid_configure(padx=(0,border_width))
self._scrollbar.configure(width=12)
if bg_color:
super().configure(bg_color=bg_color)
self.select_color = customtkinter.ThemeManager.theme["CTkButton"]["fg_color"] if select_color=="default" else select_color
self.text_color = customtkinter.ThemeManager.theme["CTkButton"]["text_color"] if text_color=="default" else text_color
self.hover_color = customtkinter.ThemeManager.theme["CTkButton"]["hover_color"] if hover_color=="default" else hover_color
self.font = (customtkinter.ThemeManager.theme["CTkFont"]["family"],13) if font=="default" else font
if justify=="left":
self.justify = "w"
elif justify=="right":
self.justify = "e"
else:
self.justify = "c"
self.buttons = {}
self.command = command
self.multiple = multiple_selection
self.selected = None
self.hover = hover
self.end_num = 0
self.selections = []
if listvariable:
self.listvariable = listvariable
self.listvariable.trace_add('write', lambda a,b,c: self.update_listvar())
self.update_listvar()
super().bind("<Destroy>", lambda e: self.unbind_all("<Configure>"))
def update_listvar(self):
values = list(eval(self.listvariable.get()))
self.delete("all")
for i in values:
self.insert("END", option=i)
def select(self, index):
""" select the option """
for options in self.buttons.values():
options.configure(fg_color="transparent")
if self.multiple:
if self.buttons[index] in self.selections:
self.selections.remove(self.buttons[index])
self.buttons[index].configure(fg_color="transparent", hover=False)
self.after(100, lambda: self.buttons[index].configure(hover=self.hover))
else:
self.selections.append(self.buttons[index])
for i in self.selections:
i.configure(fg_color=self.select_color, hover=False)
self.after(100, lambda button=i: button.configure(hover=self.hover))
else:
self.selected = self.buttons[index]
self.buttons[index].configure(fg_color=self.select_color, hover=False)
self.after(100, lambda: self.buttons[index].configure(hover=self.hover))
if self.command:
self.command(self.get())
def activate(self, index):
if str(index).lower()=="all":
if self.multiple:
for i in self.buttons:
self.select(i)
return
selected = list(self.buttons.keys())[index]
self.select(selected)
def curselection(self):
index = 0
if self.multiple:
indexes = []
for i in self.buttons.values():
if i in self.selections:
indexes.append(index)
index +=1
return tuple(indexes)
else:
for i in self.buttons.values():
if i==self.selected:
return index
else:
index +=1
def bind(self, key, func):
super().bind_all(key, lambda e: func(self.get()), add="+")
def deselect(self, index):
if not self.multiple:
self.selected.configure(fg_color="transparent")
self.selected = None
return
if self.buttons[index] in self.selections:
self.selections.remove(self.buttons[index])
self.buttons[index].configure(fg_color="transparent")
def deactivate(self, index):
if str(index).lower()=="all":
for i in self.buttons:
self.deselect(i)
return
selected = list(self.buttons.keys())[index]
self.deselect(selected)
def insert(self, index, option, keybind=None ,func=None, **args):
""" add new option in the listbox """
if str(index).lower()=="end":
index = f"END{self.end_num}"
self.end_num +=1
if index in self.buttons:
self.buttons[index].destroy()
self.buttons[index] = customtkinter.CTkButton(self, text=option, fg_color="transparent", anchor=self.justify,
text_color=self.text_color, font=self.font,
hover_color=self.hover_color, **args)
self.buttons[index].configure(command=lambda num=index: self.select(num))
self.buttons[index].pack(padx=0, pady=(0,5), fill="x", expand=True)
if keybind:
self.buttons[index].bind(keybind, func)
def delete(self, index, last=None):
""" delete options from the listbox """
if str(index).lower()=="all":
for i in self.buttons:
self.buttons[i].destroy()
self.buttons = {}
self.end_num = 0
return
if str(index).lower()=="end":
index = f"END{self.end_num}"
self.end_num -=1
else:
if int(index)==len(self.buttons):
index = len(self.buttons)-1
if int(index)>len(self.buttons):
return
if not last:
index = list(self.buttons.keys())[int(index)]
if last:
if str(last).lower()=="end":
last = len(self.buttons)-1
elif int(last)>=len(self.buttons):
last = len(self.buttons)-1
deleted_list = []
for i in range(index, int(last)+1):
list(self.buttons.values())[i].destroy()
deleted_list.append(list(self.buttons.keys())[i])
for i in deleted_list:
del self.buttons[i]
else:
self.buttons[index].destroy()
del self.buttons[index]
def size(self):
""" return total number of items in the listbox """
return len(self.buttons.keys())
def get(self, index=None):
""" get the selected value """
if index:
if str(index).lower()=="all":
return list(item.cget("text") for item in self.buttons.values())
else:
index = list(self.buttons.keys())[int(index)]
return self.buttons[index].cget("text")
else:
if self.multiple:
return [x.cget("text") for x in self.selections] if len(self.selections)>0 else None
else:
return self.selected.cget("text") if self.selected is not None else None
def configure(self, **kwargs):
""" configurable options of the listbox """
if "hover_color" in kwargs:
self.hover_color = kwargs.pop("hover_color")
for i in self.buttons.values():
i.configure(hover_color=self.hover_color)
if "highlight_color" in kwargs:
self.select_color = kwargs.pop("highlight_color")
if self.selected: self.selected.configure(fg_color=self.select_color)
if len(self.selections)>0:
for i in self.selections:
i.configure(fg_color=self.select_color)
if "text_color" in kwargs:
self.text_color = kwargs.pop("text_color")
for i in self.buttons.values():
i.configure(text=self.text_color)
if "font" in kwargs:
self.font = kwargs.pop("font")
for i in self.buttons.values():
i.configure(font=self.font)
if "command" in kwargs:
self.command = kwargs.pop("command")
super().configure(**kwargs)