Color picker
File with module Custom color picker to make user life easier.
1"""File with module Custom color picker to make user life easier. 2""" 3 4import customtkinter as ctk 5import re 6 7class ColorPicker(ctk.CTkToplevel): 8 """Class used to pick custom theme color. Is also a module that can be reused with apps using customtkinter. 9 10 Args: 11 12 - ctk.CTkTopLevel : Inheritance from customtkinter CTkTopLevel window. 13 """ 14 def __init__(self, fg_color: str | None=None, preview_size: int=100, r: int=0, g: int=0, b: int=0, font: ctk.CTkFont | None=None, 15 border_color: str | None=None, slider_button_color: str | None=None, slider_progress_color: str | None=None, slider_fg_color: str | None=None, 16 preview_border_color: str | None=None, button_fg_color: str | None=None, button_hover_color: str | None=None, icon: str | None=None, 17 corner_radius: int | None=None) -> None: 18 """Constructor handling most important function calls and variable setup. 19 20 Args: 21 fg_color (str | None, optional): Background color of the window. Defaults to None. 22 preview_size (int, optional): _description_. Defaults to 100. 23 r (int, optional): Default red intensity value. Defaults to 0. 24 g (int, optional): Default green intensity value. Defaults to 0. 25 b (int, optional): Default blue intensity value. Defaults to 0. 26 font (ctk.CTkFont | None, optional): Custom font. Defaults to None. 27 """ 28 super().__init__(fg_color=fg_color) 29 self.fg_color: str| None = fg_color 30 self.border_color: str| None = border_color 31 self.slider_button_color: str| None = slider_button_color 32 self.slider_progress_color: str| None = slider_progress_color 33 self.slider_fg_color: str| None = slider_fg_color 34 self.preview_border_color: str | None = preview_border_color 35 self.button_fg_color: str | None = button_fg_color 36 self.button_hover_color: str | None = button_hover_color 37 self.corner_radius: int | None = corner_radius 38 self.grab_set() 39 self.attributes('-topmost', True) 40 self.title('Color Picker') 41 self.font: ctk.CTkFont | None = font if font else None 42 self.font_size: int = font.cget('size') if font else 15 43 self.preview_size: int = preview_size 44 self.r_val: int = r 45 self.g_val: int = g 46 self.b_val: int = b 47 self.hex_val: str | None = self.convert_to_hex() 48 self.main_frame: ctk.CTkFrame = ctk.CTkFrame( 49 master = self, 50 corner_radius = 0, 51 fg_color = self.fg_color 52 ) 53 self.main_frame.pack(side=ctk.TOP, expand=True, ipadx=10, ipady=10) 54 self.color_preview() 55 self.r_g_b_sliders() 56 self.update_sliders(None) 57 self.bottom_frame: ctk.CTkFrame = ctk.CTkFrame( 58 master = self.main_frame, 59 corner_radius = self.corner_radius, 60 fg_color = 'transparent' 61 ) 62 self.bottom_frame.pack(side=ctk.BOTTOM, expand=True, ipadx=10, ipady=10) 63 self.hex_color_label() 64 self.ok_button() 65 self.resizable(False, False) 66 self.protocol('WM_DELETE_WINDOW', self.on_close) 67 self.lift() 68 self.center_window() 69 self.after(201, lambda: self.iconbitmap(icon)) 70 71 def center_window(self) -> None: 72 """Function centering the TopLevel window. Screen size independent. 73 """ 74 x: int = self.master.winfo_screenwidth() 75 y: int = self.master.winfo_screenheight() 76 app_width: int = self.winfo_width() 77 app_height: int = self.winfo_height() 78 self.geometry(f'+{(x//2)-app_width}+{(y//2)-app_height}') 79 80 def color_preview(self) -> None: 81 """Function creating frame for color preview. 82 """ 83 self.color_prev_box: ctk.CTkFrame = ctk.CTkFrame( 84 master = self.main_frame, 85 fg_color = self.convert_to_hex(), 86 border_width = 3, 87 width = self.preview_size, 88 height = self.preview_size, 89 corner_radius = self.corner_radius, 90 border_color = self.preview_border_color 91 ) 92 self.color_prev_box.pack(side=ctk.RIGHT, padx=3, pady=3, expand=True) 93 94 @staticmethod 95 def validate_hex_color(value_if_allowed: str) -> bool: 96 """Function validating new character in hex entry box. Can take one character or longer string to allow pasting. 97 98 Args: 99 value_if_allowed (str): New value to check. 100 101 Returns: 102 bool: True if hex color patter was met, False otherwise. 103 """ 104 if len(value_if_allowed) == 0 or (re.compile(r'^#[0-9a-fA-F]{0,6}$').match(value_if_allowed)): 105 for char in value_if_allowed[1:]: 106 if char not in '0123456789ABCDEFabcdef': 107 return False 108 return True 109 return False 110 111 def paste_hex_color(self, event) -> None: 112 """Function handling pasting custom color into hex color entry box. 113 114 Args: 115 event (Any): Event type. Doesn't matter but is required parameter by customtkinter. 116 """ 117 clipboard = self.master.clipboard_get() 118 if self.validate_hex_color(clipboard): 119 self.hex_val_label.delete(0, ctk.END) 120 self.hex_val_label.insert(0, clipboard) 121 return None 122 123 def update_on_hex(self, event) -> None: 124 """Function handling all changes on entering last hex color. It changes RGB labels values, sliders values and color preview frame to the desired color. 125 126 Args: 127 event (Any): Event type. Doesn't matter but is required parameter by customtkinter. 128 """ 129 if len(self.hex_val_label.get()) == 7: 130 self.r_val, self.g_val, self.b_val = self.convert_to_r_g_b() 131 self.r_val_label.delete(0, ctk.END) 132 self.g_val_label.delete(0, ctk.END) 133 self.b_val_label.delete(0, ctk.END) 134 self.r_val_label.insert(0, f'{self.r_val}') 135 self.g_val_label.insert(0, f'{self.g_val}') 136 self.b_val_label.insert(0, f'{self.b_val}') 137 self.update_sliders(None, r=self.r_val, g=self.g_val, b=self.b_val) 138 139 def hex_color_label(self) -> None: 140 """Function creating entry box for hex color. 141 """ 142 vcmd = (self.register(self.validate_hex_color), '%P') 143 ctk.CTkLabel(self.bottom_frame, text='Hex: ', font=self.font if self.font else ctk.CTkFont('', self.font_size)).pack(side=ctk.LEFT, padx=3, pady=3) 144 self.hex_val_label: ctk.CTkEntry = ctk.CTkEntry( 145 master = self.bottom_frame, 146 validate = 'key', 147 validatecommand = vcmd, 148 corner_radius = self.corner_radius, 149 font = self.font if self.font else ctk.CTkFont('', self.font_size), 150 width = (self.font_size*8), 151 border_color = self.border_color 152 ) 153 self.hex_val_label.pack(side=ctk.LEFT, padx=3, pady=3) 154 self.hex_val_label.insert(0, f'{self.hex_val}') 155 self.hex_val_label.bind('<Control-v>', self.paste_hex_color) 156 self.hex_val_label.bind('<KeyRelease>', lambda e: self.update_on_hex(e)) 157 158 def ok_button(self) -> None: 159 """Function creating 'OK' button. After clicking the button if color is selected properly the value will be returned in master script. 160 """ 161 ok_button: ctk.CTkButton = ctk.CTkButton( 162 master = self.bottom_frame, 163 text = 'Ok', 164 command = self.on_ok_button, 165 font = self.font if self.font else ctk.CTkFont('', self.font_size), 166 width = (self.font_size*3), 167 corner_radius = self.corner_radius, 168 border_width = 2, 169 border_color = self.border_color, 170 fg_color = self.button_fg_color, 171 hover_color = self.button_hover_color 172 ) 173 ok_button.pack(side=ctk.RIGHT, padx=3, pady=3) 174 175 def new_slider_frame(self, frame: ctk.CTkFrame) -> ctk.CTkFrame: 176 """Function creating frame for slider used to change R or G or B value. 177 178 Args: 179 frame (ctk.CTkFrame): Parent Frame on which cell will be represented. 180 181 Returns: 182 ctk.CTkFrame: Ready packed frame. 183 """ 184 slider_frame: ctk.CTkFrame = ctk.CTkFrame( 185 master = frame, 186 fg_color = 'transparent', 187 corner_radius = self.corner_radius 188 ) 189 slider_frame.pack(side=ctk.TOP, padx=3, pady=3) 190 return slider_frame 191 192 @staticmethod 193 def validate_input(P: str) -> bool: 194 """Validation of R,G,B inputs from entry boxes. 195 196 Args: 197 P (str): New input character. 198 199 Returns: 200 bool: True if RGB encoding requirements are met, False otherwise. 201 """ 202 if P == '': 203 return True 204 if not P.isdigit(): 205 return False 206 if len(P) > 3: 207 return False 208 value = int(P) 209 if 0 <= value <= 255: 210 return True 211 return False 212 213 def r_g_b_sliders(self) -> None: 214 """Function creating sliders to change RGB values using interactive sliders. 215 """ 216 # sliders frame 217 vcmd = (self.register(self.validate_input), '%P') 218 frame: ctk.CTkFrame = ctk.CTkFrame( 219 master = self.main_frame, 220 fg_color = 'transparent' 221 ) 222 frame.pack(side=ctk.TOP) 223 # R value slider 224 slider_frame: ctk.CTkFrame = self.new_slider_frame(frame) 225 ctk.CTkLabel( 226 master = slider_frame, 227 text = 'R: ', 228 font = self.font if self.font else ctk.CTkFont('', self.font_size) 229 ).pack(side=ctk.LEFT, padx=3, pady=3) 230 self.r_val_label: ctk.CTkEntry = ctk.CTkEntry( 231 master = slider_frame, 232 validate = 'key', 233 validatecommand = vcmd, 234 corner_radius = self.corner_radius, 235 font = self.font if self.font else ctk.CTkFont('', self.font_size), 236 width = (self.font_size*3), 237 border_color = self.border_color 238 ) 239 self.r_val_label.pack(side=ctk.LEFT, padx=3, pady=3) 240 self.r_slider: ctk.CTkSlider = ctk.CTkSlider( 241 master = slider_frame, 242 from_ = 0, 243 to = 255, 244 number_of_steps = 255, 245 command = lambda e: self.slider_on_change(e, r=True), 246 button_corner_radius = self.corner_radius + 1 if self.corner_radius is not None else self.corner_radius, 247 button_length = 12, 248 corner_radius = self.corner_radius + 1 if self.corner_radius is not None else self.corner_radius, 249 button_color = self.slider_button_color, 250 hover = False, 251 progress_color = self.slider_progress_color, 252 fg_color = self.slider_fg_color 253 ) 254 # G value slider 255 slider_frame = self.new_slider_frame(frame) 256 ctk.CTkLabel(slider_frame, text='G: ', font=self.font if self.font else ctk.CTkFont('', self.font_size)).pack(side=ctk.LEFT, padx=3, pady=3) 257 self.g_val_label = ctk.CTkEntry( 258 master = slider_frame, 259 validate = 'key', 260 validatecommand = vcmd, 261 corner_radius = self.corner_radius, 262 font = self.font if self.font else ctk.CTkFont('', self.font_size), 263 width = (self.font_size*3), 264 border_color = self.border_color 265 ) 266 self.g_val_label.pack(side=ctk.LEFT, padx=3, pady=3) 267 self.g_slider: ctk.CTkSlider = ctk.CTkSlider( 268 master = slider_frame, 269 from_ = 0, 270 to = 255, 271 number_of_steps = 255, 272 command = lambda e: self.slider_on_change(e, g=True), 273 button_corner_radius = self.corner_radius + 1 if self.corner_radius is not None else self.corner_radius, 274 button_length = 12, 275 corner_radius = self.corner_radius + 1 if self.corner_radius is not None else self.corner_radius, 276 button_color = self.slider_button_color, 277 hover = False, 278 progress_color = self.slider_progress_color, 279 fg_color = self.slider_fg_color 280 ) 281 # B value slider 282 slider_frame = self.new_slider_frame(frame) 283 ctk.CTkLabel(slider_frame, text='B: ', font=self.font if self.font else ctk.CTkFont('', self.font_size)).pack(side=ctk.LEFT, padx=3, pady=3) 284 self.b_val_label: ctk.CTkEntry = ctk.CTkEntry( 285 master = slider_frame, 286 validate = 'key', 287 validatecommand = vcmd, 288 corner_radius = self.corner_radius, 289 font = self.font if self.font else ctk.CTkFont('', self.font_size), 290 width = (self.font_size*3), 291 border_color = self.border_color 292 ) 293 self.b_val_label.pack(side=ctk.LEFT, padx=3, pady=3) 294 self.b_slider: ctk.CTkSlider = ctk.CTkSlider( 295 master = slider_frame, 296 from_ = 0, 297 to = 255, 298 number_of_steps = 255, 299 command = lambda e: self.slider_on_change(e, b=True), 300 button_corner_radius = self.corner_radius + 1 if self.corner_radius is not None else self.corner_radius, 301 button_length = 12, 302 corner_radius = self.corner_radius + 1 if self.corner_radius is not None else self.corner_radius, 303 button_color = self.slider_button_color, 304 hover = False, 305 progress_color = self.slider_progress_color, 306 fg_color = self.slider_fg_color 307 ) 308 # initial setup of all labels 309 self.r_val_label.insert(0, self.r_val) 310 self.g_val_label.insert(0, self.g_val) 311 self.b_val_label.insert(0, self.b_val) 312 self.r_slider.set(self.r_val) 313 self.g_slider.set(self.g_val) 314 self.b_slider.set(self.b_val) 315 self.r_slider.pack(side=ctk.LEFT, padx=3, pady=3) 316 self.g_slider.pack(side=ctk.LEFT, padx=3, pady=3) 317 self.b_slider.pack(side=ctk.LEFT, padx=3, pady=3) 318 # binding for preview update 319 self.r_val_label.bind('<KeyRelease>', lambda e: self.update_sliders(e)) 320 self.g_val_label.bind('<KeyRelease>', lambda e: self.update_sliders(e)) 321 self.b_val_label.bind('<KeyRelease>', lambda e: self.update_sliders(e)) 322 self.r_val_label.bind('<KeyRelease>', lambda e: self.update_hex(e)) 323 self.g_val_label.bind('<KeyRelease>', lambda e: self.update_hex(e)) 324 self.b_val_label.bind('<KeyRelease>', lambda e: self.update_hex(e)) 325 326 def update_hex(self, event=None) -> None: 327 self.hex_val_label.delete(0, ctk.END) 328 self.hex_val_label.insert(0, f'{self.convert_to_hex()}') 329 330 def update_sliders(self, event=None, r: int=-1, g: int=-1, b: int=-1) -> None: 331 """Function updating position of sliders and its corresponding RGB entry boxes to proper value after changing hex entry box. 332 333 Args: 334 event (Any): Event type. Doesn't matter but is required parameter by customtkinter. 335 r (int, optional): Given red color intensity. Defaults to -1. 336 g (int, optional): Given green color intensity. Defaults to -1. 337 b (int, optional): Given blue color intensity. Defaults to -1. 338 """ 339 if r == -1: 340 r = int(self.r_val_label.get()) if self.r_val_label.get() != '' else 0 341 if g == -1: 342 g = int(self.g_val_label.get()) if self.g_val_label.get() != '' else 0 343 if b == -1: 344 b = int(self.b_val_label.get()) if self.b_val_label.get() != '' else 0 345 self.r_val = r 346 self.g_val = g 347 self.b_val = b 348 self.r_slider.set(r) if 0 < r <= 255 else self.r_slider.set(0) 349 self.g_slider.set(g) if 0 < g <= 255 else self.g_slider.set(0) 350 self.b_slider.set(b) if 0 < b <= 255 else self.b_slider.set(0) 351 self.color_prev_box.configure(fg_color=self.convert_to_hex()) 352 353 def slider_on_change(self, event, r: bool=False, g: bool=False, b: bool=False) -> None: 354 """Updates corresponding RGB color code and hex color value based on value of slider. 355 356 Args: 357 event (Any): Event type. Doesn't matter but is required parameter by customtkinter. 358 r (bool, optional): Flag to set which slider was changed [r]. Defaults to False. 359 g (bool, optional): Flag to set which slider was changed [g]. Defaults to False. 360 b (bool, optional): Flag to set which slider was changed [b]. Defaults to False. 361 """ 362 if r: 363 self.r_val = int(self.r_slider.get()) 364 self.r_val_label.delete(0, ctk.END) 365 self.r_val_label.insert(0, self.r_val) 366 elif g: 367 self.g_val = int(self.g_slider.get()) 368 self.g_val_label.delete(0, ctk.END) 369 self.g_val_label.insert(0, self.g_val) 370 elif b: 371 self.b_val = int(self.b_slider.get()) 372 self.b_val_label.delete(0, ctk.END) 373 self.b_val_label.insert(0, self.b_val) 374 self.color_prev_box.configure(fg_color=self.convert_to_hex()) 375 self.update_hex() 376 377 def convert_to_hex(self) -> str: 378 """Function converting RGB value to hex color code. 379 380 Returns: 381 str: f'{6 digit code}' | Regex example: ^#[0-9a-fA-F]{6} 382 """ 383 return f'#{self.r_val:02x}{self.g_val:02x}{self.b_val:02x}' 384 385 def convert_to_r_g_b(self) -> tuple[int, int , int]: 386 """Function converting hex color code to RGB value. 387 388 Returns: 389 tuple[int, int , int]: Tuple of R, G and B values. 390 """ 391 self.hex_val = self.hex_val_label.get() 392 hex_code: str = self.hex_val.lstrip('#') if self.hex_val else '000000' 393 r = int(hex_code[0:2], 16) 394 g = int(hex_code[2:4], 16) 395 b = int(hex_code[4:6], 16) 396 return (r, g, b) 397 398 def on_close(self) -> None: 399 """Custom closing function ensuring proper closing of the window. Sets hex_val to None to omit color change. 400 """ 401 self.hex_val = None 402 self.grab_release() 403 self.destroy() 404 405 def on_ok_button(self) -> None: 406 """Custom closing function. 407 """ 408 self.destroy() 409 410 def get_color(self) -> str | None: 411 """Function waiting for the window being destroyed. 412 413 Returns: 414 str | None: Hex color code if closed with OK button or None if closed with ❌. 415 """ 416 self.master.wait_window(self) 417 return self.convert_to_hex() if self.hex_val else None
8class ColorPicker(ctk.CTkToplevel): 9 """Class used to pick custom theme color. Is also a module that can be reused with apps using customtkinter. 10 11 Args: 12 13 - ctk.CTkTopLevel : Inheritance from customtkinter CTkTopLevel window. 14 """ 15 def __init__(self, fg_color: str | None=None, preview_size: int=100, r: int=0, g: int=0, b: int=0, font: ctk.CTkFont | None=None, 16 border_color: str | None=None, slider_button_color: str | None=None, slider_progress_color: str | None=None, slider_fg_color: str | None=None, 17 preview_border_color: str | None=None, button_fg_color: str | None=None, button_hover_color: str | None=None, icon: str | None=None, 18 corner_radius: int | None=None) -> None: 19 """Constructor handling most important function calls and variable setup. 20 21 Args: 22 fg_color (str | None, optional): Background color of the window. Defaults to None. 23 preview_size (int, optional): _description_. Defaults to 100. 24 r (int, optional): Default red intensity value. Defaults to 0. 25 g (int, optional): Default green intensity value. Defaults to 0. 26 b (int, optional): Default blue intensity value. Defaults to 0. 27 font (ctk.CTkFont | None, optional): Custom font. Defaults to None. 28 """ 29 super().__init__(fg_color=fg_color) 30 self.fg_color: str| None = fg_color 31 self.border_color: str| None = border_color 32 self.slider_button_color: str| None = slider_button_color 33 self.slider_progress_color: str| None = slider_progress_color 34 self.slider_fg_color: str| None = slider_fg_color 35 self.preview_border_color: str | None = preview_border_color 36 self.button_fg_color: str | None = button_fg_color 37 self.button_hover_color: str | None = button_hover_color 38 self.corner_radius: int | None = corner_radius 39 self.grab_set() 40 self.attributes('-topmost', True) 41 self.title('Color Picker') 42 self.font: ctk.CTkFont | None = font if font else None 43 self.font_size: int = font.cget('size') if font else 15 44 self.preview_size: int = preview_size 45 self.r_val: int = r 46 self.g_val: int = g 47 self.b_val: int = b 48 self.hex_val: str | None = self.convert_to_hex() 49 self.main_frame: ctk.CTkFrame = ctk.CTkFrame( 50 master = self, 51 corner_radius = 0, 52 fg_color = self.fg_color 53 ) 54 self.main_frame.pack(side=ctk.TOP, expand=True, ipadx=10, ipady=10) 55 self.color_preview() 56 self.r_g_b_sliders() 57 self.update_sliders(None) 58 self.bottom_frame: ctk.CTkFrame = ctk.CTkFrame( 59 master = self.main_frame, 60 corner_radius = self.corner_radius, 61 fg_color = 'transparent' 62 ) 63 self.bottom_frame.pack(side=ctk.BOTTOM, expand=True, ipadx=10, ipady=10) 64 self.hex_color_label() 65 self.ok_button() 66 self.resizable(False, False) 67 self.protocol('WM_DELETE_WINDOW', self.on_close) 68 self.lift() 69 self.center_window() 70 self.after(201, lambda: self.iconbitmap(icon)) 71 72 def center_window(self) -> None: 73 """Function centering the TopLevel window. Screen size independent. 74 """ 75 x: int = self.master.winfo_screenwidth() 76 y: int = self.master.winfo_screenheight() 77 app_width: int = self.winfo_width() 78 app_height: int = self.winfo_height() 79 self.geometry(f'+{(x//2)-app_width}+{(y//2)-app_height}') 80 81 def color_preview(self) -> None: 82 """Function creating frame for color preview. 83 """ 84 self.color_prev_box: ctk.CTkFrame = ctk.CTkFrame( 85 master = self.main_frame, 86 fg_color = self.convert_to_hex(), 87 border_width = 3, 88 width = self.preview_size, 89 height = self.preview_size, 90 corner_radius = self.corner_radius, 91 border_color = self.preview_border_color 92 ) 93 self.color_prev_box.pack(side=ctk.RIGHT, padx=3, pady=3, expand=True) 94 95 @staticmethod 96 def validate_hex_color(value_if_allowed: str) -> bool: 97 """Function validating new character in hex entry box. Can take one character or longer string to allow pasting. 98 99 Args: 100 value_if_allowed (str): New value to check. 101 102 Returns: 103 bool: True if hex color patter was met, False otherwise. 104 """ 105 if len(value_if_allowed) == 0 or (re.compile(r'^#[0-9a-fA-F]{0,6}$').match(value_if_allowed)): 106 for char in value_if_allowed[1:]: 107 if char not in '0123456789ABCDEFabcdef': 108 return False 109 return True 110 return False 111 112 def paste_hex_color(self, event) -> None: 113 """Function handling pasting custom color into hex color entry box. 114 115 Args: 116 event (Any): Event type. Doesn't matter but is required parameter by customtkinter. 117 """ 118 clipboard = self.master.clipboard_get() 119 if self.validate_hex_color(clipboard): 120 self.hex_val_label.delete(0, ctk.END) 121 self.hex_val_label.insert(0, clipboard) 122 return None 123 124 def update_on_hex(self, event) -> None: 125 """Function handling all changes on entering last hex color. It changes RGB labels values, sliders values and color preview frame to the desired color. 126 127 Args: 128 event (Any): Event type. Doesn't matter but is required parameter by customtkinter. 129 """ 130 if len(self.hex_val_label.get()) == 7: 131 self.r_val, self.g_val, self.b_val = self.convert_to_r_g_b() 132 self.r_val_label.delete(0, ctk.END) 133 self.g_val_label.delete(0, ctk.END) 134 self.b_val_label.delete(0, ctk.END) 135 self.r_val_label.insert(0, f'{self.r_val}') 136 self.g_val_label.insert(0, f'{self.g_val}') 137 self.b_val_label.insert(0, f'{self.b_val}') 138 self.update_sliders(None, r=self.r_val, g=self.g_val, b=self.b_val) 139 140 def hex_color_label(self) -> None: 141 """Function creating entry box for hex color. 142 """ 143 vcmd = (self.register(self.validate_hex_color), '%P') 144 ctk.CTkLabel(self.bottom_frame, text='Hex: ', font=self.font if self.font else ctk.CTkFont('', self.font_size)).pack(side=ctk.LEFT, padx=3, pady=3) 145 self.hex_val_label: ctk.CTkEntry = ctk.CTkEntry( 146 master = self.bottom_frame, 147 validate = 'key', 148 validatecommand = vcmd, 149 corner_radius = self.corner_radius, 150 font = self.font if self.font else ctk.CTkFont('', self.font_size), 151 width = (self.font_size*8), 152 border_color = self.border_color 153 ) 154 self.hex_val_label.pack(side=ctk.LEFT, padx=3, pady=3) 155 self.hex_val_label.insert(0, f'{self.hex_val}') 156 self.hex_val_label.bind('<Control-v>', self.paste_hex_color) 157 self.hex_val_label.bind('<KeyRelease>', lambda e: self.update_on_hex(e)) 158 159 def ok_button(self) -> None: 160 """Function creating 'OK' button. After clicking the button if color is selected properly the value will be returned in master script. 161 """ 162 ok_button: ctk.CTkButton = ctk.CTkButton( 163 master = self.bottom_frame, 164 text = 'Ok', 165 command = self.on_ok_button, 166 font = self.font if self.font else ctk.CTkFont('', self.font_size), 167 width = (self.font_size*3), 168 corner_radius = self.corner_radius, 169 border_width = 2, 170 border_color = self.border_color, 171 fg_color = self.button_fg_color, 172 hover_color = self.button_hover_color 173 ) 174 ok_button.pack(side=ctk.RIGHT, padx=3, pady=3) 175 176 def new_slider_frame(self, frame: ctk.CTkFrame) -> ctk.CTkFrame: 177 """Function creating frame for slider used to change R or G or B value. 178 179 Args: 180 frame (ctk.CTkFrame): Parent Frame on which cell will be represented. 181 182 Returns: 183 ctk.CTkFrame: Ready packed frame. 184 """ 185 slider_frame: ctk.CTkFrame = ctk.CTkFrame( 186 master = frame, 187 fg_color = 'transparent', 188 corner_radius = self.corner_radius 189 ) 190 slider_frame.pack(side=ctk.TOP, padx=3, pady=3) 191 return slider_frame 192 193 @staticmethod 194 def validate_input(P: str) -> bool: 195 """Validation of R,G,B inputs from entry boxes. 196 197 Args: 198 P (str): New input character. 199 200 Returns: 201 bool: True if RGB encoding requirements are met, False otherwise. 202 """ 203 if P == '': 204 return True 205 if not P.isdigit(): 206 return False 207 if len(P) > 3: 208 return False 209 value = int(P) 210 if 0 <= value <= 255: 211 return True 212 return False 213 214 def r_g_b_sliders(self) -> None: 215 """Function creating sliders to change RGB values using interactive sliders. 216 """ 217 # sliders frame 218 vcmd = (self.register(self.validate_input), '%P') 219 frame: ctk.CTkFrame = ctk.CTkFrame( 220 master = self.main_frame, 221 fg_color = 'transparent' 222 ) 223 frame.pack(side=ctk.TOP) 224 # R value slider 225 slider_frame: ctk.CTkFrame = self.new_slider_frame(frame) 226 ctk.CTkLabel( 227 master = slider_frame, 228 text = 'R: ', 229 font = self.font if self.font else ctk.CTkFont('', self.font_size) 230 ).pack(side=ctk.LEFT, padx=3, pady=3) 231 self.r_val_label: ctk.CTkEntry = ctk.CTkEntry( 232 master = slider_frame, 233 validate = 'key', 234 validatecommand = vcmd, 235 corner_radius = self.corner_radius, 236 font = self.font if self.font else ctk.CTkFont('', self.font_size), 237 width = (self.font_size*3), 238 border_color = self.border_color 239 ) 240 self.r_val_label.pack(side=ctk.LEFT, padx=3, pady=3) 241 self.r_slider: ctk.CTkSlider = ctk.CTkSlider( 242 master = slider_frame, 243 from_ = 0, 244 to = 255, 245 number_of_steps = 255, 246 command = lambda e: self.slider_on_change(e, r=True), 247 button_corner_radius = self.corner_radius + 1 if self.corner_radius is not None else self.corner_radius, 248 button_length = 12, 249 corner_radius = self.corner_radius + 1 if self.corner_radius is not None else self.corner_radius, 250 button_color = self.slider_button_color, 251 hover = False, 252 progress_color = self.slider_progress_color, 253 fg_color = self.slider_fg_color 254 ) 255 # G value slider 256 slider_frame = self.new_slider_frame(frame) 257 ctk.CTkLabel(slider_frame, text='G: ', font=self.font if self.font else ctk.CTkFont('', self.font_size)).pack(side=ctk.LEFT, padx=3, pady=3) 258 self.g_val_label = ctk.CTkEntry( 259 master = slider_frame, 260 validate = 'key', 261 validatecommand = vcmd, 262 corner_radius = self.corner_radius, 263 font = self.font if self.font else ctk.CTkFont('', self.font_size), 264 width = (self.font_size*3), 265 border_color = self.border_color 266 ) 267 self.g_val_label.pack(side=ctk.LEFT, padx=3, pady=3) 268 self.g_slider: ctk.CTkSlider = ctk.CTkSlider( 269 master = slider_frame, 270 from_ = 0, 271 to = 255, 272 number_of_steps = 255, 273 command = lambda e: self.slider_on_change(e, g=True), 274 button_corner_radius = self.corner_radius + 1 if self.corner_radius is not None else self.corner_radius, 275 button_length = 12, 276 corner_radius = self.corner_radius + 1 if self.corner_radius is not None else self.corner_radius, 277 button_color = self.slider_button_color, 278 hover = False, 279 progress_color = self.slider_progress_color, 280 fg_color = self.slider_fg_color 281 ) 282 # B value slider 283 slider_frame = self.new_slider_frame(frame) 284 ctk.CTkLabel(slider_frame, text='B: ', font=self.font if self.font else ctk.CTkFont('', self.font_size)).pack(side=ctk.LEFT, padx=3, pady=3) 285 self.b_val_label: ctk.CTkEntry = ctk.CTkEntry( 286 master = slider_frame, 287 validate = 'key', 288 validatecommand = vcmd, 289 corner_radius = self.corner_radius, 290 font = self.font if self.font else ctk.CTkFont('', self.font_size), 291 width = (self.font_size*3), 292 border_color = self.border_color 293 ) 294 self.b_val_label.pack(side=ctk.LEFT, padx=3, pady=3) 295 self.b_slider: ctk.CTkSlider = ctk.CTkSlider( 296 master = slider_frame, 297 from_ = 0, 298 to = 255, 299 number_of_steps = 255, 300 command = lambda e: self.slider_on_change(e, b=True), 301 button_corner_radius = self.corner_radius + 1 if self.corner_radius is not None else self.corner_radius, 302 button_length = 12, 303 corner_radius = self.corner_radius + 1 if self.corner_radius is not None else self.corner_radius, 304 button_color = self.slider_button_color, 305 hover = False, 306 progress_color = self.slider_progress_color, 307 fg_color = self.slider_fg_color 308 ) 309 # initial setup of all labels 310 self.r_val_label.insert(0, self.r_val) 311 self.g_val_label.insert(0, self.g_val) 312 self.b_val_label.insert(0, self.b_val) 313 self.r_slider.set(self.r_val) 314 self.g_slider.set(self.g_val) 315 self.b_slider.set(self.b_val) 316 self.r_slider.pack(side=ctk.LEFT, padx=3, pady=3) 317 self.g_slider.pack(side=ctk.LEFT, padx=3, pady=3) 318 self.b_slider.pack(side=ctk.LEFT, padx=3, pady=3) 319 # binding for preview update 320 self.r_val_label.bind('<KeyRelease>', lambda e: self.update_sliders(e)) 321 self.g_val_label.bind('<KeyRelease>', lambda e: self.update_sliders(e)) 322 self.b_val_label.bind('<KeyRelease>', lambda e: self.update_sliders(e)) 323 self.r_val_label.bind('<KeyRelease>', lambda e: self.update_hex(e)) 324 self.g_val_label.bind('<KeyRelease>', lambda e: self.update_hex(e)) 325 self.b_val_label.bind('<KeyRelease>', lambda e: self.update_hex(e)) 326 327 def update_hex(self, event=None) -> None: 328 self.hex_val_label.delete(0, ctk.END) 329 self.hex_val_label.insert(0, f'{self.convert_to_hex()}') 330 331 def update_sliders(self, event=None, r: int=-1, g: int=-1, b: int=-1) -> None: 332 """Function updating position of sliders and its corresponding RGB entry boxes to proper value after changing hex entry box. 333 334 Args: 335 event (Any): Event type. Doesn't matter but is required parameter by customtkinter. 336 r (int, optional): Given red color intensity. Defaults to -1. 337 g (int, optional): Given green color intensity. Defaults to -1. 338 b (int, optional): Given blue color intensity. Defaults to -1. 339 """ 340 if r == -1: 341 r = int(self.r_val_label.get()) if self.r_val_label.get() != '' else 0 342 if g == -1: 343 g = int(self.g_val_label.get()) if self.g_val_label.get() != '' else 0 344 if b == -1: 345 b = int(self.b_val_label.get()) if self.b_val_label.get() != '' else 0 346 self.r_val = r 347 self.g_val = g 348 self.b_val = b 349 self.r_slider.set(r) if 0 < r <= 255 else self.r_slider.set(0) 350 self.g_slider.set(g) if 0 < g <= 255 else self.g_slider.set(0) 351 self.b_slider.set(b) if 0 < b <= 255 else self.b_slider.set(0) 352 self.color_prev_box.configure(fg_color=self.convert_to_hex()) 353 354 def slider_on_change(self, event, r: bool=False, g: bool=False, b: bool=False) -> None: 355 """Updates corresponding RGB color code and hex color value based on value of slider. 356 357 Args: 358 event (Any): Event type. Doesn't matter but is required parameter by customtkinter. 359 r (bool, optional): Flag to set which slider was changed [r]. Defaults to False. 360 g (bool, optional): Flag to set which slider was changed [g]. Defaults to False. 361 b (bool, optional): Flag to set which slider was changed [b]. Defaults to False. 362 """ 363 if r: 364 self.r_val = int(self.r_slider.get()) 365 self.r_val_label.delete(0, ctk.END) 366 self.r_val_label.insert(0, self.r_val) 367 elif g: 368 self.g_val = int(self.g_slider.get()) 369 self.g_val_label.delete(0, ctk.END) 370 self.g_val_label.insert(0, self.g_val) 371 elif b: 372 self.b_val = int(self.b_slider.get()) 373 self.b_val_label.delete(0, ctk.END) 374 self.b_val_label.insert(0, self.b_val) 375 self.color_prev_box.configure(fg_color=self.convert_to_hex()) 376 self.update_hex() 377 378 def convert_to_hex(self) -> str: 379 """Function converting RGB value to hex color code. 380 381 Returns: 382 str: f'{6 digit code}' | Regex example: ^#[0-9a-fA-F]{6} 383 """ 384 return f'#{self.r_val:02x}{self.g_val:02x}{self.b_val:02x}' 385 386 def convert_to_r_g_b(self) -> tuple[int, int , int]: 387 """Function converting hex color code to RGB value. 388 389 Returns: 390 tuple[int, int , int]: Tuple of R, G and B values. 391 """ 392 self.hex_val = self.hex_val_label.get() 393 hex_code: str = self.hex_val.lstrip('#') if self.hex_val else '000000' 394 r = int(hex_code[0:2], 16) 395 g = int(hex_code[2:4], 16) 396 b = int(hex_code[4:6], 16) 397 return (r, g, b) 398 399 def on_close(self) -> None: 400 """Custom closing function ensuring proper closing of the window. Sets hex_val to None to omit color change. 401 """ 402 self.hex_val = None 403 self.grab_release() 404 self.destroy() 405 406 def on_ok_button(self) -> None: 407 """Custom closing function. 408 """ 409 self.destroy() 410 411 def get_color(self) -> str | None: 412 """Function waiting for the window being destroyed. 413 414 Returns: 415 str | None: Hex color code if closed with OK button or None if closed with ❌. 416 """ 417 self.master.wait_window(self) 418 return self.convert_to_hex() if self.hex_val else None
Class used to pick custom theme color. Is also a module that can be reused with apps using customtkinter.
Arguments:
- - ctk.CTkTopLevel : Inheritance from customtkinter CTkTopLevel window.
15 def __init__(self, fg_color: str | None=None, preview_size: int=100, r: int=0, g: int=0, b: int=0, font: ctk.CTkFont | None=None, 16 border_color: str | None=None, slider_button_color: str | None=None, slider_progress_color: str | None=None, slider_fg_color: str | None=None, 17 preview_border_color: str | None=None, button_fg_color: str | None=None, button_hover_color: str | None=None, icon: str | None=None, 18 corner_radius: int | None=None) -> None: 19 """Constructor handling most important function calls and variable setup. 20 21 Args: 22 fg_color (str | None, optional): Background color of the window. Defaults to None. 23 preview_size (int, optional): _description_. Defaults to 100. 24 r (int, optional): Default red intensity value. Defaults to 0. 25 g (int, optional): Default green intensity value. Defaults to 0. 26 b (int, optional): Default blue intensity value. Defaults to 0. 27 font (ctk.CTkFont | None, optional): Custom font. Defaults to None. 28 """ 29 super().__init__(fg_color=fg_color) 30 self.fg_color: str| None = fg_color 31 self.border_color: str| None = border_color 32 self.slider_button_color: str| None = slider_button_color 33 self.slider_progress_color: str| None = slider_progress_color 34 self.slider_fg_color: str| None = slider_fg_color 35 self.preview_border_color: str | None = preview_border_color 36 self.button_fg_color: str | None = button_fg_color 37 self.button_hover_color: str | None = button_hover_color 38 self.corner_radius: int | None = corner_radius 39 self.grab_set() 40 self.attributes('-topmost', True) 41 self.title('Color Picker') 42 self.font: ctk.CTkFont | None = font if font else None 43 self.font_size: int = font.cget('size') if font else 15 44 self.preview_size: int = preview_size 45 self.r_val: int = r 46 self.g_val: int = g 47 self.b_val: int = b 48 self.hex_val: str | None = self.convert_to_hex() 49 self.main_frame: ctk.CTkFrame = ctk.CTkFrame( 50 master = self, 51 corner_radius = 0, 52 fg_color = self.fg_color 53 ) 54 self.main_frame.pack(side=ctk.TOP, expand=True, ipadx=10, ipady=10) 55 self.color_preview() 56 self.r_g_b_sliders() 57 self.update_sliders(None) 58 self.bottom_frame: ctk.CTkFrame = ctk.CTkFrame( 59 master = self.main_frame, 60 corner_radius = self.corner_radius, 61 fg_color = 'transparent' 62 ) 63 self.bottom_frame.pack(side=ctk.BOTTOM, expand=True, ipadx=10, ipady=10) 64 self.hex_color_label() 65 self.ok_button() 66 self.resizable(False, False) 67 self.protocol('WM_DELETE_WINDOW', self.on_close) 68 self.lift() 69 self.center_window() 70 self.after(201, lambda: self.iconbitmap(icon))
Constructor handling most important function calls and variable setup.
Arguments:
- fg_color (str | None, optional): Background color of the window. Defaults to None.
- preview_size (int, optional): _description_. Defaults to 100.
- r (int, optional): Default red intensity value. Defaults to 0.
- g (int, optional): Default green intensity value. Defaults to 0.
- b (int, optional): Default blue intensity value. Defaults to 0.
- font (ctk.CTkFont | None, optional): Custom font. Defaults to None.
72 def center_window(self) -> None: 73 """Function centering the TopLevel window. Screen size independent. 74 """ 75 x: int = self.master.winfo_screenwidth() 76 y: int = self.master.winfo_screenheight() 77 app_width: int = self.winfo_width() 78 app_height: int = self.winfo_height() 79 self.geometry(f'+{(x//2)-app_width}+{(y//2)-app_height}')
Function centering the TopLevel window. Screen size independent.
81 def color_preview(self) -> None: 82 """Function creating frame for color preview. 83 """ 84 self.color_prev_box: ctk.CTkFrame = ctk.CTkFrame( 85 master = self.main_frame, 86 fg_color = self.convert_to_hex(), 87 border_width = 3, 88 width = self.preview_size, 89 height = self.preview_size, 90 corner_radius = self.corner_radius, 91 border_color = self.preview_border_color 92 ) 93 self.color_prev_box.pack(side=ctk.RIGHT, padx=3, pady=3, expand=True)
Function creating frame for color preview.
95 @staticmethod 96 def validate_hex_color(value_if_allowed: str) -> bool: 97 """Function validating new character in hex entry box. Can take one character or longer string to allow pasting. 98 99 Args: 100 value_if_allowed (str): New value to check. 101 102 Returns: 103 bool: True if hex color patter was met, False otherwise. 104 """ 105 if len(value_if_allowed) == 0 or (re.compile(r'^#[0-9a-fA-F]{0,6}$').match(value_if_allowed)): 106 for char in value_if_allowed[1:]: 107 if char not in '0123456789ABCDEFabcdef': 108 return False 109 return True 110 return False
Function validating new character in hex entry box. Can take one character or longer string to allow pasting.
Arguments:
- value_if_allowed (str): New value to check.
Returns:
bool: True if hex color patter was met, False otherwise.
112 def paste_hex_color(self, event) -> None: 113 """Function handling pasting custom color into hex color entry box. 114 115 Args: 116 event (Any): Event type. Doesn't matter but is required parameter by customtkinter. 117 """ 118 clipboard = self.master.clipboard_get() 119 if self.validate_hex_color(clipboard): 120 self.hex_val_label.delete(0, ctk.END) 121 self.hex_val_label.insert(0, clipboard) 122 return None
Function handling pasting custom color into hex color entry box.
Arguments:
- event (Any): Event type. Doesn't matter but is required parameter by customtkinter.
124 def update_on_hex(self, event) -> None: 125 """Function handling all changes on entering last hex color. It changes RGB labels values, sliders values and color preview frame to the desired color. 126 127 Args: 128 event (Any): Event type. Doesn't matter but is required parameter by customtkinter. 129 """ 130 if len(self.hex_val_label.get()) == 7: 131 self.r_val, self.g_val, self.b_val = self.convert_to_r_g_b() 132 self.r_val_label.delete(0, ctk.END) 133 self.g_val_label.delete(0, ctk.END) 134 self.b_val_label.delete(0, ctk.END) 135 self.r_val_label.insert(0, f'{self.r_val}') 136 self.g_val_label.insert(0, f'{self.g_val}') 137 self.b_val_label.insert(0, f'{self.b_val}') 138 self.update_sliders(None, r=self.r_val, g=self.g_val, b=self.b_val)
Function handling all changes on entering last hex color. It changes RGB labels values, sliders values and color preview frame to the desired color.
Arguments:
- event (Any): Event type. Doesn't matter but is required parameter by customtkinter.
140 def hex_color_label(self) -> None: 141 """Function creating entry box for hex color. 142 """ 143 vcmd = (self.register(self.validate_hex_color), '%P') 144 ctk.CTkLabel(self.bottom_frame, text='Hex: ', font=self.font if self.font else ctk.CTkFont('', self.font_size)).pack(side=ctk.LEFT, padx=3, pady=3) 145 self.hex_val_label: ctk.CTkEntry = ctk.CTkEntry( 146 master = self.bottom_frame, 147 validate = 'key', 148 validatecommand = vcmd, 149 corner_radius = self.corner_radius, 150 font = self.font if self.font else ctk.CTkFont('', self.font_size), 151 width = (self.font_size*8), 152 border_color = self.border_color 153 ) 154 self.hex_val_label.pack(side=ctk.LEFT, padx=3, pady=3) 155 self.hex_val_label.insert(0, f'{self.hex_val}') 156 self.hex_val_label.bind('<Control-v>', self.paste_hex_color) 157 self.hex_val_label.bind('<KeyRelease>', lambda e: self.update_on_hex(e))
Function creating entry box for hex color.
176 def new_slider_frame(self, frame: ctk.CTkFrame) -> ctk.CTkFrame: 177 """Function creating frame for slider used to change R or G or B value. 178 179 Args: 180 frame (ctk.CTkFrame): Parent Frame on which cell will be represented. 181 182 Returns: 183 ctk.CTkFrame: Ready packed frame. 184 """ 185 slider_frame: ctk.CTkFrame = ctk.CTkFrame( 186 master = frame, 187 fg_color = 'transparent', 188 corner_radius = self.corner_radius 189 ) 190 slider_frame.pack(side=ctk.TOP, padx=3, pady=3) 191 return slider_frame
Function creating frame for slider used to change R or G or B value.
Arguments:
- frame (ctk.CTkFrame): Parent Frame on which cell will be represented.
Returns:
ctk.CTkFrame: Ready packed frame.
193 @staticmethod 194 def validate_input(P: str) -> bool: 195 """Validation of R,G,B inputs from entry boxes. 196 197 Args: 198 P (str): New input character. 199 200 Returns: 201 bool: True if RGB encoding requirements are met, False otherwise. 202 """ 203 if P == '': 204 return True 205 if not P.isdigit(): 206 return False 207 if len(P) > 3: 208 return False 209 value = int(P) 210 if 0 <= value <= 255: 211 return True 212 return False
Validation of R,G,B inputs from entry boxes.
Arguments:
- P (str): New input character.
Returns:
bool: True if RGB encoding requirements are met, False otherwise.
214 def r_g_b_sliders(self) -> None: 215 """Function creating sliders to change RGB values using interactive sliders. 216 """ 217 # sliders frame 218 vcmd = (self.register(self.validate_input), '%P') 219 frame: ctk.CTkFrame = ctk.CTkFrame( 220 master = self.main_frame, 221 fg_color = 'transparent' 222 ) 223 frame.pack(side=ctk.TOP) 224 # R value slider 225 slider_frame: ctk.CTkFrame = self.new_slider_frame(frame) 226 ctk.CTkLabel( 227 master = slider_frame, 228 text = 'R: ', 229 font = self.font if self.font else ctk.CTkFont('', self.font_size) 230 ).pack(side=ctk.LEFT, padx=3, pady=3) 231 self.r_val_label: ctk.CTkEntry = ctk.CTkEntry( 232 master = slider_frame, 233 validate = 'key', 234 validatecommand = vcmd, 235 corner_radius = self.corner_radius, 236 font = self.font if self.font else ctk.CTkFont('', self.font_size), 237 width = (self.font_size*3), 238 border_color = self.border_color 239 ) 240 self.r_val_label.pack(side=ctk.LEFT, padx=3, pady=3) 241 self.r_slider: ctk.CTkSlider = ctk.CTkSlider( 242 master = slider_frame, 243 from_ = 0, 244 to = 255, 245 number_of_steps = 255, 246 command = lambda e: self.slider_on_change(e, r=True), 247 button_corner_radius = self.corner_radius + 1 if self.corner_radius is not None else self.corner_radius, 248 button_length = 12, 249 corner_radius = self.corner_radius + 1 if self.corner_radius is not None else self.corner_radius, 250 button_color = self.slider_button_color, 251 hover = False, 252 progress_color = self.slider_progress_color, 253 fg_color = self.slider_fg_color 254 ) 255 # G value slider 256 slider_frame = self.new_slider_frame(frame) 257 ctk.CTkLabel(slider_frame, text='G: ', font=self.font if self.font else ctk.CTkFont('', self.font_size)).pack(side=ctk.LEFT, padx=3, pady=3) 258 self.g_val_label = ctk.CTkEntry( 259 master = slider_frame, 260 validate = 'key', 261 validatecommand = vcmd, 262 corner_radius = self.corner_radius, 263 font = self.font if self.font else ctk.CTkFont('', self.font_size), 264 width = (self.font_size*3), 265 border_color = self.border_color 266 ) 267 self.g_val_label.pack(side=ctk.LEFT, padx=3, pady=3) 268 self.g_slider: ctk.CTkSlider = ctk.CTkSlider( 269 master = slider_frame, 270 from_ = 0, 271 to = 255, 272 number_of_steps = 255, 273 command = lambda e: self.slider_on_change(e, g=True), 274 button_corner_radius = self.corner_radius + 1 if self.corner_radius is not None else self.corner_radius, 275 button_length = 12, 276 corner_radius = self.corner_radius + 1 if self.corner_radius is not None else self.corner_radius, 277 button_color = self.slider_button_color, 278 hover = False, 279 progress_color = self.slider_progress_color, 280 fg_color = self.slider_fg_color 281 ) 282 # B value slider 283 slider_frame = self.new_slider_frame(frame) 284 ctk.CTkLabel(slider_frame, text='B: ', font=self.font if self.font else ctk.CTkFont('', self.font_size)).pack(side=ctk.LEFT, padx=3, pady=3) 285 self.b_val_label: ctk.CTkEntry = ctk.CTkEntry( 286 master = slider_frame, 287 validate = 'key', 288 validatecommand = vcmd, 289 corner_radius = self.corner_radius, 290 font = self.font if self.font else ctk.CTkFont('', self.font_size), 291 width = (self.font_size*3), 292 border_color = self.border_color 293 ) 294 self.b_val_label.pack(side=ctk.LEFT, padx=3, pady=3) 295 self.b_slider: ctk.CTkSlider = ctk.CTkSlider( 296 master = slider_frame, 297 from_ = 0, 298 to = 255, 299 number_of_steps = 255, 300 command = lambda e: self.slider_on_change(e, b=True), 301 button_corner_radius = self.corner_radius + 1 if self.corner_radius is not None else self.corner_radius, 302 button_length = 12, 303 corner_radius = self.corner_radius + 1 if self.corner_radius is not None else self.corner_radius, 304 button_color = self.slider_button_color, 305 hover = False, 306 progress_color = self.slider_progress_color, 307 fg_color = self.slider_fg_color 308 ) 309 # initial setup of all labels 310 self.r_val_label.insert(0, self.r_val) 311 self.g_val_label.insert(0, self.g_val) 312 self.b_val_label.insert(0, self.b_val) 313 self.r_slider.set(self.r_val) 314 self.g_slider.set(self.g_val) 315 self.b_slider.set(self.b_val) 316 self.r_slider.pack(side=ctk.LEFT, padx=3, pady=3) 317 self.g_slider.pack(side=ctk.LEFT, padx=3, pady=3) 318 self.b_slider.pack(side=ctk.LEFT, padx=3, pady=3) 319 # binding for preview update 320 self.r_val_label.bind('<KeyRelease>', lambda e: self.update_sliders(e)) 321 self.g_val_label.bind('<KeyRelease>', lambda e: self.update_sliders(e)) 322 self.b_val_label.bind('<KeyRelease>', lambda e: self.update_sliders(e)) 323 self.r_val_label.bind('<KeyRelease>', lambda e: self.update_hex(e)) 324 self.g_val_label.bind('<KeyRelease>', lambda e: self.update_hex(e)) 325 self.b_val_label.bind('<KeyRelease>', lambda e: self.update_hex(e))
Function creating sliders to change RGB values using interactive sliders.
331 def update_sliders(self, event=None, r: int=-1, g: int=-1, b: int=-1) -> None: 332 """Function updating position of sliders and its corresponding RGB entry boxes to proper value after changing hex entry box. 333 334 Args: 335 event (Any): Event type. Doesn't matter but is required parameter by customtkinter. 336 r (int, optional): Given red color intensity. Defaults to -1. 337 g (int, optional): Given green color intensity. Defaults to -1. 338 b (int, optional): Given blue color intensity. Defaults to -1. 339 """ 340 if r == -1: 341 r = int(self.r_val_label.get()) if self.r_val_label.get() != '' else 0 342 if g == -1: 343 g = int(self.g_val_label.get()) if self.g_val_label.get() != '' else 0 344 if b == -1: 345 b = int(self.b_val_label.get()) if self.b_val_label.get() != '' else 0 346 self.r_val = r 347 self.g_val = g 348 self.b_val = b 349 self.r_slider.set(r) if 0 < r <= 255 else self.r_slider.set(0) 350 self.g_slider.set(g) if 0 < g <= 255 else self.g_slider.set(0) 351 self.b_slider.set(b) if 0 < b <= 255 else self.b_slider.set(0) 352 self.color_prev_box.configure(fg_color=self.convert_to_hex())
Function updating position of sliders and its corresponding RGB entry boxes to proper value after changing hex entry box.
Arguments:
- event (Any): Event type. Doesn't matter but is required parameter by customtkinter.
- r (int, optional): Given red color intensity. Defaults to -1.
- g (int, optional): Given green color intensity. Defaults to -1.
- b (int, optional): Given blue color intensity. Defaults to -1.
354 def slider_on_change(self, event, r: bool=False, g: bool=False, b: bool=False) -> None: 355 """Updates corresponding RGB color code and hex color value based on value of slider. 356 357 Args: 358 event (Any): Event type. Doesn't matter but is required parameter by customtkinter. 359 r (bool, optional): Flag to set which slider was changed [r]. Defaults to False. 360 g (bool, optional): Flag to set which slider was changed [g]. Defaults to False. 361 b (bool, optional): Flag to set which slider was changed [b]. Defaults to False. 362 """ 363 if r: 364 self.r_val = int(self.r_slider.get()) 365 self.r_val_label.delete(0, ctk.END) 366 self.r_val_label.insert(0, self.r_val) 367 elif g: 368 self.g_val = int(self.g_slider.get()) 369 self.g_val_label.delete(0, ctk.END) 370 self.g_val_label.insert(0, self.g_val) 371 elif b: 372 self.b_val = int(self.b_slider.get()) 373 self.b_val_label.delete(0, ctk.END) 374 self.b_val_label.insert(0, self.b_val) 375 self.color_prev_box.configure(fg_color=self.convert_to_hex()) 376 self.update_hex()
Updates corresponding RGB color code and hex color value based on value of slider.
Arguments:
- event (Any): Event type. Doesn't matter but is required parameter by customtkinter.
- r (bool, optional): Flag to set which slider was changed [r]. Defaults to False.
- g (bool, optional): Flag to set which slider was changed [g]. Defaults to False.
- b (bool, optional): Flag to set which slider was changed [b]. Defaults to False.
378 def convert_to_hex(self) -> str: 379 """Function converting RGB value to hex color code. 380 381 Returns: 382 str: f'{6 digit code}' | Regex example: ^#[0-9a-fA-F]{6} 383 """ 384 return f'#{self.r_val:02x}{self.g_val:02x}{self.b_val:02x}'
Function converting RGB value to hex color code.
Returns:
str: f'{6 digit code}' | Regex example: ^#[0-9a-fA-F]{6}
386 def convert_to_r_g_b(self) -> tuple[int, int , int]: 387 """Function converting hex color code to RGB value. 388 389 Returns: 390 tuple[int, int , int]: Tuple of R, G and B values. 391 """ 392 self.hex_val = self.hex_val_label.get() 393 hex_code: str = self.hex_val.lstrip('#') if self.hex_val else '000000' 394 r = int(hex_code[0:2], 16) 395 g = int(hex_code[2:4], 16) 396 b = int(hex_code[4:6], 16) 397 return (r, g, b)
Function converting hex color code to RGB value.
Returns:
tuple[int, int , int]: Tuple of R, G and B values.
399 def on_close(self) -> None: 400 """Custom closing function ensuring proper closing of the window. Sets hex_val to None to omit color change. 401 """ 402 self.hex_val = None 403 self.grab_release() 404 self.destroy()
Custom closing function ensuring proper closing of the window. Sets hex_val to None to omit color change.
411 def get_color(self) -> str | None: 412 """Function waiting for the window being destroyed. 413 414 Returns: 415 str | None: Hex color code if closed with OK button or None if closed with ❌. 416 """ 417 self.master.wait_window(self) 418 return self.convert_to_hex() if self.hex_val else None
Function waiting for the window being destroyed.
Returns:
str | None: Hex color code if closed with OK button or None if closed with ❌.