diff --git a/CTkListbox/__init__.py b/CTkListbox/__init__.py new file mode 100644 index 0000000..07f1262 --- /dev/null +++ b/CTkListbox/__init__.py @@ -0,0 +1,10 @@ +""" +CustomTkinter Listbox +Author: Akash Bora (Akascape) +License: MIT +Homepage: https://github.com/Akascape/CTkListbox +""" + +__version__ = '0.9' + +from .ctk_listbox import CTkListbox diff --git a/CTkListbox/__pycache__/__init__.cpython-311.pyc b/CTkListbox/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000..1667ef3 Binary files /dev/null and b/CTkListbox/__pycache__/__init__.cpython-311.pyc differ diff --git a/CTkListbox/__pycache__/ctk_listbox.cpython-311.pyc b/CTkListbox/__pycache__/ctk_listbox.cpython-311.pyc new file mode 100644 index 0000000..80c1c31 Binary files /dev/null and b/CTkListbox/__pycache__/ctk_listbox.cpython-311.pyc differ diff --git a/CTkListbox/ctk_listbox.py b/CTkListbox/ctk_listbox.py new file mode 100644 index 0000000..7190195 --- /dev/null +++ b/CTkListbox/ctk_listbox.py @@ -0,0 +1,234 @@ +""" +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("", lambda e: self.unbind_all("")) + + 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) diff --git a/boiiiwd.py b/boiiiwd.py index d7581f7..2ebcb33 100644 --- a/boiiiwd.py +++ b/boiiiwd.py @@ -1047,7 +1047,7 @@ class LibraryTab(ctk.CTkScrollableFrame): top.withdraw() if os.path.exists(os.path.join(RESOURCES_DIR, "ryuk.ico")): top.after(210, lambda: top.iconbitmap(os.path.join(RESOURCES_DIR, "ryuk.ico"))) - top.title("Item updater - List of items that need updating! - Click to select 1 or more") + top.title("Item updater - List of Items with Updates - Click to select 1 or more") longest_text_length = max(len(text) for text in self.to_update) window_width = longest_text_length * 6 + 5 top.geometry(f"{window_width}x450") diff --git a/dist/BOIIIWD.exe b/dist/BOIIIWD.exe index bfe6dcc..5f7beeb 100644 Binary files a/dist/BOIIIWD.exe and b/dist/BOIIIWD.exe differ