from PIL import Image from customtkinter import CTkFrame, CTk, CTkScrollableFrame, CTkLabel, CTkButton, CTkImage from loguru import logger as _logger try: from ..utils import fonts except ImportError: from utils import fonts logger = _logger.bind(module="Sidebar", prefix="misc") class CTkSidebarFrame(CTkFrame): fg_color = "#2b2b2b" bg_color = "#333333" butt = { "enabled": "#ededed", "disabled": "#363636", "fg": "#3a6069", "bg": "transparent", "hv": "#518894", "pressed": "#2b2b2b" } def __init__(self, master: CTk, name, icon_light, icon_dark, name_font=None, options=False, options_text="Options", options_img=None, options_command=None, corner_radius=0, *args, **kwargs): super().__init__(master, corner_radius=corner_radius, *args, **kwargs) self.__state = { "status": { "text": "Starting..", "color": "#aec6cf" } } self.__buttons = {} self.active = None self._name = name self._name_font = name_font or fonts.heading_font() self._icon_light = icon_light self._icon_dark = icon_dark self._add_options = options self._options_text = options_text if options_img is not None: options_img = Image.open(options_img) self._options_img = options_img if options and options_command is None: raise ValueError("options_command must be provided if options is True") self._options_command = options_command self.grid_columnconfigure(0, weight=0) # label self.grid_columnconfigure(1, weight=0) # dropdown self.grid_rowconfigure(2, weight=1) # buttons self.grid_rowconfigure(3, weight=0) # settings self._create_widgets() def _create_widgets(self): self.update_idletasks() name_label = CTkLabel(self, text=self._name, fg_color=self.fg_color, bg_color=self.bg_color, corner_radius=0, font=self._name_font) name_label.grid(row=0, column=0, columnspan=2, sticky="ew", padx=5, pady=(10, 0)) self.status_label = CTkLabel(self, text="Status", fg_color=self.fg_color, bg_color=self.bg_color, corner_radius=0) self.status_label.grid(row=1, column=0, columnspan=2, sticky="ew", padx=5, pady=(0, 3)) self._render_status() self._sc_frame = CTkScrollableFrame(self, width=160, fg_color=self.fg_color, scrollbar_button_color=self.bg_color, corner_radius=0) self._sc_frame.grid(row=2, column=0, sticky="nsew", padx=5, pady=(3, 10)) if self._add_options: settings_img = None if self._options_img is not None: settings_img = CTkImage(self._options_img, self._options_img, (12, 12)) self.options = CTkButton(self, text=self._options_text, fg_color=self.fg_color, bg_color=self.bg_color, font=fonts.button_med_font(), image=settings_img, command=self._options_command) self.options.grid(row=3, column=0, sticky="ew", padx=5, pady=(0, 10)) def _render_status(self): status = self.__state["status"] self.status_label.configure(text=status["text"], font=fonts.subheading_font(), text_color=status["color"]) def _on_press(self, name): logger.debug(f"[{name!r}] pressed") button = self.__buttons[name] if not button['active']: logger.debug(f"{name} is disabled") return self.set_enabled(self.active) self.set_pressed(name) self.active = name button['button'].configure(hover_color=self.butt['pressed']) f = button['command'] f_args = button['command_args'] f(*f_args) def set_status(self, text, color=None): self.__state["status"]["text"] = text if color is not None: self.__state["status"]["color"] = color self._render_status() def add_button(self, name, text, command, command_args=(), icon=None): self.update_idletasks() if icon is not None: _icon = Image.open(icon) icon = CTkImage(_icon, _icon, (12, 12)) button = CTkButton(self._sc_frame, text=text, font=fonts.button_med_font(), image=icon, command=lambda: self._on_press(name)) button.pack(fill="x", padx=5, pady=5) self.__buttons[name] = {"active": True, "button": button, "command": command, "command_args": command_args} self.set_enabled(name) def set_pressed(self, name): if name is None: return butt = self.__buttons[name] if not butt['active']: return butt['button'].configure(fg_color=self.butt['pressed'], hover_color=self.butt['pressed']) def set_enabled(self, name): self.update_idletasks() logger.debug(f"[{name!r}] set_enabled") if name is None: return butt = self.__buttons[name] butt['active'] = True butt['button'].configure( fg_color=self.butt['fg'], bg_color=self.butt['bg'], text_color=self.butt['enabled'], hover_color=self.butt['hv'] ) def set_disabled(self, name): self.update_idletasks() logger.debug(f"[{name!r}] set_disabled") if name is None: return butt = self.__buttons[name] butt['active'] = False butt['button'].configure( fg_color=self.butt['fg'], bg_color=self.butt['bg'], text_color=self.butt['disabled'], hover_color=self.butt['fg'] )