diff --git a/customtkinter/__init__.py b/customtkinter/__init__.py index b9e73f1..120e8eb 100644 --- a/customtkinter/__init__.py +++ b/customtkinter/__init__.py @@ -52,6 +52,7 @@ from .widgets.ctk_radiobutton import CTkRadioButton from .widgets.ctk_canvas import CTkCanvas from .widgets.ctk_switch import CTkSwitch from .widgets.ctk_optionmenu import CTkOptionMenu +from .widgets.ctk_combobox import CTkComboBox # import windows from .windows.ctk_tk import CTk diff --git a/customtkinter/assets/themes/blue.json b/customtkinter/assets/themes/blue.json index b4d1106..41ce683 100644 --- a/customtkinter/assets/themes/blue.json +++ b/customtkinter/assets/themes/blue.json @@ -29,8 +29,10 @@ "switch_button_hover": ["gray20", "gray100"], "optionmenu_button": ["#36719F", "#144870"], "optionmenu_button_hover": ["#27577D", "#203A4F"], + "combobox_border": ["#979DA2", "#565B5E"], + "combobox_button_hover": ["#6E7174", "#7A848D"], "dropdown_color": ["#A8ACB1", "#535353"], - "dropdown_hover": ["#D6DCE2", "#46484A"], + "dropdown_hover": ["#D6DCE2", "#393D40"], "dropdown_text": ["gray10", "#DCE4EE"] }, "text": { diff --git a/customtkinter/draw_engine.py b/customtkinter/draw_engine.py index a8a68af..dc07aa3 100644 --- a/customtkinter/draw_engine.py +++ b/customtkinter/draw_engine.py @@ -54,14 +54,14 @@ class DrawEngine: else: return user_corner_radius - def draw_rounded_rect_with_border(self, width: int, height: int, corner_radius: Union[float, int], border_width: Union[float, int], - overwrite_preferred_drawing_method: str = None) -> bool: + def draw_rounded_rect_with_border(self, width: Union[float, int], height: Union[float, int], corner_radius: Union[float, int], + border_width: Union[float, int], overwrite_preferred_drawing_method: str = None) -> bool: """ Draws a rounded rectangle with a corner_radius and border_width on the canvas. The border elements have a 'border_parts' tag, the main foreground elements have an 'inner_parts' tag to color the elements accordingly. returns bool if recoloring is necessary """ - width = math.floor(width / 2) * 2 # round (floor) current_width and current_height and restrict them to even values only + width = math.floor(width / 2) * 2 # round (floor) _current_width and _current_height and restrict them to even values only height = math.floor(height / 2) * 2 corner_radius = round(corner_radius) @@ -353,8 +353,8 @@ class DrawEngine: return requires_recoloring - def draw_rounded_rect_with_border_vertical_split(self, width: int, height: int, corner_radius: Union[float, int], border_width: Union[float, int], - left_section_width: int) -> bool: + def draw_rounded_rect_with_border_vertical_split(self, width: Union[float, int], height: Union[float, int], corner_radius: Union[float, int], + border_width: Union[float, int], left_section_width: int) -> bool: """ Draws a rounded rectangle with a corner_radius and border_width on the canvas which is split at left_section_width. The border elements have the tags 'border_parts_left', 'border_parts_lright', the main foreground elements have an 'inner_parts_left' and inner_parts_right' tag, @@ -362,7 +362,7 @@ class DrawEngine: returns bool if recoloring is necessary """ - width = math.floor(width / 2) * 2 # round (floor) current_width and current_height and restrict them to even values only + width = math.floor(width / 2) * 2 # round (floor) _current_width and _current_height and restrict them to even values only height = math.floor(height / 2) * 2 corner_radius = round(corner_radius) @@ -394,10 +394,10 @@ class DrawEngine: # create border button parts (only if border exists) if border_width > 0: if not self._canvas.find_withtag("border_parts"): - self._canvas.create_polygon((0, 0, 0, 0), tags=("border_line_left_1", "border_parts_left", "border_parts")) - self._canvas.create_polygon((0, 0, 0, 0), tags=("border_line_right_1", "border_parts_right", "border_parts")) - self._canvas.create_rectangle((0, 0, 0, 0), tags=("border_rect_left_1", "border_parts_left", "border_parts")) - self._canvas.create_rectangle((0, 0, 0, 0), tags=("border_rect_right_1", "border_parts_right", "border_parts")) + self._canvas.create_polygon((0, 0, 0, 0), tags=("border_line_left_1", "border_parts_left", "border_parts", "left_parts")) + self._canvas.create_polygon((0, 0, 0, 0), tags=("border_line_right_1", "border_parts_right", "border_parts", "right_parts")) + self._canvas.create_rectangle((0, 0, 0, 0), tags=("border_rect_left_1", "border_parts_left", "border_parts", "left_parts")) + self._canvas.create_rectangle((0, 0, 0, 0), tags=("border_rect_right_1", "border_parts_right", "border_parts", "right_parts")) requires_recoloring = True self._canvas.coords("border_line_left_1", @@ -436,29 +436,29 @@ class DrawEngine: # create inner button parts if not self._canvas.find_withtag("inner_parts"): - self._canvas.create_polygon((0, 0, 0, 0), tags=("inner_line_left_1", "inner_parts_left", "inner_parts"), joinstyle=tkinter.ROUND) - self._canvas.create_polygon((0, 0, 0, 0), tags=("inner_line_right_1", "inner_parts_right", "inner_parts"), joinstyle=tkinter.ROUND) - self._canvas.create_rectangle((0, 0, 0, 0), tags=("inner_rect_left_1", "inner_parts_left", "inner_parts")) - self._canvas.create_rectangle((0, 0, 0, 0), tags=("inner_rect_right_1", "inner_parts_right", "inner_parts")) + self._canvas.create_polygon((0, 0, 0, 0), tags=("inner_line_left_1", "inner_parts_left", "inner_parts", "left_parts"), joinstyle=tkinter.ROUND) + self._canvas.create_polygon((0, 0, 0, 0), tags=("inner_line_right_1", "inner_parts_right", "inner_parts", "right_parts"), joinstyle=tkinter.ROUND) + self._canvas.create_rectangle((0, 0, 0, 0), tags=("inner_rect_left_1", "inner_parts_left", "inner_parts", "left_parts"), width=0) + self._canvas.create_rectangle((0, 0, 0, 0), tags=("inner_rect_right_1", "inner_parts_right", "inner_parts", "right_parts"), width=0) requires_recoloring = True self._canvas.coords("inner_line_left_1", corner_radius, corner_radius, - left_section_width - corner_radius, + left_section_width - inner_corner_radius, corner_radius, - left_section_width - corner_radius, + left_section_width - inner_corner_radius, height - corner_radius, corner_radius, height - corner_radius) self._canvas.coords("inner_line_right_1", - left_section_width + corner_radius, + left_section_width + inner_corner_radius, corner_radius, width - corner_radius, corner_radius, width - corner_radius, height - corner_radius, - left_section_width + corner_radius, + left_section_width + inner_corner_radius, height - corner_radius) self._canvas.coords("inner_rect_left_1", (left_section_width - inner_corner_radius, @@ -632,15 +632,15 @@ class DrawEngine: return requires_recoloring - def draw_rounded_progress_bar_with_border(self, width: int, height: int, corner_radius: Union[float, int], border_width: Union[float, int], - progress_value: float, orientation: str) -> bool: + def draw_rounded_progress_bar_with_border(self, width: Union[float, int], height: Union[float, int], corner_radius: Union[float, int], + border_width: Union[float, int], progress_value: float, orientation: str) -> bool: """ Draws a rounded bar on the canvas, which is split in half according to the argument 'progress_value' (0 - 1). The border elements get the 'border_parts' tag", the main elements get the 'inner_parts' tag and the progress elements get the 'progress_parts' tag. The 'orientation' argument defines from which direction the progress starts (n, w, s, e). returns bool if recoloring is necessary """ - width = math.floor(width / 2) * 2 # round current_width and current_height and restrict them to even values only + width = math.floor(width / 2) * 2 # round _current_width and _current_height and restrict them to even values only height = math.floor(height / 2) * 2 if corner_radius > width / 2 or corner_radius > height / 2: # restrict corner_radius if it's too larger @@ -800,11 +800,11 @@ class DrawEngine: return requires_recoloring or requires_recoloring_2 - def draw_rounded_slider_with_border_and_button(self, width: int, height: int, corner_radius: Union[float, int], border_width: Union[float, int], - button_length: Union[float, int], button_corner_radius: Union[float, int], slider_value: float, - orientation: str) -> bool: + def draw_rounded_slider_with_border_and_button(self, width: Union[float, int], height: Union[float, int], corner_radius: Union[float, int], + border_width: Union[float, int], button_length: Union[float, int], button_corner_radius: Union[float, int], + slider_value: float, orientation: str) -> bool: - width = math.floor(width / 2) * 2 # round current_width and current_height and restrict them to even values only + width = math.floor(width / 2) * 2 # round _current_width and _current_height and restrict them to even values only height = math.floor(height / 2) * 2 if corner_radius > width / 2 or corner_radius > height / 2: # restrict corner_radius if it's too larger @@ -958,7 +958,7 @@ class DrawEngine: return requires_recoloring - def draw_checkmark(self, width: int, height: int, size: Union[int, float]) -> bool: + def draw_checkmark(self, width: Union[float, int], height: Union[float, int], size: Union[int, float]) -> bool: """ Draws a rounded rectangle with a corner_radius and border_width on the canvas. The border elements have a 'border_parts' tag, the main foreground elements have an 'inner_parts' tag to color the elements accordingly. diff --git a/customtkinter/widgets/ctk_button.py b/customtkinter/widgets/ctk_button.py index 7aba4c7..ccfdb50 100644 --- a/customtkinter/widgets/ctk_button.py +++ b/customtkinter/widgets/ctk_button.py @@ -20,7 +20,7 @@ class CTkButton(CTkBaseClass): border_width="default_theme", command=None, textvariable=None, - width=120, + width=140, height=28, corner_radius="default_theme", text_font="default_theme", @@ -33,7 +33,7 @@ class CTkButton(CTkBaseClass): state=tkinter.NORMAL, **kwargs): - # transfer basic functionality (bg_color, size, appearance_mode, scaling) to CTkBaseClass + # transfer basic functionality (bg_color, size, _appearance_mode, scaling) to CTkBaseClass super().__init__(*args, bg_color=bg_color, width=width, height=height, **kwargs) self.configure_basic_grid() @@ -66,8 +66,8 @@ class CTkButton(CTkBaseClass): self.canvas = CTkCanvas(master=self, highlightthickness=0, - width=self.apply_widget_scaling(self.desired_width), - height=self.apply_widget_scaling(self.desired_height)) + width=self.apply_widget_scaling(self._desired_width), + height=self.apply_widget_scaling(self._desired_height)) self.canvas.grid(row=0, column=0, rowspan=2, columnspan=2, sticky="nsew") self.draw_engine = DrawEngine(self.canvas) @@ -98,41 +98,41 @@ class CTkButton(CTkBaseClass): self.image_label.destroy() self.image_label = None - self.canvas.configure(width=self.apply_widget_scaling(self.desired_width), - height=self.apply_widget_scaling(self.desired_height)) + self.canvas.configure(width=self.apply_widget_scaling(self._desired_width), + height=self.apply_widget_scaling(self._desired_height)) self.draw() def set_dimensions(self, width=None, height=None): super().set_dimensions(width, height) - self.canvas.configure(width=self.apply_widget_scaling(self.desired_width), - height=self.apply_widget_scaling(self.desired_height)) + self.canvas.configure(width=self.apply_widget_scaling(self._desired_width), + height=self.apply_widget_scaling(self._desired_height)) self.draw() def draw(self, no_color_updates=False): - requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.apply_widget_scaling(self.current_width), - self.apply_widget_scaling(self.current_height), + requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.apply_widget_scaling(self._current_width), + self.apply_widget_scaling(self._current_height), self.apply_widget_scaling(self.corner_radius), self.apply_widget_scaling(self.border_width)) if no_color_updates is False or requires_recoloring: - self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self.appearance_mode)) + self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode)) # set color for the button border parts (outline) self.canvas.itemconfig("border_parts", - outline=ThemeManager.single_color(self.border_color, self.appearance_mode), - fill=ThemeManager.single_color(self.border_color, self.appearance_mode)) + outline=ThemeManager.single_color(self.border_color, self._appearance_mode), + fill=ThemeManager.single_color(self.border_color, self._appearance_mode)) # set color for inner button parts if self.fg_color is None: self.canvas.itemconfig("inner_parts", - outline=ThemeManager.single_color(self.bg_color, self.appearance_mode), - fill=ThemeManager.single_color(self.bg_color, self.appearance_mode)) + outline=ThemeManager.single_color(self.bg_color, self._appearance_mode), + fill=ThemeManager.single_color(self.bg_color, self._appearance_mode)) else: self.canvas.itemconfig("inner_parts", - outline=ThemeManager.single_color(self.fg_color, self.appearance_mode), - fill=ThemeManager.single_color(self.fg_color, self.appearance_mode)) + outline=ThemeManager.single_color(self.fg_color, self._appearance_mode), + fill=ThemeManager.single_color(self.fg_color, self._appearance_mode)) # create text label if text given if self.text is not None and self.text != "": @@ -149,17 +149,17 @@ class CTkButton(CTkBaseClass): if no_color_updates is False: # set text_label fg color (text color) - self.text_label.configure(fg=ThemeManager.single_color(self.text_color, self.appearance_mode)) + self.text_label.configure(fg=ThemeManager.single_color(self.text_color, self._appearance_mode)) if self.state == tkinter.DISABLED: - self.text_label.configure(fg=(ThemeManager.single_color(self.text_color_disabled, self.appearance_mode))) + self.text_label.configure(fg=(ThemeManager.single_color(self.text_color_disabled, self._appearance_mode))) else: - self.text_label.configure(fg=ThemeManager.single_color(self.text_color, self.appearance_mode)) + self.text_label.configure(fg=ThemeManager.single_color(self.text_color, self._appearance_mode)) if self.fg_color is None: - self.text_label.configure(bg=ThemeManager.single_color(self.bg_color, self.appearance_mode)) + self.text_label.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode)) else: - self.text_label.configure(bg=ThemeManager.single_color(self.fg_color, self.appearance_mode)) + self.text_label.configure(bg=ThemeManager.single_color(self.fg_color, self._appearance_mode)) self.text_label.configure(text=self.text) # set text @@ -183,9 +183,9 @@ class CTkButton(CTkBaseClass): if no_color_updates is False: # set image_label bg color (background color of label) if self.fg_color is None: - self.image_label.configure(bg=ThemeManager.single_color(self.bg_color, self.appearance_mode)) + self.image_label.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode)) else: - self.image_label.configure(bg=ThemeManager.single_color(self.fg_color, self.appearance_mode)) + self.image_label.configure(bg=ThemeManager.single_color(self.fg_color, self._appearance_mode)) self.image_label.configure(image=self.image) # set image @@ -341,16 +341,16 @@ class CTkButton(CTkBaseClass): # set color of inner button parts to hover color self.canvas.itemconfig("inner_parts", - outline=ThemeManager.single_color(inner_parts_color, self.appearance_mode), - fill=ThemeManager.single_color(inner_parts_color, self.appearance_mode)) + outline=ThemeManager.single_color(inner_parts_color, self._appearance_mode), + fill=ThemeManager.single_color(inner_parts_color, self._appearance_mode)) # set text_label bg color to button hover color if self.text_label is not None: - self.text_label.configure(bg=ThemeManager.single_color(inner_parts_color, self.appearance_mode)) + self.text_label.configure(bg=ThemeManager.single_color(inner_parts_color, self._appearance_mode)) # set image_label bg color to button hover color if self.image_label is not None: - self.image_label.configure(bg=ThemeManager.single_color(inner_parts_color, self.appearance_mode)) + self.image_label.configure(bg=ThemeManager.single_color(inner_parts_color, self._appearance_mode)) def on_leave(self, event=0): self.click_animation_running = False @@ -363,16 +363,16 @@ class CTkButton(CTkBaseClass): # set color of inner button parts self.canvas.itemconfig("inner_parts", - outline=ThemeManager.single_color(inner_parts_color, self.appearance_mode), - fill=ThemeManager.single_color(inner_parts_color, self.appearance_mode)) + outline=ThemeManager.single_color(inner_parts_color, self._appearance_mode), + fill=ThemeManager.single_color(inner_parts_color, self._appearance_mode)) # set text_label bg color (label color) if self.text_label is not None: - self.text_label.configure(bg=ThemeManager.single_color(inner_parts_color, self.appearance_mode)) + self.text_label.configure(bg=ThemeManager.single_color(inner_parts_color, self._appearance_mode)) # set image_label bg color (image bg color) if self.image_label is not None: - self.image_label.configure(bg=ThemeManager.single_color(inner_parts_color, self.appearance_mode)) + self.image_label.configure(bg=ThemeManager.single_color(inner_parts_color, self._appearance_mode)) def click_animation(self): if self.click_animation_running: diff --git a/customtkinter/widgets/ctk_checkbox.py b/customtkinter/widgets/ctk_checkbox.py index 2e18b00..5471a3a 100644 --- a/customtkinter/widgets/ctk_checkbox.py +++ b/customtkinter/widgets/ctk_checkbox.py @@ -35,7 +35,7 @@ class CTkCheckBox(CTkBaseClass): textvariable=None, **kwargs): - # transfer basic functionality (bg_color, size, appearance_mode, scaling) to CTkBaseClass + # transfer basic functionality (bg_color, size, _appearance_mode, scaling) to CTkBaseClass super().__init__(*args, bg_color=bg_color, width=width, height=height, **kwargs) # color @@ -75,14 +75,14 @@ class CTkCheckBox(CTkBaseClass): self.bg_canvas = CTkCanvas(master=self, highlightthickness=0, - width=self.apply_widget_scaling(self.desired_width), - height=self.apply_widget_scaling(self.desired_height)) + width=self.apply_widget_scaling(self._desired_width), + height=self.apply_widget_scaling(self._desired_height)) self.bg_canvas.grid(row=0, column=0, padx=0, pady=0, columnspan=3, rowspan=1, sticky="nswe") self.canvas = CTkCanvas(master=self, highlightthickness=0, - width=self.apply_widget_scaling(self.desired_width), - height=self.apply_widget_scaling(self.desired_height)) + width=self.apply_widget_scaling(self._desired_width), + height=self.apply_widget_scaling(self._desired_height)) self.canvas.grid(row=0, column=0, padx=0, pady=0, columnspan=1, rowspan=1) self.draw_engine = DrawEngine(self.canvas) @@ -107,8 +107,8 @@ class CTkCheckBox(CTkBaseClass): self.grid_columnconfigure(1, weight=0, minsize=self.apply_widget_scaling(6)) self.text_label.configure(font=self.apply_font_scaling(self.text_font)) - self.bg_canvas.configure(width=self.apply_widget_scaling(self.desired_width), height=self.apply_widget_scaling(self.desired_height)) - self.canvas.configure(width=self.apply_widget_scaling(self.desired_width), height=self.apply_widget_scaling(self.desired_height)) + self.bg_canvas.configure(width=self.apply_widget_scaling(self._desired_width), height=self.apply_widget_scaling(self._desired_height)) + self.canvas.configure(width=self.apply_widget_scaling(self._desired_width), height=self.apply_widget_scaling(self._desired_height)) self.draw() def destroy(self): @@ -118,40 +118,40 @@ class CTkCheckBox(CTkBaseClass): super().destroy() def draw(self, no_color_updates=False): - requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.apply_widget_scaling(self.current_width), - self.apply_widget_scaling(self.current_height), + requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.apply_widget_scaling(self._current_width), + self.apply_widget_scaling(self._current_height), self.apply_widget_scaling(self.corner_radius), self.apply_widget_scaling(self.border_width)) if self.check_state is True: - self.draw_engine.draw_checkmark(self.apply_widget_scaling(self.current_width), - self.apply_widget_scaling(self.current_height), - self.apply_widget_scaling(self.current_height * 0.58)) + self.draw_engine.draw_checkmark(self.apply_widget_scaling(self._current_width), + self.apply_widget_scaling(self._current_height), + self.apply_widget_scaling(self._current_height * 0.58)) else: self.canvas.delete("checkmark") - self.bg_canvas.configure(bg=ThemeManager.single_color(self.bg_color, self.appearance_mode)) - self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self.appearance_mode)) + self.bg_canvas.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode)) + self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode)) if self.check_state is True: self.canvas.itemconfig("inner_parts", - outline=ThemeManager.single_color(self.fg_color, self.appearance_mode), - fill=ThemeManager.single_color(self.fg_color, self.appearance_mode)) + outline=ThemeManager.single_color(self.fg_color, self._appearance_mode), + fill=ThemeManager.single_color(self.fg_color, self._appearance_mode)) self.canvas.itemconfig("border_parts", - outline=ThemeManager.single_color(self.fg_color, self.appearance_mode), - fill=ThemeManager.single_color(self.fg_color, self.appearance_mode)) + outline=ThemeManager.single_color(self.fg_color, self._appearance_mode), + fill=ThemeManager.single_color(self.fg_color, self._appearance_mode)) if "create_line" in self.canvas.gettags("checkmark"): - self.canvas.itemconfig("checkmark", fill=ThemeManager.single_color(self.checkmark_color, self.appearance_mode)) + self.canvas.itemconfig("checkmark", fill=ThemeManager.single_color(self.checkmark_color, self._appearance_mode)) else: - self.canvas.itemconfig("checkmark", fill=ThemeManager.single_color(self.checkmark_color, self.appearance_mode)) + self.canvas.itemconfig("checkmark", fill=ThemeManager.single_color(self.checkmark_color, self._appearance_mode)) else: self.canvas.itemconfig("inner_parts", - outline=ThemeManager.single_color(self.bg_color, self.appearance_mode), - fill=ThemeManager.single_color(self.bg_color, self.appearance_mode)) + outline=ThemeManager.single_color(self.bg_color, self._appearance_mode), + fill=ThemeManager.single_color(self.bg_color, self._appearance_mode)) self.canvas.itemconfig("border_parts", - outline=ThemeManager.single_color(self.border_color, self.appearance_mode), - fill=ThemeManager.single_color(self.border_color, self.appearance_mode)) + outline=ThemeManager.single_color(self.border_color, self._appearance_mode), + fill=ThemeManager.single_color(self.border_color, self._appearance_mode)) if self.text_label is None: self.text_label = tkinter.Label(master=self, @@ -167,10 +167,10 @@ class CTkCheckBox(CTkBaseClass): self.text_label.bind("", self.toggle) if self.state == tkinter.DISABLED: - self.text_label.configure(fg=(ThemeManager.single_color(self.text_color_disabled, self.appearance_mode))) + self.text_label.configure(fg=(ThemeManager.single_color(self.text_color_disabled, self._appearance_mode))) else: - self.text_label.configure(fg=ThemeManager.single_color(self.text_color, self.appearance_mode)) - self.text_label.configure(bg=ThemeManager.single_color(self.bg_color, self.appearance_mode)) + self.text_label.configure(fg=ThemeManager.single_color(self.text_color, self._appearance_mode)) + self.text_label.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode)) self.set_text(self.text) @@ -274,32 +274,32 @@ class CTkCheckBox(CTkBaseClass): if self.hover is True and self.state == tkinter.NORMAL: if self.check_state is True: self.canvas.itemconfig("inner_parts", - fill=ThemeManager.single_color(self.hover_color, self.appearance_mode), - outline=ThemeManager.single_color(self.hover_color, self.appearance_mode)) + fill=ThemeManager.single_color(self.hover_color, self._appearance_mode), + outline=ThemeManager.single_color(self.hover_color, self._appearance_mode)) self.canvas.itemconfig("border_parts", - fill=ThemeManager.single_color(self.hover_color, self.appearance_mode), - outline=ThemeManager.single_color(self.hover_color, self.appearance_mode)) + fill=ThemeManager.single_color(self.hover_color, self._appearance_mode), + outline=ThemeManager.single_color(self.hover_color, self._appearance_mode)) else: self.canvas.itemconfig("inner_parts", - fill=ThemeManager.single_color(self.hover_color, self.appearance_mode), - outline=ThemeManager.single_color(self.hover_color, self.appearance_mode)) + fill=ThemeManager.single_color(self.hover_color, self._appearance_mode), + outline=ThemeManager.single_color(self.hover_color, self._appearance_mode)) def on_leave(self, event=0): if self.hover is True: if self.check_state is True: self.canvas.itemconfig("inner_parts", - fill=ThemeManager.single_color(self.fg_color, self.appearance_mode), - outline=ThemeManager.single_color(self.fg_color, self.appearance_mode)) + fill=ThemeManager.single_color(self.fg_color, self._appearance_mode), + outline=ThemeManager.single_color(self.fg_color, self._appearance_mode)) self.canvas.itemconfig("border_parts", - fill=ThemeManager.single_color(self.fg_color, self.appearance_mode), - outline=ThemeManager.single_color(self.fg_color, self.appearance_mode)) + fill=ThemeManager.single_color(self.fg_color, self._appearance_mode), + outline=ThemeManager.single_color(self.fg_color, self._appearance_mode)) else: self.canvas.itemconfig("inner_parts", - fill=ThemeManager.single_color(self.bg_color, self.appearance_mode), - outline=ThemeManager.single_color(self.bg_color, self.appearance_mode)) + fill=ThemeManager.single_color(self.bg_color, self._appearance_mode), + outline=ThemeManager.single_color(self.bg_color, self._appearance_mode)) self.canvas.itemconfig("border_parts", - fill=ThemeManager.single_color(self.border_color, self.appearance_mode), - outline=ThemeManager.single_color(self.border_color, self.appearance_mode)) + fill=ThemeManager.single_color(self.border_color, self._appearance_mode), + outline=ThemeManager.single_color(self.border_color, self._appearance_mode)) def variable_callback(self, var_name, index, mode): if not self.variable_callback_blocked: @@ -335,10 +335,7 @@ class CTkCheckBox(CTkBaseClass): self.variable_callback_blocked = False if self.function is not None: - try: - self.function() - except: - pass + self.function() def deselect(self, from_variable_callback=False): self.check_state = False diff --git a/customtkinter/widgets/ctk_combobox.py b/customtkinter/widgets/ctk_combobox.py new file mode 100644 index 0000000..101cbb2 --- /dev/null +++ b/customtkinter/widgets/ctk_combobox.py @@ -0,0 +1,321 @@ +import tkinter +import sys +from typing import Union + +from .dropdown_menu import DropdownMenu + +from .ctk_canvas import CTkCanvas +from ..theme_manager import ThemeManager +from ..settings import Settings +from ..draw_engine import DrawEngine +from .widget_base_class import CTkBaseClass + + +class CTkComboBox(CTkBaseClass): + + def __init__(self, *args, + bg_color=None, + fg_color="default_theme", + border_color="default_theme", + button_color="default_theme", + button_hover_color="default_theme", + dropdown_color="default_theme", + dropdown_hover_color="default_theme", + dropdown_text_color="default_theme", + variable=None, + values=None, + command=None, + width=140, + height=28, + corner_radius="default_theme", + border_width="default_theme", + text_font="default_theme", + text_color="default_theme", + text_color_disabled="default_theme", + hover=True, + state=tkinter.NORMAL, + **kwargs): + + # transfer basic functionality (bg_color, size, _appearance_mode, scaling) to CTkBaseClass + super().__init__(*args, bg_color=bg_color, width=width, height=height, **kwargs) + + # color variables + self.fg_color = ThemeManager.theme["color"]["entry"] if fg_color == "default_theme" else fg_color + self.border_color = ThemeManager.theme["color"]["combobox_border"] if border_color == "default_theme" else border_color + self.button_color = ThemeManager.theme["color"]["combobox_border"] if button_color == "default_theme" else button_color + self.button_hover_color = ThemeManager.theme["color"]["combobox_button_hover"] if button_hover_color == "default_theme" else button_hover_color + self.dropdown_color = ThemeManager.theme["color"]["dropdown_color"] if dropdown_color == "default_theme" else dropdown_color + self.dropdown_hover_color = ThemeManager.theme["color"]["dropdown_hover"] if dropdown_hover_color == "default_theme" else dropdown_hover_color + self.dropdown_text_color = ThemeManager.theme["color"]["dropdown_text"] if dropdown_text_color == "default_theme" else dropdown_text_color + + # shape + self.corner_radius = ThemeManager.theme["shape"]["button_corner_radius"] if corner_radius == "default_theme" else corner_radius + self.border_width = ThemeManager.theme["shape"]["entry_border_width"] if border_width == "default_theme" else border_width + + # text and font + self.text_label = None + self.text_color = ThemeManager.theme["color"]["text"] if text_color == "default_theme" else text_color + self.text_color_disabled = ThemeManager.theme["color"]["text_button_disabled"] if text_color_disabled == "default_theme" else text_color_disabled + self.text_font = (ThemeManager.theme["text"]["font"], ThemeManager.theme["text"]["size"]) if text_font == "default_theme" else text_font + + # callback and hover functionality + self.function = command + self.variable = variable + self.variable_callback_blocked = False + self.variable_callback_name = None + self.state = state + self.hover = hover + self.click_animation_running = False + + if values is None: + self.values = ["CTkComboBox"] + else: + self.values = values + + if len(self.values) > 0: + self.current_value = self.values[0] + else: + self.current_value = "CTkComboBox" + + self.dropdown_menu: Union[DropdownMenu, None] = None + + # configure grid system (1x1) + self.grid_rowconfigure(0, weight=1) + self.grid_columnconfigure(0, weight=1) + + self.canvas = CTkCanvas(master=self, + highlightthickness=0, + width=self.apply_widget_scaling(self._desired_width), + height=self.apply_widget_scaling(self._desired_height)) + self.canvas.grid(row=0, column=0, rowspan=1, columnspan=1, sticky="nsew") + self.draw_engine = DrawEngine(self.canvas) + + self.entry = tkinter.Entry(master=self, + width=0, + bd=0, + highlightthickness=0, + font=self.apply_font_scaling(self.text_font)) + left_section_width = self._current_width - self._current_height + self.entry.grid(row=0, column=0, rowspan=1, columnspan=1, sticky="w", + padx=(self.apply_widget_scaling(max(self.corner_radius, 3)), + self.apply_widget_scaling(max(self._current_width - left_section_width + 3, 3)))) + + self.draw() # initial draw + + # event bindings + self.canvas.tag_bind("right_parts", "", self.on_enter) + self.canvas.tag_bind("dropdown_arrow", "", self.on_enter) + self.canvas.tag_bind("right_parts", "", self.on_leave) + self.canvas.tag_bind("dropdown_arrow", "", self.on_leave) + self.canvas.tag_bind("right_parts", "", self.clicked) + self.canvas.tag_bind("dropdown_arrow", "", self.clicked) + self.bind('', self.update_dimensions_event) + + if self.variable is not None: + self.variable_callback_name = self.variable.trace_add("write", self.variable_callback) + self.set(self.variable.get(), from_variable_callback=True) + + def set_scaling(self, *args, **kwargs): + super().set_scaling(*args, **kwargs) + + if self.text_label is not None: + self.text_label.destroy() + self.text_label = None + + self.canvas.configure(width=self.apply_widget_scaling(self._desired_width), + height=self.apply_widget_scaling(self._desired_height)) + self.draw() + + def set_dimensions(self, width: int = None, height: int = None): + super().set_dimensions(width, height) + + self.canvas.configure(width=self.apply_widget_scaling(self._desired_width), + height=self.apply_widget_scaling(self._desired_height)) + self.draw() + + def draw(self, no_color_updates=False): + left_section_width = self._current_width - self._current_height + requires_recoloring = self.draw_engine.draw_rounded_rect_with_border_vertical_split(self.apply_widget_scaling(self._current_width), + self.apply_widget_scaling(self._current_height), + self.apply_widget_scaling(self.corner_radius), + self.apply_widget_scaling(self.border_width), + self.apply_widget_scaling(left_section_width)) + + requires_recoloring_2 = self.draw_engine.draw_dropdown_arrow(self.apply_widget_scaling(self._current_width - (self._current_height / 2)), + self.apply_widget_scaling(self._current_height / 2), + self.apply_widget_scaling(self._current_height / 3)) + + if self.current_value is not None: + self.entry.delete(0, tkinter.END) + self.entry.insert(0, self.current_value) + + if no_color_updates is False or requires_recoloring or requires_recoloring_2: + + self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode)) + + self.canvas.itemconfig("inner_parts_left", + outline=ThemeManager.single_color(self.fg_color, self._appearance_mode), + fill=ThemeManager.single_color(self.fg_color, self._appearance_mode)) + self.canvas.itemconfig("border_parts_left", + outline=ThemeManager.single_color(self.border_color, self._appearance_mode), + fill=ThemeManager.single_color(self.border_color, self._appearance_mode)) + self.canvas.itemconfig("inner_parts_right", + outline=ThemeManager.single_color(self.border_color, self._appearance_mode), + fill=ThemeManager.single_color(self.border_color, self._appearance_mode)) + self.canvas.itemconfig("border_parts_right", + outline=ThemeManager.single_color(self.border_color, self._appearance_mode), + fill=ThemeManager.single_color(self.border_color, self._appearance_mode)) + + self.entry.configure(fg=ThemeManager.single_color(self.text_color, self._appearance_mode)) + self.entry.configure(bg=ThemeManager.single_color(self.fg_color, self._appearance_mode)) + + if self.state == tkinter.DISABLED: + self.entry.configure(fg=(ThemeManager.single_color(self.text_color_disabled, self._appearance_mode))) + else: + self.entry.configure(fg=ThemeManager.single_color(self.text_color, self._appearance_mode)) + + def open_dropdown_menu(self): + self.dropdown_menu = DropdownMenu(x_position=self.winfo_rootx(), + y_position=self.winfo_rooty() + self.apply_widget_scaling(self._current_height + 4), + width=self._current_width, + values=self.values, + command=self.set, + fg_color=self.dropdown_color, + button_hover_color=self.dropdown_hover_color, + button_color=self.dropdown_color, + text_color=self.dropdown_text_color) + + def configure(self, *args, **kwargs): + require_redraw = False # some attribute changes require a call of self.draw() at the end + + if "state" in kwargs: + self.state = kwargs["state"] + self.set_cursor() + require_redraw = True + del kwargs["state"] + + if "fg_color" in kwargs: + self.fg_color = kwargs["fg_color"] + require_redraw = True + del kwargs["fg_color"] + + if "bg_color" in kwargs: + if kwargs["bg_color"] is None: + self.bg_color = self.detect_color_of_master() + else: + self.bg_color = kwargs["bg_color"] + require_redraw = True + del kwargs["bg_color"] + + if "button_color" in kwargs: + self.button_color = kwargs["button_color"] + require_redraw = True + del kwargs["button_color"] + + if "button_hover_color" in kwargs: + self.button_hover_color = kwargs["button_hover_color"] + require_redraw = True + del kwargs["button_hover_color"] + + if "text_color" in kwargs: + self.text_color = kwargs["text_color"] + require_redraw = True + del kwargs["text_color"] + + if "command" in kwargs: + self.function = kwargs["command"] + del kwargs["command"] + + if "variable" in kwargs: + if self.variable is not None: # remove old callback + self.variable.trace_remove("write", self.variable_callback_name) + + self.variable = kwargs["variable"] + + if self.variable is not None and self.variable != "": + self.variable_callback_name = self.variable.trace_add("write", self.variable_callback) + self.set(self.variable.get(), from_variable_callback=True) + else: + self.variable = None + + del kwargs["variable"] + + if "width" in kwargs: + self.set_dimensions(width=kwargs["width"]) + del kwargs["width"] + + if "height" in kwargs: + self.set_dimensions(height=kwargs["height"]) + del kwargs["height"] + + super().configure(*args, **kwargs) + + if require_redraw: + self.draw() + + def on_enter(self, event=0): + if self.hover is True and self.state == tkinter.NORMAL: + if sys.platform == "darwin" and len(self.values) > 0 and Settings.cursor_manipulation_enabled: + self.canvas.configure(cursor="pointinghand") + elif sys.platform.startswith("win") and len(self.values) > 0 and Settings.cursor_manipulation_enabled: + self.canvas.configure(cursor="hand2") + + # set color of inner button parts to hover color + self.canvas.itemconfig("inner_parts_right", + outline=ThemeManager.single_color(self.button_hover_color, self._appearance_mode), + fill=ThemeManager.single_color(self.button_hover_color, self._appearance_mode)) + self.canvas.itemconfig("border_parts_right", + outline=ThemeManager.single_color(self.button_hover_color, self._appearance_mode), + fill=ThemeManager.single_color(self.button_hover_color, self._appearance_mode)) + + def on_leave(self, event=0): + self.click_animation_running = False + + if self.hover is True: + if sys.platform == "darwin" and len(self.values) > 0 and Settings.cursor_manipulation_enabled: + self.canvas.configure(cursor="arrow") + elif sys.platform.startswith("win") and len(self.values) > 0 and Settings.cursor_manipulation_enabled: + self.canvas.configure(cursor="arrow") + + # set color of inner button parts + self.canvas.itemconfig("inner_parts_right", + outline=ThemeManager.single_color(self.button_color, self._appearance_mode), + fill=ThemeManager.single_color(self.button_color, self._appearance_mode)) + self.canvas.itemconfig("border_parts_right", + outline=ThemeManager.single_color(self.button_color, self._appearance_mode), + fill=ThemeManager.single_color(self.button_color, self._appearance_mode)) + + def click_animation(self): + if self.click_animation_running: + self.on_enter() + + def variable_callback(self, var_name, index, mode): + if not self.variable_callback_blocked: + self.set(self.variable.get(), from_variable_callback=True) + + def set(self, value: str, from_variable_callback: bool = False): + self.current_value = value + + self.entry.delete(0, tkinter.END) + self.entry.insert(0, self.current_value) + + if self.variable is not None and not from_variable_callback: + self.variable_callback_blocked = True + self.variable.set(self.current_value) + self.variable_callback_blocked = False + + if not from_variable_callback: + if self.function is not None: + self.function(self.current_value) + + def get(self) -> str: + return self.current_value + + def clicked(self, event=0): + if self.state is not tkinter.DISABLED: + self.open_dropdown_menu() + + # click animation: change color with .on_leave() and back to normal after 100ms with click_animation() + self.on_leave() + self.click_animation_running = True + self.after(100, self.click_animation) diff --git a/customtkinter/widgets/ctk_entry.py b/customtkinter/widgets/ctk_entry.py index 7ab787c..3c6c9a6 100644 --- a/customtkinter/widgets/ctk_entry.py +++ b/customtkinter/widgets/ctk_entry.py @@ -17,12 +17,12 @@ class CTkEntry(CTkBaseClass): corner_radius="default_theme", border_width="default_theme", border_color="default_theme", - width=120, + width=140, height=28, state=tkinter.NORMAL, **kwargs): - # transfer basic functionality (bg_color, size, appearance_mode, scaling) to CTkBaseClass + # transfer basic functionality (bg_color, size, _appearance_mode, scaling) to CTkBaseClass if "master" in kwargs: super().__init__(*args, bg_color=bg_color, width=width, height=height, master=kwargs["master"]) del kwargs["master"] @@ -49,8 +49,8 @@ class CTkEntry(CTkBaseClass): self.canvas = CTkCanvas(master=self, highlightthickness=0, - width=self.apply_widget_scaling(self.current_width), - height=self.apply_widget_scaling(self.current_height)) + width=self.apply_widget_scaling(self._current_width), + height=self.apply_widget_scaling(self._current_height)) self.canvas.grid(column=0, row=0, sticky="we") self.draw_engine = DrawEngine(self.canvas) @@ -78,14 +78,14 @@ class CTkEntry(CTkBaseClass): self.entry.grid(column=0, row=0, sticky="we", padx=self.apply_widget_scaling(self.corner_radius) if self.corner_radius >= 6 else self.apply_widget_scaling(6)) - self.canvas.configure(width=self.apply_widget_scaling(self.desired_width), height=self.apply_widget_scaling(self.desired_height)) + self.canvas.configure(width=self.apply_widget_scaling(self._desired_width), height=self.apply_widget_scaling(self._desired_height)) self.draw() def set_dimensions(self, width=None, height=None): super().set_dimensions(width, height) - self.canvas.configure(width=self.apply_widget_scaling(self.desired_width), - height=self.apply_widget_scaling(self.desired_height)) + self.canvas.configure(width=self.apply_widget_scaling(self._desired_width), + height=self.apply_widget_scaling(self._desired_height)) self.draw() def set_placeholder(self, event=None): @@ -93,54 +93,54 @@ class CTkEntry(CTkBaseClass): if not self.placeholder_text_active and self.entry.get() == "": self.placeholder_text_active = True self.pre_placeholder_arguments = {"show": self.entry.cget("show")} - self.entry.config(fg=ThemeManager.single_color(self.placeholder_text_color, self.appearance_mode), show="") + self.entry.config(fg=ThemeManager.single_color(self.placeholder_text_color, self._appearance_mode), show="") self.entry.delete(0, tkinter.END) self.entry.insert(0, self.placeholder_text) def clear_placeholder(self, event=None): if self.placeholder_text_active: self.placeholder_text_active = False - self.entry.config(fg=ThemeManager.single_color(self.text_color, self.appearance_mode)) + self.entry.config(fg=ThemeManager.single_color(self.text_color, self._appearance_mode)) self.entry.delete(0, tkinter.END) for argument, value in self.pre_placeholder_arguments.items(): self.entry[argument] = value def draw(self, no_color_updates=False): - self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self.appearance_mode)) + self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode)) - requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.apply_widget_scaling(self.current_width), - self.apply_widget_scaling(self.current_height), + requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.apply_widget_scaling(self._current_width), + self.apply_widget_scaling(self._current_height), self.apply_widget_scaling(self.corner_radius), self.apply_widget_scaling(self.border_width)) if requires_recoloring or no_color_updates is False: - if ThemeManager.single_color(self.fg_color, self.appearance_mode) is not None: + if ThemeManager.single_color(self.fg_color, self._appearance_mode) is not None: self.canvas.itemconfig("inner_parts", - fill=ThemeManager.single_color(self.fg_color, self.appearance_mode), - outline=ThemeManager.single_color(self.fg_color, self.appearance_mode)) - self.entry.configure(bg=ThemeManager.single_color(self.fg_color, self.appearance_mode), - disabledbackground=ThemeManager.single_color(self.fg_color, self.appearance_mode), - highlightcolor=ThemeManager.single_color(self.fg_color, self.appearance_mode), - fg=ThemeManager.single_color(self.text_color, self.appearance_mode), - disabledforeground=ThemeManager.single_color(self.text_color, self.appearance_mode), - insertbackground=ThemeManager.single_color(self.text_color, self.appearance_mode)) + fill=ThemeManager.single_color(self.fg_color, self._appearance_mode), + outline=ThemeManager.single_color(self.fg_color, self._appearance_mode)) + self.entry.configure(bg=ThemeManager.single_color(self.fg_color, self._appearance_mode), + disabledbackground=ThemeManager.single_color(self.fg_color, self._appearance_mode), + highlightcolor=ThemeManager.single_color(self.fg_color, self._appearance_mode), + fg=ThemeManager.single_color(self.text_color, self._appearance_mode), + disabledforeground=ThemeManager.single_color(self.text_color, self._appearance_mode), + insertbackground=ThemeManager.single_color(self.text_color, self._appearance_mode)) else: self.canvas.itemconfig("inner_parts", - fill=ThemeManager.single_color(self.bg_color, self.appearance_mode), - outline=ThemeManager.single_color(self.bg_color, self.appearance_mode)) - self.entry.configure(bg=ThemeManager.single_color(self.bg_color, self.appearance_mode), - disabledbackground=ThemeManager.single_color(self.bg_color, self.appearance_mode), - highlightcolor=ThemeManager.single_color(self.bg_color, self.appearance_mode), - fg=ThemeManager.single_color(self.text_color, self.appearance_mode), - disabledforeground=ThemeManager.single_color(self.text_color, self.appearance_mode), - insertbackground=ThemeManager.single_color(self.text_color, self.appearance_mode)) + fill=ThemeManager.single_color(self.bg_color, self._appearance_mode), + outline=ThemeManager.single_color(self.bg_color, self._appearance_mode)) + self.entry.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode), + disabledbackground=ThemeManager.single_color(self.bg_color, self._appearance_mode), + highlightcolor=ThemeManager.single_color(self.bg_color, self._appearance_mode), + fg=ThemeManager.single_color(self.text_color, self._appearance_mode), + disabledforeground=ThemeManager.single_color(self.text_color, self._appearance_mode), + insertbackground=ThemeManager.single_color(self.text_color, self._appearance_mode)) self.canvas.itemconfig("border_parts", - fill=ThemeManager.single_color(self.border_color, self.appearance_mode), - outline=ThemeManager.single_color(self.border_color, self.appearance_mode)) + fill=ThemeManager.single_color(self.border_color, self._appearance_mode), + outline=ThemeManager.single_color(self.border_color, self._appearance_mode)) if self.placeholder_text_active: - self.entry.config(fg=ThemeManager.single_color(self.placeholder_text_color, self.appearance_mode)) + self.entry.config(fg=ThemeManager.single_color(self.placeholder_text_color, self._appearance_mode)) def bind(self, *args, **kwargs): self.entry.bind(*args, **kwargs) @@ -179,10 +179,10 @@ class CTkEntry(CTkBaseClass): if "corner_radius" in kwargs: self.corner_radius = kwargs["corner_radius"] - if self.corner_radius * 2 > self.current_height: - self.corner_radius = self.current_height / 2 - elif self.corner_radius * 2 > self.current_width: - self.corner_radius = self.current_width / 2 + if self.corner_radius * 2 > self._current_height: + self.corner_radius = self._current_height / 2 + elif self.corner_radius * 2 > self._current_width: + self.corner_radius = self._current_width / 2 self.entry.grid(column=0, row=0, sticky="we", padx=self.apply_widget_scaling(self.corner_radius) if self.corner_radius >= 6 else self.apply_widget_scaling(6)) del kwargs["corner_radius"] diff --git a/customtkinter/widgets/ctk_frame.py b/customtkinter/widgets/ctk_frame.py index b1912d9..b377451 100644 --- a/customtkinter/widgets/ctk_frame.py +++ b/customtkinter/widgets/ctk_frame.py @@ -16,7 +16,7 @@ class CTkFrame(CTkBaseClass): overwrite_preferred_drawing_method: str = None, **kwargs): - # transfer basic functionality (bg_color, size, appearance_mode, scaling) to CTkBaseClass + # transfer basic functionality (bg_color, size, _appearance_mode, scaling) to CTkBaseClass super().__init__(*args, bg_color=bg_color, width=width, height=height, **kwargs) # color @@ -40,10 +40,10 @@ class CTkFrame(CTkBaseClass): self.canvas = CTkCanvas(master=self, highlightthickness=0, - width=self.apply_widget_scaling(self.current_width), - height=self.apply_widget_scaling(self.current_height)) + width=self.apply_widget_scaling(self._current_width), + height=self.apply_widget_scaling(self._current_height)) self.canvas.place(x=0, y=0, relwidth=1, relheight=1) - self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self.appearance_mode)) + self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode)) self.draw_engine = DrawEngine(self.canvas) self._overwrite_preferred_drawing_method = overwrite_preferred_drawing_method @@ -65,20 +65,20 @@ class CTkFrame(CTkBaseClass): def set_scaling(self, *args, **kwargs): super().set_scaling(*args, **kwargs) - self.canvas.configure(width=self.apply_widget_scaling(self.desired_width), height=self.apply_widget_scaling(self.desired_height)) + self.canvas.configure(width=self.apply_widget_scaling(self._desired_width), height=self.apply_widget_scaling(self._desired_height)) self.draw() def set_dimensions(self, width=None, height=None): super().set_dimensions(width, height) - self.canvas.configure(width=self.apply_widget_scaling(self.desired_width), - height=self.apply_widget_scaling(self.desired_height)) + self.canvas.configure(width=self.apply_widget_scaling(self._desired_width), + height=self.apply_widget_scaling(self._desired_height)) self.draw() def draw(self, no_color_updates=False): - requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.apply_widget_scaling(self.current_width), - self.apply_widget_scaling(self.current_height), + requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.apply_widget_scaling(self._current_width), + self.apply_widget_scaling(self._current_height), self.apply_widget_scaling(self.corner_radius), self.apply_widget_scaling(self.border_width), overwrite_preferred_drawing_method=self._overwrite_preferred_drawing_method) @@ -86,17 +86,17 @@ class CTkFrame(CTkBaseClass): if no_color_updates is False or requires_recoloring: if self.fg_color is None: self.canvas.itemconfig("inner_parts", - fill=ThemeManager.single_color(self.bg_color, self.appearance_mode), - outline=ThemeManager.single_color(self.bg_color, self.appearance_mode)) + fill=ThemeManager.single_color(self.bg_color, self._appearance_mode), + outline=ThemeManager.single_color(self.bg_color, self._appearance_mode)) else: self.canvas.itemconfig("inner_parts", - fill=ThemeManager.single_color(self.fg_color, self.appearance_mode), - outline=ThemeManager.single_color(self.fg_color, self.appearance_mode)) + fill=ThemeManager.single_color(self.fg_color, self._appearance_mode), + outline=ThemeManager.single_color(self.fg_color, self._appearance_mode)) self.canvas.itemconfig("border_parts", - fill=ThemeManager.single_color(self.border_color, self.appearance_mode), - outline=ThemeManager.single_color(self.border_color, self.appearance_mode)) - self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self.appearance_mode)) + fill=ThemeManager.single_color(self.border_color, self._appearance_mode), + outline=ThemeManager.single_color(self.border_color, self._appearance_mode)) + self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode)) self.canvas.tag_lower("inner_parts") self.canvas.tag_lower("border_parts") diff --git a/customtkinter/widgets/ctk_label.py b/customtkinter/widgets/ctk_label.py index 7896b21..fed2a48 100644 --- a/customtkinter/widgets/ctk_label.py +++ b/customtkinter/widgets/ctk_label.py @@ -12,13 +12,13 @@ class CTkLabel(CTkBaseClass): fg_color="default_theme", text_color="default_theme", corner_radius="default_theme", - width=120, + width=140, height=28, text="CTkLabel", text_font="default_theme", **kwargs): - # transfer basic functionality (bg_color, size, appearance_mode, scaling) to CTkBaseClass + # transfer basic functionality (bg_color, size, _appearance_mode, scaling) to CTkBaseClass if "master" in kwargs: super().__init__(*args, bg_color=bg_color, width=width, height=height, master=kwargs["master"]) del kwargs["master"] @@ -44,8 +44,8 @@ class CTkLabel(CTkBaseClass): self.canvas = CTkCanvas(master=self, highlightthickness=0, - width=self.apply_widget_scaling(self.desired_width), - height=self.apply_widget_scaling(self.desired_height)) + width=self.apply_widget_scaling(self._desired_width), + height=self.apply_widget_scaling(self._desired_height)) self.canvas.grid(row=0, column=0, sticky="nswe") self.draw_engine = DrawEngine(self.canvas) @@ -63,7 +63,7 @@ class CTkLabel(CTkBaseClass): def set_scaling(self, *args, **kwargs): super().set_scaling(*args, **kwargs) - self.canvas.configure(width=self.apply_widget_scaling(self.desired_width), height=self.apply_widget_scaling(self.desired_height)) + self.canvas.configure(width=self.apply_widget_scaling(self._desired_width), height=self.apply_widget_scaling(self._desired_height)) self.text_label.configure(font=self.apply_font_scaling(self.text_font)) self.text_label.grid(row=0, column=0, padx=self.apply_widget_scaling(self.corner_radius)) @@ -72,33 +72,33 @@ class CTkLabel(CTkBaseClass): def set_dimensions(self, width=None, height=None): super().set_dimensions(width, height) - self.canvas.configure(width=self.apply_widget_scaling(self.desired_width), - height=self.apply_widget_scaling(self.desired_height)) + self.canvas.configure(width=self.apply_widget_scaling(self._desired_width), + height=self.apply_widget_scaling(self._desired_height)) self.draw() def draw(self, no_color_updates=False): - requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.apply_widget_scaling(self.current_width), - self.apply_widget_scaling(self.current_height), + requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.apply_widget_scaling(self._current_width), + self.apply_widget_scaling(self._current_height), self.apply_widget_scaling(self.corner_radius), 0) if no_color_updates is False or requires_recoloring: - if ThemeManager.single_color(self.fg_color, self.appearance_mode) is not None: + if ThemeManager.single_color(self.fg_color, self._appearance_mode) is not None: self.canvas.itemconfig("inner_parts", - fill=ThemeManager.single_color(self.fg_color, self.appearance_mode), - outline=ThemeManager.single_color(self.fg_color, self.appearance_mode)) + fill=ThemeManager.single_color(self.fg_color, self._appearance_mode), + outline=ThemeManager.single_color(self.fg_color, self._appearance_mode)) - self.text_label.configure(fg=ThemeManager.single_color(self.text_color, self.appearance_mode), - bg=ThemeManager.single_color(self.fg_color, self.appearance_mode)) + self.text_label.configure(fg=ThemeManager.single_color(self.text_color, self._appearance_mode), + bg=ThemeManager.single_color(self.fg_color, self._appearance_mode)) else: self.canvas.itemconfig("inner_parts", - fill=ThemeManager.single_color(self.bg_color, self.appearance_mode), - outline=ThemeManager.single_color(self.bg_color, self.appearance_mode)) + fill=ThemeManager.single_color(self.bg_color, self._appearance_mode), + outline=ThemeManager.single_color(self.bg_color, self._appearance_mode)) - self.text_label.configure(fg=ThemeManager.single_color(self.text_color, self.appearance_mode), - bg=ThemeManager.single_color(self.bg_color, self.appearance_mode)) + self.text_label.configure(fg=ThemeManager.single_color(self.text_color, self._appearance_mode), + bg=ThemeManager.single_color(self.bg_color, self._appearance_mode)) - self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self.appearance_mode)) + self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode)) def configure(self, *args, **kwargs): require_redraw = False # some attribute changes require a call of self.draw() at the end diff --git a/customtkinter/widgets/ctk_optionmenu.py b/customtkinter/widgets/ctk_optionmenu.py index 4f72e29..f95625e 100644 --- a/customtkinter/widgets/ctk_optionmenu.py +++ b/customtkinter/widgets/ctk_optionmenu.py @@ -1,5 +1,6 @@ import tkinter import sys +from typing import Union from .dropdown_menu import DropdownMenu @@ -23,7 +24,7 @@ class CTkOptionMenu(CTkBaseClass): variable=None, values=None, command=None, - width=120, + width=140, height=28, corner_radius="default_theme", text_font="default_theme", @@ -33,7 +34,7 @@ class CTkOptionMenu(CTkBaseClass): state=tkinter.NORMAL, **kwargs): - # transfer basic functionality (bg_color, size, appearance_mode, scaling) to CTkBaseClass + # transfer basic functionality (bg_color, size, _appearance_mode, scaling) to CTkBaseClass super().__init__(*args, bg_color=bg_color, width=width, height=height, **kwargs) # color variables @@ -61,13 +62,18 @@ class CTkOptionMenu(CTkBaseClass): self.state = state self.hover = hover self.click_animation_running = False + if values is None: self.values = ["CTkOptionMenu"] else: self.values = values - self.current_value = self.values[0] - self.dropdown_menu = None + if len(self.values) > 0: + self.current_value = self.values[0] + else: + self.current_value = "CTkOptionMenu" + + self.dropdown_menu: Union[DropdownMenu, None] = None # configure grid system (1x1) self.grid_rowconfigure(0, weight=1) @@ -75,8 +81,8 @@ class CTkOptionMenu(CTkBaseClass): self.canvas = CTkCanvas(master=self, highlightthickness=0, - width=self.apply_widget_scaling(self.desired_width), - height=self.apply_widget_scaling(self.desired_height)) + width=self.apply_widget_scaling(self._desired_width), + height=self.apply_widget_scaling(self._desired_height)) self.canvas.grid(row=0, column=0, rowspan=1, columnspan=1, sticky="nsew") self.draw_engine = DrawEngine(self.canvas) @@ -101,34 +107,34 @@ class CTkOptionMenu(CTkBaseClass): self.text_label.destroy() self.text_label = None - self.canvas.configure(width=self.apply_widget_scaling(self.desired_width), - height=self.apply_widget_scaling(self.desired_height)) + self.canvas.configure(width=self.apply_widget_scaling(self._desired_width), + height=self.apply_widget_scaling(self._desired_height)) self.draw() def set_dimensions(self, width: int = None, height: int = None): super().set_dimensions(width, height) - self.canvas.configure(width=self.apply_widget_scaling(self.desired_width), - height=self.apply_widget_scaling(self.desired_height)) + self.canvas.configure(width=self.apply_widget_scaling(self._desired_width), + height=self.apply_widget_scaling(self._desired_height)) self.draw() def draw(self, no_color_updates=False): - left_section_width = self.current_width - self.current_height - requires_recoloring = self.draw_engine.draw_rounded_rect_with_border_vertical_split(self.apply_widget_scaling(self.current_width), - self.apply_widget_scaling(self.current_height), + left_section_width = self._current_width - self._current_height + requires_recoloring = self.draw_engine.draw_rounded_rect_with_border_vertical_split(self.apply_widget_scaling(self._current_width), + self.apply_widget_scaling(self._current_height), self.apply_widget_scaling(self.corner_radius), 0, self.apply_widget_scaling(left_section_width)) - requires_recoloring_2 = self.draw_engine.draw_dropdown_arrow(self.apply_widget_scaling(self.current_width - (self.current_height / 2)), - self.apply_widget_scaling(self.current_height / 2), - self.apply_widget_scaling(self.current_height / 3)) + requires_recoloring_2 = self.draw_engine.draw_dropdown_arrow(self.apply_widget_scaling(self._current_width - (self._current_height / 2)), + self.apply_widget_scaling(self._current_height / 2), + self.apply_widget_scaling(self._current_height / 3)) if self.text_label is None: self.text_label = tkinter.Label(master=self, font=self.apply_font_scaling(self.text_font)) self.text_label.grid(row=0, column=0, rowspan=1, columnspan=1, sticky="w", padx=(max(self.apply_widget_scaling(self.corner_radius), 3), - max(self.current_width - left_section_width + 3, 3))) + max(self._current_width - left_section_width + 3, 3))) self.text_label.bind("", self.on_enter) self.text_label.bind("", self.on_leave) @@ -140,38 +146,39 @@ class CTkOptionMenu(CTkBaseClass): if no_color_updates is False or requires_recoloring or requires_recoloring_2: - self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self.appearance_mode)) + self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode)) self.canvas.itemconfig("inner_parts_left", - outline=ThemeManager.single_color(self.fg_color, self.appearance_mode), - fill=ThemeManager.single_color(self.fg_color, self.appearance_mode)) + outline=ThemeManager.single_color(self.fg_color, self._appearance_mode), + fill=ThemeManager.single_color(self.fg_color, self._appearance_mode)) self.canvas.itemconfig("inner_parts_right", - outline=ThemeManager.single_color(self.button_color, self.appearance_mode), - fill=ThemeManager.single_color(self.button_color, self.appearance_mode)) + outline=ThemeManager.single_color(self.button_color, self._appearance_mode), + fill=ThemeManager.single_color(self.button_color, self._appearance_mode)) - self.text_label.configure(fg=ThemeManager.single_color(self.text_color, self.appearance_mode)) + self.text_label.configure(fg=ThemeManager.single_color(self.text_color, self._appearance_mode)) if self.state == tkinter.DISABLED: - self.text_label.configure(fg=(ThemeManager.single_color(self.text_color_disabled, self.appearance_mode))) + self.text_label.configure(fg=(ThemeManager.single_color(self.text_color_disabled, self._appearance_mode))) self.canvas.itemconfig("dropdown_arrow", - fill=ThemeManager.single_color(self.text_color_disabled, self.appearance_mode)) + fill=ThemeManager.single_color(self.text_color_disabled, self._appearance_mode)) else: - self.text_label.configure(fg=ThemeManager.single_color(self.text_color, self.appearance_mode)) + self.text_label.configure(fg=ThemeManager.single_color(self.text_color, self._appearance_mode)) self.canvas.itemconfig("dropdown_arrow", - fill=ThemeManager.single_color(self.text_color, self.appearance_mode)) + fill=ThemeManager.single_color(self.text_color, self._appearance_mode)) - self.text_label.configure(bg=ThemeManager.single_color(self.fg_color, self.appearance_mode)) + self.text_label.configure(bg=ThemeManager.single_color(self.fg_color, self._appearance_mode)) def open_dropdown_menu(self): - self.dropdown_menu = DropdownMenu(x_position=self.winfo_rootx(), - y_position=self.winfo_rooty() + self.apply_widget_scaling(self.current_height + 4), - width=self.current_width, - values=self.values, - command=self.set, - fg_color=self.dropdown_color, - button_hover_color=self.dropdown_hover_color, - button_color=self.dropdown_color, - text_color=self.dropdown_text_color) + if len(self.values) > 0: + self.dropdown_menu = DropdownMenu(x_position=self.winfo_rootx(), + y_position=self.winfo_rooty() + self.apply_widget_scaling(self._current_height + 4), + width=self._current_width, + values=self.values, + command=self.set, + fg_color=self.dropdown_color, + button_hover_color=self.dropdown_hover_color, + button_color=self.dropdown_color, + text_color=self.dropdown_text_color) def configure(self, *args, **kwargs): require_redraw = False # some attribute changes require a call of self.draw() at the end @@ -236,6 +243,11 @@ class CTkOptionMenu(CTkBaseClass): self.set_dimensions(height=kwargs["height"]) del kwargs["height"] + if "values" in kwargs: + self.values = kwargs["values"] + del kwargs["values"] + self.set_cursor() + super().configure(*args, **kwargs) if require_redraw: @@ -259,8 +271,8 @@ class CTkOptionMenu(CTkBaseClass): if self.hover is True and self.state == tkinter.NORMAL: # set color of inner button parts to hover color self.canvas.itemconfig("inner_parts_right", - outline=ThemeManager.single_color(self.button_hover_color, self.appearance_mode), - fill=ThemeManager.single_color(self.button_hover_color, self.appearance_mode)) + outline=ThemeManager.single_color(self.button_hover_color, self._appearance_mode), + fill=ThemeManager.single_color(self.button_hover_color, self._appearance_mode)) def on_leave(self, event=0): self.click_animation_running = False @@ -268,8 +280,8 @@ class CTkOptionMenu(CTkBaseClass): if self.hover is True: # set color of inner button parts self.canvas.itemconfig("inner_parts_right", - outline=ThemeManager.single_color(self.button_color, self.appearance_mode), - fill=ThemeManager.single_color(self.button_color, self.appearance_mode)) + outline=ThemeManager.single_color(self.button_color, self._appearance_mode), + fill=ThemeManager.single_color(self.button_color, self._appearance_mode)) def click_animation(self): if self.click_animation_running: @@ -294,10 +306,7 @@ class CTkOptionMenu(CTkBaseClass): if not from_variable_callback: if self.function is not None: - try: - self.function(self.current_value) - except Exception: - pass + self.function(self.current_value) def get(self) -> str: return self.current_value diff --git a/customtkinter/widgets/ctk_progressbar.py b/customtkinter/widgets/ctk_progressbar.py index 1991bf4..4cf1304 100644 --- a/customtkinter/widgets/ctk_progressbar.py +++ b/customtkinter/widgets/ctk_progressbar.py @@ -3,12 +3,11 @@ import tkinter from .ctk_canvas import CTkCanvas from ..theme_manager import ThemeManager from ..draw_engine import DrawEngine -from ..settings import Settings from .widget_base_class import CTkBaseClass class CTkProgressBar(CTkBaseClass): - """ tkinter custom progressbar, always horizontal, values are from 0 to 1 """ + """ tkinter custom progressbar, values from 0 to 1 """ def __init__(self, *args, variable=None, @@ -35,7 +34,7 @@ class CTkProgressBar(CTkBaseClass): else: height = 8 - # transfer basic functionality (bg_color, size, appearance_mode, scaling) to CTkBaseClass + # transfer basic functionality (bg_color, size, _appearance_mode, scaling) to CTkBaseClass super().__init__(*args, bg_color=bg_color, width=width, height=height, **kwargs) # color @@ -59,8 +58,8 @@ class CTkProgressBar(CTkBaseClass): self.canvas = CTkCanvas(master=self, highlightthickness=0, - width=self.apply_widget_scaling(self.desired_width), - height=self.apply_widget_scaling(self.desired_height)) + width=self.apply_widget_scaling(self._desired_width), + height=self.apply_widget_scaling(self._desired_height)) self.canvas.grid(row=0, column=0, rowspan=1, columnspan=1, sticky="nswe") self.draw_engine = DrawEngine(self.canvas) @@ -78,14 +77,14 @@ class CTkProgressBar(CTkBaseClass): def set_scaling(self, *args, **kwargs): super().set_scaling(*args, **kwargs) - self.canvas.configure(width=self.apply_widget_scaling(self.desired_width), height=self.apply_widget_scaling(self.desired_height)) + self.canvas.configure(width=self.apply_widget_scaling(self._desired_width), height=self.apply_widget_scaling(self._desired_height)) self.draw() def set_dimensions(self, width=None, height=None): super().set_dimensions(width, height) - self.canvas.configure(width=self.apply_widget_scaling(self.desired_width), - height=self.apply_widget_scaling(self.desired_height)) + self.canvas.configure(width=self.apply_widget_scaling(self._desired_width), + height=self.apply_widget_scaling(self._desired_height)) self.draw() def destroy(self): @@ -102,24 +101,24 @@ class CTkProgressBar(CTkBaseClass): else: orientation = "w" - requires_recoloring = self.draw_engine.draw_rounded_progress_bar_with_border(self.apply_widget_scaling(self.current_width), - self.apply_widget_scaling(self.current_height), + requires_recoloring = self.draw_engine.draw_rounded_progress_bar_with_border(self.apply_widget_scaling(self._current_width), + self.apply_widget_scaling(self._current_height), self.apply_widget_scaling(self.corner_radius), self.apply_widget_scaling(self.border_width), self.value, orientation) if no_color_updates is False or requires_recoloring: - self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self.appearance_mode)) + self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode)) self.canvas.itemconfig("border_parts", - fill=ThemeManager.single_color(self.border_color, self.appearance_mode), - outline=ThemeManager.single_color(self.border_color, self.appearance_mode)) + fill=ThemeManager.single_color(self.border_color, self._appearance_mode), + outline=ThemeManager.single_color(self.border_color, self._appearance_mode)) self.canvas.itemconfig("inner_parts", - fill=ThemeManager.single_color(self.fg_color, self.appearance_mode), - outline=ThemeManager.single_color(self.fg_color, self.appearance_mode)) + fill=ThemeManager.single_color(self.fg_color, self._appearance_mode), + outline=ThemeManager.single_color(self.fg_color, self._appearance_mode)) self.canvas.itemconfig("progress_parts", - fill=ThemeManager.single_color(self.progress_color, self.appearance_mode), - outline=ThemeManager.single_color(self.progress_color, self.appearance_mode)) + fill=ThemeManager.single_color(self.progress_color, self._appearance_mode), + outline=ThemeManager.single_color(self.progress_color, self._appearance_mode)) def configure(self, *args, **kwargs): require_redraw = False # some attribute changes require a call of self.draw() at the end diff --git a/customtkinter/widgets/ctk_radiobutton.py b/customtkinter/widgets/ctk_radiobutton.py index ac690ac..779d9b8 100644 --- a/customtkinter/widgets/ctk_radiobutton.py +++ b/customtkinter/widgets/ctk_radiobutton.py @@ -32,7 +32,7 @@ class CTkRadioButton(CTkBaseClass): textvariable=None, **kwargs): - # transfer basic functionality (bg_color, size, appearance_mode, scaling) to CTkBaseClass + # transfer basic functionality (bg_color, size, _appearance_mode, scaling) to CTkBaseClass super().__init__(*args, bg_color=bg_color, width=width, height=height, **kwargs) # color @@ -71,14 +71,14 @@ class CTkRadioButton(CTkBaseClass): self.bg_canvas = CTkCanvas(master=self, highlightthickness=0, - width=self.apply_widget_scaling(self.current_width), - height=self.apply_widget_scaling(self.current_height)) + width=self.apply_widget_scaling(self._current_width), + height=self.apply_widget_scaling(self._current_height)) self.bg_canvas.grid(row=0, column=0, padx=0, pady=0, columnspan=3, rowspan=1, sticky="nswe") self.canvas = CTkCanvas(master=self, highlightthickness=0, - width=self.apply_widget_scaling(self.current_width), - height=self.apply_widget_scaling(self.current_height)) + width=self.apply_widget_scaling(self._current_width), + height=self.apply_widget_scaling(self._current_height)) self.canvas.grid(row=0, column=0, padx=0, pady=0, columnspan=1) self.draw_engine = DrawEngine(self.canvas) @@ -102,8 +102,8 @@ class CTkRadioButton(CTkBaseClass): self.grid_columnconfigure(1, weight=0, minsize=self.apply_widget_scaling(6)) self.text_label.configure(font=self.apply_font_scaling(self.text_font)) - self.bg_canvas.configure(width=self.apply_widget_scaling(self.desired_width), height=self.apply_widget_scaling(self.desired_height)) - self.canvas.configure(width=self.apply_widget_scaling(self.desired_width), height=self.apply_widget_scaling(self.desired_height)) + self.bg_canvas.configure(width=self.apply_widget_scaling(self._desired_width), height=self.apply_widget_scaling(self._desired_height)) + self.canvas.configure(width=self.apply_widget_scaling(self._desired_width), height=self.apply_widget_scaling(self._desired_height)) self.draw() def destroy(self): @@ -113,26 +113,26 @@ class CTkRadioButton(CTkBaseClass): super().destroy() def draw(self, no_color_updates=False): - requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.apply_widget_scaling(self.current_width), - self.apply_widget_scaling(self.current_height), + requires_recoloring = self.draw_engine.draw_rounded_rect_with_border(self.apply_widget_scaling(self._current_width), + self.apply_widget_scaling(self._current_height), self.apply_widget_scaling(self.corner_radius), self.apply_widget_scaling(self.border_width)) - self.bg_canvas.configure(bg=ThemeManager.single_color(self.bg_color, self.appearance_mode)) - self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self.appearance_mode)) + self.bg_canvas.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode)) + self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode)) if self.check_state is False: self.canvas.itemconfig("border_parts", - outline=ThemeManager.single_color(self.border_color, self.appearance_mode), - fill=ThemeManager.single_color(self.border_color, self.appearance_mode)) + outline=ThemeManager.single_color(self.border_color, self._appearance_mode), + fill=ThemeManager.single_color(self.border_color, self._appearance_mode)) else: self.canvas.itemconfig("border_parts", - outline=ThemeManager.single_color(self.fg_color, self.appearance_mode), - fill=ThemeManager.single_color(self.fg_color, self.appearance_mode)) + outline=ThemeManager.single_color(self.fg_color, self._appearance_mode), + fill=ThemeManager.single_color(self.fg_color, self._appearance_mode)) self.canvas.itemconfig("inner_parts", - outline=ThemeManager.single_color(self.bg_color, self.appearance_mode), - fill=ThemeManager.single_color(self.bg_color, self.appearance_mode)) + outline=ThemeManager.single_color(self.bg_color, self._appearance_mode), + fill=ThemeManager.single_color(self.bg_color, self._appearance_mode)) if self.text_label is None: self.text_label = tkinter.Label(master=self, @@ -148,11 +148,11 @@ class CTkRadioButton(CTkBaseClass): self.text_label.bind("", self.invoke) if self.state == tkinter.DISABLED: - self.text_label.configure(fg=ThemeManager.single_color(self.text_color_disabled, self.appearance_mode)) + self.text_label.configure(fg=ThemeManager.single_color(self.text_color_disabled, self._appearance_mode)) else: - self.text_label.configure(fg=ThemeManager.single_color(self.text_color, self.appearance_mode)) + self.text_label.configure(fg=ThemeManager.single_color(self.text_color, self._appearance_mode)) - self.text_label.configure(bg=ThemeManager.single_color(self.bg_color, self.appearance_mode)) + self.text_label.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode)) self.set_text(self.text) @@ -260,19 +260,19 @@ class CTkRadioButton(CTkBaseClass): def on_enter(self, event=0): if self.hover is True and self.state == tkinter.NORMAL: self.canvas.itemconfig("border_parts", - fill=ThemeManager.single_color(self.hover_color, self.appearance_mode), - outline=ThemeManager.single_color(self.hover_color, self.appearance_mode)) + fill=ThemeManager.single_color(self.hover_color, self._appearance_mode), + outline=ThemeManager.single_color(self.hover_color, self._appearance_mode)) def on_leave(self, event=0): if self.hover is True: if self.check_state is True: self.canvas.itemconfig("border_parts", - fill=ThemeManager.single_color(self.fg_color, self.appearance_mode), - outline=ThemeManager.single_color(self.fg_color, self.appearance_mode)) + fill=ThemeManager.single_color(self.fg_color, self._appearance_mode), + outline=ThemeManager.single_color(self.fg_color, self._appearance_mode)) else: self.canvas.itemconfig("border_parts", - fill=ThemeManager.single_color(self.border_color, self.appearance_mode), - outline=ThemeManager.single_color(self.border_color, self.appearance_mode)) + fill=ThemeManager.single_color(self.border_color, self._appearance_mode), + outline=ThemeManager.single_color(self.border_color, self._appearance_mode)) def variable_callback(self, var_name, index, mode): if not self.variable_callback_blocked: diff --git a/customtkinter/widgets/ctk_slider.py b/customtkinter/widgets/ctk_slider.py index 58f80a3..6fc6a5c 100644 --- a/customtkinter/widgets/ctk_slider.py +++ b/customtkinter/widgets/ctk_slider.py @@ -9,7 +9,7 @@ from .widget_base_class import CTkBaseClass class CTkSlider(CTkBaseClass): - """ tkinter custom slider, always horizontal """ + """ tkinter custom slider""" def __init__(self, *args, bg_color=None, @@ -44,7 +44,7 @@ class CTkSlider(CTkBaseClass): else: height = 16 - # transfer basic functionality (bg_color, size, appearance_mode, scaling) to CTkBaseClass + # transfer basic functionality (bg_color, size, _appearance_mode, scaling) to CTkBaseClass super().__init__(*args, bg_color=bg_color, width=width, height=height, **kwargs) # color @@ -81,8 +81,8 @@ class CTkSlider(CTkBaseClass): self.canvas = CTkCanvas(master=self, highlightthickness=0, - width=self.apply_widget_scaling(self.desired_width), - height=self.apply_widget_scaling(self.desired_height)) + width=self.apply_widget_scaling(self._desired_width), + height=self.apply_widget_scaling(self._desired_height)) self.canvas.grid(column=0, row=0, rowspan=1, columnspan=1, sticky="nswe") self.draw_engine = DrawEngine(self.canvas) @@ -106,14 +106,14 @@ class CTkSlider(CTkBaseClass): def set_scaling(self, *args, **kwargs): super().set_scaling(*args, **kwargs) - self.canvas.configure(width=self.apply_widget_scaling(self.desired_width), height=self.apply_widget_scaling(self.desired_height)) + self.canvas.configure(width=self.apply_widget_scaling(self._desired_width), height=self.apply_widget_scaling(self._desired_height)) self.draw() def set_dimensions(self, width=None, height=None): super().set_dimensions(width, height) - self.canvas.configure(width=self.apply_widget_scaling(self.desired_width), - height=self.apply_widget_scaling(self.desired_height)) + self.canvas.configure(width=self.apply_widget_scaling(self._desired_width), + height=self.apply_widget_scaling(self._desired_height)) self.draw() def destroy(self): @@ -138,8 +138,8 @@ class CTkSlider(CTkBaseClass): else: orientation = "w" - requires_recoloring = self.draw_engine.draw_rounded_slider_with_border_and_button(self.apply_widget_scaling(self.current_width), - self.apply_widget_scaling(self.current_height), + requires_recoloring = self.draw_engine.draw_rounded_slider_with_border_and_button(self.apply_widget_scaling(self._current_width), + self.apply_widget_scaling(self._current_height), self.apply_widget_scaling(self.corner_radius), self.apply_widget_scaling(self.border_width), self.apply_widget_scaling(self.button_length), @@ -147,33 +147,33 @@ class CTkSlider(CTkBaseClass): self.value, orientation) if no_color_updates is False or requires_recoloring: - self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self.appearance_mode)) + self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode)) if self.border_color is None: - self.canvas.itemconfig("border_parts", fill=ThemeManager.single_color(self.bg_color, self.appearance_mode), - outline=ThemeManager.single_color(self.bg_color, self.appearance_mode)) + self.canvas.itemconfig("border_parts", fill=ThemeManager.single_color(self.bg_color, self._appearance_mode), + outline=ThemeManager.single_color(self.bg_color, self._appearance_mode)) else: - self.canvas.itemconfig("border_parts", fill=ThemeManager.single_color(self.border_color, self.appearance_mode), - outline=ThemeManager.single_color(self.border_color, self.appearance_mode)) + self.canvas.itemconfig("border_parts", fill=ThemeManager.single_color(self.border_color, self._appearance_mode), + outline=ThemeManager.single_color(self.border_color, self._appearance_mode)) - self.canvas.itemconfig("inner_parts", fill=ThemeManager.single_color(self.fg_color, self.appearance_mode), - outline=ThemeManager.single_color(self.fg_color, self.appearance_mode)) + self.canvas.itemconfig("inner_parts", fill=ThemeManager.single_color(self.fg_color, self._appearance_mode), + outline=ThemeManager.single_color(self.fg_color, self._appearance_mode)) if self.progress_color is None: - self.canvas.itemconfig("progress_parts", fill=ThemeManager.single_color(self.fg_color, self.appearance_mode), - outline=ThemeManager.single_color(self.fg_color, self.appearance_mode)) + self.canvas.itemconfig("progress_parts", fill=ThemeManager.single_color(self.fg_color, self._appearance_mode), + outline=ThemeManager.single_color(self.fg_color, self._appearance_mode)) else: - self.canvas.itemconfig("progress_parts", fill=ThemeManager.single_color(self.progress_color, self.appearance_mode), - outline=ThemeManager.single_color(self.progress_color, self.appearance_mode)) + self.canvas.itemconfig("progress_parts", fill=ThemeManager.single_color(self.progress_color, self._appearance_mode), + outline=ThemeManager.single_color(self.progress_color, self._appearance_mode)) - self.canvas.itemconfig("slider_parts", fill=ThemeManager.single_color(self.button_color, self.appearance_mode), - outline=ThemeManager.single_color(self.button_color, self.appearance_mode)) + self.canvas.itemconfig("slider_parts", fill=ThemeManager.single_color(self.button_color, self._appearance_mode), + outline=ThemeManager.single_color(self.button_color, self._appearance_mode)) def clicked(self, event=None): if self.orient.lower() == "horizontal": - self.value = (event.x / self.current_width) / self.widget_scaling + self.value = (event.x / self._current_width) / self._widget_scaling else: - self.value = 1 - (event.y / self.current_height) / self.widget_scaling + self.value = 1 - (event.y / self._current_height) / self._widget_scaling if self.value > 1: self.value = 1 @@ -195,13 +195,13 @@ class CTkSlider(CTkBaseClass): def on_enter(self, event=0): self.hover_state = True - self.canvas.itemconfig("slider_parts", fill=ThemeManager.single_color(self.button_hover_color, self.appearance_mode), - outline=ThemeManager.single_color(self.button_hover_color, self.appearance_mode)) + self.canvas.itemconfig("slider_parts", fill=ThemeManager.single_color(self.button_hover_color, self._appearance_mode), + outline=ThemeManager.single_color(self.button_hover_color, self._appearance_mode)) def on_leave(self, event=0): self.hover_state = False - self.canvas.itemconfig("slider_parts", fill=ThemeManager.single_color(self.button_color, self.appearance_mode), - outline=ThemeManager.single_color(self.button_color, self.appearance_mode)) + self.canvas.itemconfig("slider_parts", fill=ThemeManager.single_color(self.button_color, self._appearance_mode), + outline=ThemeManager.single_color(self.button_color, self._appearance_mode)) def round_to_step_size(self, value): if self.number_of_steps is not None: diff --git a/customtkinter/widgets/ctk_switch.py b/customtkinter/widgets/ctk_switch.py index 2e98f09..a7084d2 100644 --- a/customtkinter/widgets/ctk_switch.py +++ b/customtkinter/widgets/ctk_switch.py @@ -34,7 +34,7 @@ class CTkSwitch(CTkBaseClass): state=tkinter.NORMAL, **kwargs): - # transfer basic functionality (bg_color, size, appearance_mode, scaling) to CTkBaseClass + # transfer basic functionality (bg_color, size, _appearance_mode, scaling) to CTkBaseClass super().__init__(*args, bg_color=bg_color, width=width, height=height, **kwargs) # color @@ -79,14 +79,14 @@ class CTkSwitch(CTkBaseClass): self.bg_canvas = CTkCanvas(master=self, highlightthickness=0, - width=self.apply_widget_scaling(self.current_width), - height=self.apply_widget_scaling(self.current_height)) + width=self.apply_widget_scaling(self._current_width), + height=self.apply_widget_scaling(self._current_height)) self.bg_canvas.grid(row=0, column=0, padx=0, pady=0, columnspan=3, rowspan=1, sticky="nswe") self.canvas = CTkCanvas(master=self, highlightthickness=0, - width=self.apply_widget_scaling(self.current_width), - height=self.apply_widget_scaling(self.current_height)) + width=self.apply_widget_scaling(self._current_width), + height=self.apply_widget_scaling(self._current_height)) self.canvas.grid(row=0, column=0, padx=0, pady=0, columnspan=1, sticky="nswe") self.draw_engine = DrawEngine(self.canvas) @@ -110,8 +110,8 @@ class CTkSwitch(CTkBaseClass): self.grid_columnconfigure(1, weight=0, minsize=self.apply_widget_scaling(6)) self.text_label.configure(font=self.apply_font_scaling(self.text_font)) - self.bg_canvas.configure(width=self.apply_widget_scaling(self.desired_width), height=self.apply_widget_scaling(self.desired_height)) - self.canvas.configure(width=self.apply_widget_scaling(self.desired_width), height=self.apply_widget_scaling(self.desired_height)) + self.bg_canvas.configure(width=self.apply_widget_scaling(self._desired_width), height=self.apply_widget_scaling(self._desired_height)) + self.canvas.configure(width=self.apply_widget_scaling(self._desired_width), height=self.apply_widget_scaling(self._desired_height)) self.draw() def destroy(self): @@ -146,16 +146,16 @@ class CTkSwitch(CTkBaseClass): def draw(self, no_color_updates=False): if self.check_state is True: - requires_recoloring = self.draw_engine.draw_rounded_slider_with_border_and_button(self.apply_widget_scaling(self.current_width), - self.apply_widget_scaling(self.current_height), + requires_recoloring = self.draw_engine.draw_rounded_slider_with_border_and_button(self.apply_widget_scaling(self._current_width), + self.apply_widget_scaling(self._current_height), self.apply_widget_scaling(self.corner_radius), self.apply_widget_scaling(self.border_width), self.apply_widget_scaling(self.button_length), self.apply_widget_scaling(self.corner_radius), 1, "w") else: - requires_recoloring = self.draw_engine.draw_rounded_slider_with_border_and_button(self.apply_widget_scaling(self.current_width), - self.apply_widget_scaling(self.current_height), + requires_recoloring = self.draw_engine.draw_rounded_slider_with_border_and_button(self.apply_widget_scaling(self._current_width), + self.apply_widget_scaling(self._current_height), self.apply_widget_scaling(self.corner_radius), self.apply_widget_scaling(self.border_width), self.apply_widget_scaling(self.button_length), @@ -163,28 +163,28 @@ class CTkSwitch(CTkBaseClass): 0, "w") if no_color_updates is False or requires_recoloring: - self.bg_canvas.configure(bg=ThemeManager.single_color(self.bg_color, self.appearance_mode)) - self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self.appearance_mode)) + self.bg_canvas.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode)) + self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode)) if self.border_color is None: - self.canvas.itemconfig("border_parts", fill=ThemeManager.single_color(self.bg_color, self.appearance_mode), - outline=ThemeManager.single_color(self.bg_color, self.appearance_mode)) + self.canvas.itemconfig("border_parts", fill=ThemeManager.single_color(self.bg_color, self._appearance_mode), + outline=ThemeManager.single_color(self.bg_color, self._appearance_mode)) else: - self.canvas.itemconfig("border_parts", fill=ThemeManager.single_color(self.border_color, self.appearance_mode), - outline=ThemeManager.single_color(self.border_color, self.appearance_mode)) + self.canvas.itemconfig("border_parts", fill=ThemeManager.single_color(self.border_color, self._appearance_mode), + outline=ThemeManager.single_color(self.border_color, self._appearance_mode)) - self.canvas.itemconfig("inner_parts", fill=ThemeManager.single_color(self.fg_color, self.appearance_mode), - outline=ThemeManager.single_color(self.fg_color, self.appearance_mode)) + self.canvas.itemconfig("inner_parts", fill=ThemeManager.single_color(self.fg_color, self._appearance_mode), + outline=ThemeManager.single_color(self.fg_color, self._appearance_mode)) if self.progress_color is None: - self.canvas.itemconfig("progress_parts", fill=ThemeManager.single_color(self.fg_color, self.appearance_mode), - outline=ThemeManager.single_color(self.fg_color, self.appearance_mode)) + self.canvas.itemconfig("progress_parts", fill=ThemeManager.single_color(self.fg_color, self._appearance_mode), + outline=ThemeManager.single_color(self.fg_color, self._appearance_mode)) else: - self.canvas.itemconfig("progress_parts", fill=ThemeManager.single_color(self.progress_color, self.appearance_mode), - outline=ThemeManager.single_color(self.progress_color, self.appearance_mode)) + self.canvas.itemconfig("progress_parts", fill=ThemeManager.single_color(self.progress_color, self._appearance_mode), + outline=ThemeManager.single_color(self.progress_color, self._appearance_mode)) - self.canvas.itemconfig("slider_parts", fill=ThemeManager.single_color(self.button_color, self.appearance_mode), - outline=ThemeManager.single_color(self.button_color, self.appearance_mode)) + self.canvas.itemconfig("slider_parts", fill=ThemeManager.single_color(self.button_color, self._appearance_mode), + outline=ThemeManager.single_color(self.button_color, self._appearance_mode)) if self.text_label is None: self.text_label = tkinter.Label(master=self, @@ -203,11 +203,11 @@ class CTkSwitch(CTkBaseClass): self.text_label.configure(textvariable=self.textvariable) if self.state == tkinter.DISABLED: - self.text_label.configure(fg=(ThemeManager.single_color(self.text_color_disabled, self.appearance_mode))) + self.text_label.configure(fg=(ThemeManager.single_color(self.text_color_disabled, self._appearance_mode))) else: - self.text_label.configure(fg=ThemeManager.single_color(self.text_color, self.appearance_mode)) + self.text_label.configure(fg=ThemeManager.single_color(self.text_color, self._appearance_mode)) - self.text_label.configure(bg=ThemeManager.single_color(self.bg_color, self.appearance_mode)) + self.text_label.configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode)) self.set_text(self.text) @@ -239,28 +239,28 @@ class CTkSwitch(CTkBaseClass): self.draw(no_color_updates=True) - if self.callback_function is not None: - self.callback_function() - if self.variable is not None and not from_variable_callback: self.variable_callback_blocked = True self.variable.set(self.onvalue) self.variable_callback_blocked = False + if self.callback_function is not None: + self.callback_function() + def deselect(self, from_variable_callback=False): if self.state is not tkinter.DISABLED or from_variable_callback: self.check_state = False self.draw(no_color_updates=True) - if self.callback_function is not None: - self.callback_function() - if self.variable is not None and not from_variable_callback: self.variable_callback_blocked = True self.variable.set(self.offvalue) self.variable_callback_blocked = False + if self.callback_function is not None: + self.callback_function() + def get(self): return self.onvalue if self.check_state is True else self.offvalue @@ -268,13 +268,13 @@ class CTkSwitch(CTkBaseClass): self.hover_state = True if self.state is not tkinter.DISABLED: - self.canvas.itemconfig("slider_parts", fill=ThemeManager.single_color(self.button_hover_color, self.appearance_mode), - outline=ThemeManager.single_color(self.button_hover_color, self.appearance_mode)) + self.canvas.itemconfig("slider_parts", fill=ThemeManager.single_color(self.button_hover_color, self._appearance_mode), + outline=ThemeManager.single_color(self.button_hover_color, self._appearance_mode)) def on_leave(self, event=0): self.hover_state = False - self.canvas.itemconfig("slider_parts", fill=ThemeManager.single_color(self.button_color, self.appearance_mode), - outline=ThemeManager.single_color(self.button_color, self.appearance_mode)) + self.canvas.itemconfig("slider_parts", fill=ThemeManager.single_color(self.button_color, self._appearance_mode), + outline=ThemeManager.single_color(self.button_color, self._appearance_mode)) def variable_callback(self, var_name, index, mode): if not self.variable_callback_blocked: diff --git a/customtkinter/widgets/dropdown_menu.py b/customtkinter/widgets/dropdown_menu.py index 5c14ce6..34ab383 100644 --- a/customtkinter/widgets/dropdown_menu.py +++ b/customtkinter/widgets/dropdown_menu.py @@ -40,13 +40,14 @@ class DropdownMenu(tkinter.Toplevel): self.text_color = text_color # shape - self.width = width self.corner_radius = corner_radius self.button_corner_radius = button_corner_radius self.button_height = button_height + self.width = width + self.height = max(len(self.values), 1) * (self.button_height + y_spacing) + y_spacing self.geometry(f"{round(self.apply_widget_scaling(self.width))}x" + - f"{round(self.apply_widget_scaling(len(self.values) * (self.button_height + y_spacing) + y_spacing))}+" + + f"{round(self.apply_widget_scaling(self.height))}+" + f"{round(x_position)}+{round(y_position)}") self.grid_columnconfigure(0, weight=1) diff --git a/customtkinter/widgets/widget_base_class.py b/customtkinter/widgets/widget_base_class.py index b910105..9988ce0 100644 --- a/customtkinter/widgets/widget_base_class.py +++ b/customtkinter/widgets/widget_base_class.py @@ -17,38 +17,48 @@ from ..theme_manager import ThemeManager class CTkBaseClass(tkinter.Frame): - def __init__(self, *args, bg_color=None, width, height, **kwargs): + """ Base class of every Ctk widget, handles the dimensions, bg_color, + appearance_mode changes, scaling, bg changes of master if master is not a CTk widget """ + + def __init__(self, + *args, + bg_color: Union[str, tuple] = None, + width: int, + height: int, + **kwargs): super().__init__(*args, width=width, height=height, **kwargs) # set desired size of underlying tkinter.Frame - self.bg_color = self.detect_color_of_master() if bg_color is None else bg_color + # dimensions + self._current_width = width # _current_width and _current_height in pixel, represent current size of the widget + self._current_height = height # _current_width and _current_height are independent of the scale + self._desired_width = width # _desired_width and _desired_height, represent desired size set by width and height + self._desired_height = height - self.current_width = width # current_width and current_height in pixel, represent current size of the widget (not the desired size by init) - self.current_height = height # current_width and current_height are independent of the scale - self.desired_width = width - self.desired_height = height + # scaling + ScalingTracker.add_widget(self.set_scaling, self) # add callback for automatic scaling changes + self._widget_scaling = ScalingTracker.get_widget_scaling(self) + self._spacing_scaling = ScalingTracker.get_spacing_scaling(self) - # add set_scaling method to callback list of ScalingTracker for automatic scaling changes - ScalingTracker.add_widget(self.set_scaling, self) - self.widget_scaling = ScalingTracker.get_widget_scaling(self) - self.spacing_scaling = ScalingTracker.get_spacing_scaling(self) - - super().configure(width=self.apply_widget_scaling(self.desired_width), - height=self.apply_widget_scaling(self.desired_height)) + super().configure(width=self.apply_widget_scaling(self._desired_width), + height=self.apply_widget_scaling(self._desired_height)) # save latest geometry function and kwargs class GeometryCallDict(TypedDict): function: Callable kwargs: dict - self.last_geometry_manager_call: Union[GeometryCallDict, None] = None + self._last_geometry_manager_call: Union[GeometryCallDict, None] = None # add set_appearance_mode method to callback list of AppearanceModeTracker for appearance mode changes AppearanceModeTracker.add(self.set_appearance_mode, self) - self.appearance_mode = AppearanceModeTracker.get_mode() # 0: "Light" 1: "Dark" + self._appearance_mode = AppearanceModeTracker.get_mode() # 0: "Light" 1: "Dark" - super().configure(bg=ThemeManager.single_color(self.bg_color, self.appearance_mode)) + # background color + self.bg_color = self.detect_color_of_master() if bg_color is None else bg_color - # overwrite configure methods of master when master is tkinter widget, so that bg changes get applied on child CTk widget too + super().configure(bg=ThemeManager.single_color(self.bg_color, self._appearance_mode)) + + # overwrite configure methods of master when master is tkinter widget, so that bg changes get applied on child CTk widget as well if isinstance(self.master, (tkinter.Tk, tkinter.Toplevel, tkinter.Frame)) and not isinstance(self.master, (CTkBaseClass, CTk, CTkToplevel)): master_old_configure = self.master.config @@ -74,15 +84,15 @@ class CTkBaseClass(tkinter.Frame): super().destroy() def place(self, **kwargs): - self.last_geometry_manager_call = {"function": super().place, "kwargs": kwargs} + self._last_geometry_manager_call = {"function": super().place, "kwargs": kwargs} super().place(**self.apply_argument_scaling(kwargs)) def pack(self, **kwargs): - self.last_geometry_manager_call = {"function": super().pack, "kwargs": kwargs} + self._last_geometry_manager_call = {"function": super().pack, "kwargs": kwargs} super().pack(**self.apply_argument_scaling(kwargs)) def grid(self, **kwargs): - self.last_geometry_manager_call = {"function": super().grid, "kwargs": kwargs} + self._last_geometry_manager_call = {"function": super().grid, "kwargs": kwargs} super().grid(**self.apply_argument_scaling(kwargs)) def apply_argument_scaling(self, kwargs: dict) -> dict: @@ -129,9 +139,9 @@ class CTkBaseClass(tkinter.Frame): def update_dimensions_event(self, event): # only redraw if dimensions changed (for performance) - if round(self.current_width) != round(event.width / self.widget_scaling) or round(self.current_height) != round(event.height / self.widget_scaling): - self.current_width = (event.width / self.widget_scaling) # adjust current size according to new size given by event - self.current_height = (event.height / self.widget_scaling) # current_width and current_height are independent of the scale + if round(self._current_width) != round(event.width / self._widget_scaling) or round(self._current_height) != round(event.height / self._widget_scaling): + self._current_width = (event.width / self._widget_scaling) # adjust current size according to new size given by event + self._current_height = (event.height / self._widget_scaling) # _current_width and _current_height are independent of the scale self.draw(no_color_updates=True) # faster drawing without color changes @@ -164,9 +174,9 @@ class CTkBaseClass(tkinter.Frame): def set_appearance_mode(self, mode_string): if mode_string.lower() == "dark": - self.appearance_mode = 1 + self._appearance_mode = 1 elif mode_string.lower() == "light": - self.appearance_mode = 0 + self._appearance_mode = 0 if isinstance(self.master, (CTkBaseClass, CTk)) and hasattr(self.master, "fg_color"): self.bg_color = self.master.fg_color @@ -176,33 +186,33 @@ class CTkBaseClass(tkinter.Frame): self.draw() def set_scaling(self, new_widget_scaling, new_spacing_scaling, new_window_scaling): - self.widget_scaling = new_widget_scaling - self.spacing_scaling = new_spacing_scaling + self._widget_scaling = new_widget_scaling + self._spacing_scaling = new_spacing_scaling - super().configure(width=self.apply_widget_scaling(self.desired_width), - height=self.apply_widget_scaling(self.desired_height)) + super().configure(width=self.apply_widget_scaling(self._desired_width), + height=self.apply_widget_scaling(self._desired_height)) - if self.last_geometry_manager_call is not None: - self.last_geometry_manager_call["function"](**self.apply_argument_scaling(self.last_geometry_manager_call["kwargs"])) + if self._last_geometry_manager_call is not None: + self._last_geometry_manager_call["function"](**self.apply_argument_scaling(self._last_geometry_manager_call["kwargs"])) def set_dimensions(self, width=None, height=None): if width is not None: - self.desired_width = width + self._desired_width = width if height is not None: - self.desired_height = height + self._desired_height = height - super().configure(width=self.apply_widget_scaling(self.desired_width), - height=self.apply_widget_scaling(self.desired_height)) + super().configure(width=self.apply_widget_scaling(self._desired_width), + height=self.apply_widget_scaling(self._desired_height)) - def apply_widget_scaling(self, value): + def apply_widget_scaling(self, value: Union[int, float, str]) -> Union[float, str]: if isinstance(value, (int, float)): - return value * self.widget_scaling + return value * self._widget_scaling else: return value - def apply_spacing_scaling(self, value): + def apply_spacing_scaling(self, value: Union[int, float, str]) -> Union[float, str]: if isinstance(value, (int, float)): - return value * self.spacing_scaling + return value * self._spacing_scaling else: return value @@ -211,25 +221,23 @@ class CTkBaseClass(tkinter.Frame): font_list = list(font) for i in range(len(font_list)): if (type(font_list[i]) == int or type(font_list[i]) == float) and font_list[i] < 0: - font_list[i] = int(font_list[i] * self.widget_scaling) + font_list[i] = int(font_list[i] * self._widget_scaling) return tuple(font_list) elif type(font) == str: for negative_number in re.findall(r" -\d* ", font): - font = font.replace(negative_number, f" {int(int(negative_number) * self.widget_scaling)} ") + font = font.replace(negative_number, f" {int(int(negative_number) * self._widget_scaling)} ") return font elif isinstance(font, tkinter.font.Font): new_font_object = copy.copy(font) if font.cget("size") < 0: - new_font_object.config(size=int(font.cget("size") * self.widget_scaling)) + new_font_object.config(size=int(font.cget("size") * self._widget_scaling)) return new_font_object else: return font - def draw(self, no_color_updates=False): + def draw(self, no_color_updates: bool = False): """ abstract of draw method to be overridden """ pass - - diff --git a/customtkinter/windows/ctk_tk.py b/customtkinter/windows/ctk_tk.py index 7f2ab3f..b561751 100644 --- a/customtkinter/windows/ctk_tk.py +++ b/customtkinter/windows/ctk_tk.py @@ -68,7 +68,7 @@ class CTk(tkinter.Tk): if self.current_width != round(detected_width / self.window_scaling) or self.current_height != round(detected_height / self.window_scaling): self.current_width = round(detected_width / self.window_scaling) # adjust current size according to new size given by event - self.current_height = round(detected_height / self.window_scaling) # current_width and current_height are independent of the scale + self.current_height = round(detected_height / self.window_scaling) # _current_width and _current_height are independent of the scale def set_scaling(self, new_widget_scaling, new_spacing_scaling, new_window_scaling): self.window_scaling = new_window_scaling diff --git a/customtkinter/windows/ctk_toplevel.py b/customtkinter/windows/ctk_toplevel.py index a47c8e2..b8c853c 100644 --- a/customtkinter/windows/ctk_toplevel.py +++ b/customtkinter/windows/ctk_toplevel.py @@ -47,7 +47,7 @@ class CTkToplevel(tkinter.Toplevel): AppearanceModeTracker.add(self.set_appearance_mode, self) super().configure(bg=ThemeManager.single_color(self.fg_color, self.appearance_mode)) super().title("CTkToplevel") - # self.geometry(f"{self.current_width}x{self.current_height}") + # self.geometry(f"{self._current_width}x{self._current_height}") if sys.platform.startswith("win"): if self.appearance_mode == 1: @@ -63,7 +63,7 @@ class CTkToplevel(tkinter.Toplevel): if self.current_width != round(detected_width / self.window_scaling) or self.current_height != round(detected_height / self.window_scaling): self.current_width = round(detected_width / self.window_scaling) # adjust current size according to new size given by event - self.current_height = round(detected_height / self.window_scaling) # current_width and current_height are independent of the scale + self.current_height = round(detected_height / self.window_scaling) # _current_width and _current_height are independent of the scale def set_scaling(self, new_widget_scaling, new_spacing_scaling, new_window_scaling): self.window_scaling = new_window_scaling diff --git a/examples/simple_example.py b/examples/simple_example.py index cb59eb9..012eb6f 100644 --- a/examples/simple_example.py +++ b/examples/simple_example.py @@ -10,17 +10,13 @@ app.title("CustomTkinter simple_example.py") def button_function(): - print("Button click", label_1.text_label.cget("text")) + print("Button click") def slider_function(value): progressbar_1.set(value) -def check_box_function(): - print("checkbox_1:", checkbox_1.get()) - - y_padding = 13 frame_1 = customtkinter.CTkFrame(master=app) @@ -42,7 +38,15 @@ slider_1.set(0.5) entry_1 = customtkinter.CTkEntry(master=frame_1, placeholder_text="CTkEntry") entry_1.pack(pady=y_padding, padx=10) -checkbox_1 = customtkinter.CTkCheckBox(master=frame_1, command=check_box_function) +optionmenu_1 = customtkinter.CTkOptionMenu(frame_1, values=["Option 1", "Option 2", "Option 42"]) +optionmenu_1.pack(pady=10, padx=10) +optionmenu_1.set("CTkOptionMenu") + +combobox_1 = customtkinter.CTkComboBox(frame_1, values=["Option 1", "Option 2", "Option 42"]) +combobox_1.pack(pady=10, padx=10) +combobox_1.set("CTkComboBox") + +checkbox_1 = customtkinter.CTkCheckBox(master=frame_1) checkbox_1.pack(pady=y_padding, padx=10) radiobutton_var = tkinter.IntVar(value=1) diff --git a/test/manual_integration_tests/test_optionmenu.py b/test/manual_integration_tests/test_optionmenu.py index c5be41b..a3fc5f9 100644 --- a/test/manual_integration_tests/test_optionmenu.py +++ b/test/manual_integration_tests/test_optionmenu.py @@ -21,6 +21,5 @@ optionmenu_tk.pack(pady=10, padx=10) optionmenu_1 = customtkinter.CTkOptionMenu(app, variable=variable, values=countries, command=select_callback) optionmenu_1.pack(pady=10, padx=10) -optionmenu_1.set("te") app.mainloop() diff --git a/test/manual_integration_tests/test_widget_states.py b/test/manual_integration_tests/test_widget_states.py index 217e013..c921c52 100644 --- a/test/manual_integration_tests/test_widget_states.py +++ b/test/manual_integration_tests/test_widget_states.py @@ -40,8 +40,13 @@ button_4.pack(padx=20, pady=(10, 20)) radiobutton_1 = customtkinter.CTkRadioButton(master=app, text="radiobutton_1") radiobutton_1.pack(padx=20, pady=(20, 10)) -button_5 = customtkinter.CTkButton(master=app, text="Disable/Enable entry_1", command=lambda: change_state(radiobutton_1)) +button_5 = customtkinter.CTkButton(master=app, text="Disable/Enable radiobutton_1", command=lambda: change_state(radiobutton_1)) button_5.pack(padx=20, pady=(10, 20)) +optionmenu_1 = customtkinter.CTkOptionMenu(app, values=["test 1", "test 2"]) +optionmenu_1.pack(pady=10, padx=10) +button_6 = customtkinter.CTkButton(master=app, text="Disable/Enable optionmenu_1", command=lambda: change_state(optionmenu_1)) +button_6.pack(padx=20, pady=(10, 20)) + app.mainloop()