mirror of
https://github.com/TomSchimansky/CustomTkinter.git
synced 2023-08-10 21:13:13 +03:00
first main
This commit is contained in:
commit
d43a35e670
79
customtkinter/__init__.py
Normal file
79
customtkinter/__init__.py
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
from .customtkinter_button import CTkButton
|
||||||
|
from .customtkinter_slider import CTkSlider
|
||||||
|
from .customtkinter_frame import CTkFrame
|
||||||
|
from .customtkinter_progressbar import CTkProgressBar
|
||||||
|
from .customtkinter_label import CTkLabel
|
||||||
|
from .customtkinter_entry import CTkEntry
|
||||||
|
|
||||||
|
from .appearance_mode_tracker import AppearanceModeTracker, SystemAppearanceModeListenerNoThread
|
||||||
|
from .customtkinter_color_manager import CTkColorManager
|
||||||
|
|
||||||
|
from distutils.version import StrictVersion as Version
|
||||||
|
import tkinter
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
def enable_macos_darkmode():
|
||||||
|
if sys.platform == "darwin": # macOS
|
||||||
|
if Version(tkinter.Tcl().call("info", "patchlevel")) >= Version("8.6.9"): # Tcl/Tk >= 8.6.9
|
||||||
|
os.system("defaults write -g NSRequiresAquaSystemAppearance -bool No")
|
||||||
|
|
||||||
|
sys.stderr.write("WARNING (customtkinter.enable_macos_darkmode): " +
|
||||||
|
"This command forces macOS dark-mode on all programs." +
|
||||||
|
"This can cause bugs on some other programs.\n" +
|
||||||
|
"Disable it by calling customtkinter.disable_macos_darkmode at the end of the program.\n")
|
||||||
|
else:
|
||||||
|
sys.stderr.write("WARNING (customtkinter.enable_macos_darkmode): " +
|
||||||
|
"Currently this works only with anaconda python version (Tcl/Tk >= 8.6.9).\n" +
|
||||||
|
"(python.org Tcl/Tk version is only 8.6.8)\n")
|
||||||
|
else:
|
||||||
|
sys.stderr.write("WARNING (customtkinter.enable_macos_darkmode): " +
|
||||||
|
"System is not macOS, but the following: {}\n".format(sys.platform))
|
||||||
|
|
||||||
|
|
||||||
|
def disable_macos_darkmode():
|
||||||
|
if sys.platform == "darwin": # macOS
|
||||||
|
if Version(tkinter.Tcl().call("info", "patchlevel")) >= Version("8.6.9"): # Tcl/Tk >= 8.6.9
|
||||||
|
os.system("defaults delete -g NSRequiresAquaSystemAppearance")
|
||||||
|
# This command reverts the dark-mode setting for all programs.
|
||||||
|
|
||||||
|
|
||||||
|
def set_appearance_mode(mode_string):
|
||||||
|
AppearanceModeTracker.set_appearance_mode(mode_string)
|
||||||
|
|
||||||
|
|
||||||
|
def get_appearance_mode():
|
||||||
|
if AppearanceModeTracker.appearance_mode == 0:
|
||||||
|
return "Light"
|
||||||
|
elif AppearanceModeTracker.appearance_mode == 1:
|
||||||
|
return "Dark"
|
||||||
|
|
||||||
|
|
||||||
|
def set_theme(main_color):
|
||||||
|
CTkColorManager.set_theme(main_color)
|
||||||
|
|
||||||
|
|
||||||
|
def deactivate_threading():
|
||||||
|
AppearanceModeTracker.init_listener_function(no_thread=True)
|
||||||
|
sys.stderr.write("WARNING (customtkinter.deactivate_threading): Automatic threaded search for a change of the " +
|
||||||
|
"system appearance mode is deativated now.\nYou have to update the appearance mode manually " +
|
||||||
|
"in your mainloop by calling customtkinter.update_theme() every time.\n")
|
||||||
|
|
||||||
|
|
||||||
|
def activate_threading():
|
||||||
|
AppearanceModeTracker.init_listener_function()
|
||||||
|
|
||||||
|
|
||||||
|
def update_theme():
|
||||||
|
if isinstance(AppearanceModeTracker.system_mode_listener, SystemAppearanceModeListenerNoThread):
|
||||||
|
AppearanceModeTracker.system_mode_listener.update()
|
||||||
|
else:
|
||||||
|
sys.stderr.write("WARNING (customtkinter.update_theme): no need to call update_theme, because " +
|
||||||
|
"customtkinter is constantly searching for a mode change in a background thread.\n")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
144
customtkinter/appearance_mode_tracker.py
Normal file
144
customtkinter/appearance_mode_tracker.py
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
from threading import Thread
|
||||||
|
from time import sleep
|
||||||
|
import darkdetect
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
class SystemAppearanceModeListener(Thread):
|
||||||
|
""" This class checks for a system appearance change
|
||||||
|
in a loop, and if a change is detected, than the
|
||||||
|
callback function gets called. Either 'Light' or
|
||||||
|
'Dark' is passed in the callback function. """
|
||||||
|
|
||||||
|
def __init__(self, callback, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.setDaemon(True)
|
||||||
|
|
||||||
|
self.appearance_mode = self.detect_appearance_mode()
|
||||||
|
self.callback_function = callback
|
||||||
|
|
||||||
|
self.activated = True
|
||||||
|
|
||||||
|
def activate(self):
|
||||||
|
self.activated = True
|
||||||
|
|
||||||
|
def deactivate(self):
|
||||||
|
self.activated = False
|
||||||
|
|
||||||
|
def get_mode(self):
|
||||||
|
return self.appearance_mode
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def detect_appearance_mode():
|
||||||
|
if sys.platform == "darwin":
|
||||||
|
if darkdetect.theme() == "Dark":
|
||||||
|
return 1 # Dark
|
||||||
|
else:
|
||||||
|
return 0 # Light
|
||||||
|
else:
|
||||||
|
return 0 # Light
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
while True:
|
||||||
|
if self.activated:
|
||||||
|
detected_mode = self.detect_appearance_mode()
|
||||||
|
if detected_mode != self.appearance_mode:
|
||||||
|
self.appearance_mode = detected_mode
|
||||||
|
|
||||||
|
if self.appearance_mode == 0:
|
||||||
|
self.callback_function("Light", from_listener=True)
|
||||||
|
else:
|
||||||
|
self.callback_function("Dark", from_listener=True)
|
||||||
|
sleep(0.5)
|
||||||
|
|
||||||
|
|
||||||
|
class SystemAppearanceModeListenerNoThread():
|
||||||
|
def __init__(self, callback):
|
||||||
|
self.appearance_mode = self.detect_appearance_mode()
|
||||||
|
self.callback_function = callback
|
||||||
|
|
||||||
|
self.activated = True
|
||||||
|
|
||||||
|
def get_mode(self):
|
||||||
|
return self.appearance_mode
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def detect_appearance_mode():
|
||||||
|
if sys.platform == "darwin":
|
||||||
|
if darkdetect.theme() == "Dark":
|
||||||
|
return 1 # Dark
|
||||||
|
else:
|
||||||
|
return 0 # Light
|
||||||
|
else:
|
||||||
|
return 0 # Light
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
detected_mode = self.detect_appearance_mode()
|
||||||
|
if detected_mode != self.appearance_mode:
|
||||||
|
self.appearance_mode = detected_mode
|
||||||
|
|
||||||
|
if self.appearance_mode == 0:
|
||||||
|
self.callback_function("Light", from_listener=True)
|
||||||
|
else:
|
||||||
|
self.callback_function("Dark", from_listener=True)
|
||||||
|
|
||||||
|
|
||||||
|
class AppearanceModeTracker():
|
||||||
|
""" This class holds a list with callback functions
|
||||||
|
of every customtkinter object that gets created.
|
||||||
|
And when either the SystemAppearanceModeListener
|
||||||
|
or the user changes the appearance_mode, all
|
||||||
|
callbacks in the list get called and the
|
||||||
|
new appearance_mode is passed over to the
|
||||||
|
customtkinter objects """
|
||||||
|
|
||||||
|
callback_list = []
|
||||||
|
appearance_mode = 0 # Light (standard)
|
||||||
|
system_mode_listener = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def init_listener_function(cls, no_thread=False):
|
||||||
|
if isinstance(cls.system_mode_listener, SystemAppearanceModeListener):
|
||||||
|
cls.system_mode_listener.deactivate()
|
||||||
|
|
||||||
|
if no_thread is True:
|
||||||
|
cls.system_mode_listener = SystemAppearanceModeListenerNoThread(cls.set_appearance_mode)
|
||||||
|
cls.appearance_mode = cls.system_mode_listener.get_mode()
|
||||||
|
else:
|
||||||
|
cls.system_mode_listener = SystemAppearanceModeListener(cls.set_appearance_mode)
|
||||||
|
cls.system_mode_listener.start()
|
||||||
|
cls.appearance_mode = cls.system_mode_listener.get_mode()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add(cls, callback):
|
||||||
|
cls.callback_list.append(callback)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_mode(cls):
|
||||||
|
return cls.appearance_mode
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def set_appearance_mode(cls, mode_string, from_listener=False):
|
||||||
|
if mode_string.lower() == "dark":
|
||||||
|
cls.appearance_mode = 1
|
||||||
|
|
||||||
|
if not from_listener:
|
||||||
|
cls.system_mode_listener.deactivate()
|
||||||
|
|
||||||
|
elif mode_string.lower() == "light":
|
||||||
|
cls.appearance_mode = 0
|
||||||
|
if not from_listener:
|
||||||
|
cls.system_mode_listener.deactivate()
|
||||||
|
|
||||||
|
elif mode_string.lower() == "system":
|
||||||
|
cls.system_mode_listener.activate()
|
||||||
|
|
||||||
|
if cls.appearance_mode == 0:
|
||||||
|
for callback in cls.callback_list:
|
||||||
|
callback("Light")
|
||||||
|
elif cls.appearance_mode == 1:
|
||||||
|
for callback in cls.callback_list:
|
||||||
|
callback("Dark")
|
||||||
|
|
||||||
|
|
||||||
|
AppearanceModeTracker.init_listener_function()
|
239
customtkinter/customtkinter_button.py
Normal file
239
customtkinter/customtkinter_button.py
Normal file
@ -0,0 +1,239 @@
|
|||||||
|
import tkinter
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from .customtkinter_frame import CTkFrame
|
||||||
|
from .appearance_mode_tracker import AppearanceModeTracker
|
||||||
|
from .customtkinter_color_manager import CTkColorManager
|
||||||
|
|
||||||
|
|
||||||
|
class CTkButton(tkinter.Frame):
|
||||||
|
""" tkinter custom button with border, rounded corners and hover effect """
|
||||||
|
|
||||||
|
def __init__(self,
|
||||||
|
bg_color=None,
|
||||||
|
fg_color=CTkColorManager.MAIN,
|
||||||
|
hover_color=CTkColorManager.MAIN_HOVER,
|
||||||
|
border_color=None,
|
||||||
|
border_width=0,
|
||||||
|
command=None,
|
||||||
|
width=120,
|
||||||
|
height=32,
|
||||||
|
corner_radius=8,
|
||||||
|
text_font=None,
|
||||||
|
text_color=CTkColorManager.TEXT,
|
||||||
|
text="CTkButton",
|
||||||
|
hover=True,
|
||||||
|
*args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
if bg_color is None:
|
||||||
|
if isinstance(self.master, CTkFrame):
|
||||||
|
self.bg_color = self.master.fg_color
|
||||||
|
else:
|
||||||
|
self.bg_color = self.master.cget("bg")
|
||||||
|
else:
|
||||||
|
self.bg_color = bg_color
|
||||||
|
|
||||||
|
AppearanceModeTracker.add(self.set_appearance_mode)
|
||||||
|
|
||||||
|
self.fg_color = fg_color
|
||||||
|
self.hover_color = hover_color
|
||||||
|
self.border_color = border_color
|
||||||
|
self.appearance_mode = AppearanceModeTracker.get_mode() # 0: "Light" 1: "Dark"
|
||||||
|
|
||||||
|
self.width = width
|
||||||
|
self.height = height
|
||||||
|
|
||||||
|
if corner_radius*2 > self.height:
|
||||||
|
self.corner_radius = self.height/2
|
||||||
|
elif corner_radius*2 > self.width:
|
||||||
|
self.corner_radius = self.width/2
|
||||||
|
else:
|
||||||
|
self.corner_radius = corner_radius
|
||||||
|
|
||||||
|
self.border_width = border_width
|
||||||
|
|
||||||
|
if self.corner_radius >= self.border_width:
|
||||||
|
self.inner_corner_radius = self.corner_radius - self.border_width
|
||||||
|
else:
|
||||||
|
self.inner_corner_radius = 0
|
||||||
|
|
||||||
|
self.text = text
|
||||||
|
self.text_color = text_color
|
||||||
|
if text_font is None:
|
||||||
|
if sys.platform == "darwin": # macOS
|
||||||
|
self.text_font = ("Avenir", 13)
|
||||||
|
elif "win" in sys.platform: # Windows
|
||||||
|
self.text_font = ("Century Gothic", 11)
|
||||||
|
else:
|
||||||
|
self.text_font = ("TkDefaultFont")
|
||||||
|
else:
|
||||||
|
self.text_font = text_font
|
||||||
|
|
||||||
|
self.function = command
|
||||||
|
self.hover = hover
|
||||||
|
|
||||||
|
self.configure(width=self.width, height=self.height)
|
||||||
|
|
||||||
|
if sys.platform == "darwin" and self.function is not None:
|
||||||
|
self.configure(cursor="pointinghand")
|
||||||
|
|
||||||
|
self.canvas = tkinter.Canvas(master=self,
|
||||||
|
highlightthicknes=0,
|
||||||
|
width=self.width,
|
||||||
|
height=self.height)
|
||||||
|
self.canvas.place(x=0, y=0)
|
||||||
|
|
||||||
|
if self.hover is True:
|
||||||
|
self.canvas.bind("<Enter>", self.on_enter)
|
||||||
|
self.canvas.bind("<Leave>", self.on_leave)
|
||||||
|
|
||||||
|
self.canvas.bind("<Button-1>", self.clicked)
|
||||||
|
self.canvas.bind("<Button-1>", self.clicked)
|
||||||
|
|
||||||
|
self.canvas_fg_parts = []
|
||||||
|
self.canvas_border_parts = []
|
||||||
|
self.text_part = None
|
||||||
|
|
||||||
|
self.draw()
|
||||||
|
|
||||||
|
def draw(self):
|
||||||
|
self.canvas.delete("all")
|
||||||
|
self.canvas_fg_parts = []
|
||||||
|
self.canvas_border_parts = []
|
||||||
|
|
||||||
|
if type(self.bg_color) == tuple and len(self.bg_color) == 2:
|
||||||
|
self.canvas.configure(bg=self.bg_color[self.appearance_mode])
|
||||||
|
else:
|
||||||
|
self.canvas.configure(bg=self.bg_color)
|
||||||
|
|
||||||
|
# border button parts
|
||||||
|
if self.border_width > 0:
|
||||||
|
|
||||||
|
if self.corner_radius > 0:
|
||||||
|
self.canvas_border_parts.append(self.canvas.create_oval(0,
|
||||||
|
0,
|
||||||
|
self.corner_radius * 2,
|
||||||
|
self.corner_radius * 2))
|
||||||
|
self.canvas_border_parts.append(self.canvas.create_oval(self.width - self.corner_radius * 2,
|
||||||
|
0,
|
||||||
|
self.width,
|
||||||
|
self.corner_radius * 2))
|
||||||
|
self.canvas_border_parts.append(self.canvas.create_oval(0,
|
||||||
|
self.height - self.corner_radius * 2,
|
||||||
|
self.corner_radius * 2,
|
||||||
|
self.height))
|
||||||
|
self.canvas_border_parts.append(self.canvas.create_oval(self.width - self.corner_radius * 2,
|
||||||
|
self.height - self.corner_radius * 2,
|
||||||
|
self.width,
|
||||||
|
self.height))
|
||||||
|
|
||||||
|
self.canvas_border_parts.append(self.canvas.create_rectangle(0,
|
||||||
|
self.corner_radius,
|
||||||
|
self.width,
|
||||||
|
self.height - self.corner_radius))
|
||||||
|
self.canvas_border_parts.append(self.canvas.create_rectangle(self.corner_radius,
|
||||||
|
0,
|
||||||
|
self.width - self.corner_radius,
|
||||||
|
self.height))
|
||||||
|
|
||||||
|
# inner button parts
|
||||||
|
|
||||||
|
if self.corner_radius > 0:
|
||||||
|
self.canvas_fg_parts.append(self.canvas.create_oval(self.border_width,
|
||||||
|
self.border_width,
|
||||||
|
self.border_width + self.inner_corner_radius * 2,
|
||||||
|
self.border_width + self.inner_corner_radius * 2))
|
||||||
|
self.canvas_fg_parts.append(self.canvas.create_oval(self.width - self.border_width - self.inner_corner_radius * 2,
|
||||||
|
self.border_width,
|
||||||
|
self.width - self.border_width,
|
||||||
|
self.border_width + self.inner_corner_radius * 2))
|
||||||
|
self.canvas_fg_parts.append(self.canvas.create_oval(self.border_width,
|
||||||
|
self.height - self.border_width - self.inner_corner_radius * 2,
|
||||||
|
self.border_width + self.inner_corner_radius * 2,
|
||||||
|
self.height-self.border_width))
|
||||||
|
self.canvas_fg_parts.append(self.canvas.create_oval(self.width - self.border_width - self.inner_corner_radius * 2,
|
||||||
|
self.height - self.border_width - self.inner_corner_radius * 2,
|
||||||
|
self.width - self.border_width,
|
||||||
|
self.height - self.border_width))
|
||||||
|
|
||||||
|
self.canvas_fg_parts.append(self.canvas.create_rectangle(self.border_width + self.inner_corner_radius,
|
||||||
|
self.border_width,
|
||||||
|
self.width - self.border_width - self.inner_corner_radius,
|
||||||
|
self.height - self.border_width))
|
||||||
|
self.canvas_fg_parts.append(self.canvas.create_rectangle(self.border_width,
|
||||||
|
self.border_width + self.inner_corner_radius,
|
||||||
|
self.width - self.border_width,
|
||||||
|
self.height - self.inner_corner_radius - self.border_width))
|
||||||
|
|
||||||
|
for part in self.canvas_fg_parts:
|
||||||
|
if type(self.fg_color) == tuple and len(self.hover_color) == 2:
|
||||||
|
self.canvas.itemconfig(part, fill=self.fg_color[self.appearance_mode], width=0)
|
||||||
|
else:
|
||||||
|
self.canvas.itemconfig(part, fill=self.fg_color, outline=self.fg_color, width=0)
|
||||||
|
|
||||||
|
for part in self.canvas_border_parts:
|
||||||
|
if type(self.border_color) == tuple and len(self.hover_color) == 2:
|
||||||
|
self.canvas.itemconfig(part, fill=self.border_color[self.appearance_mode], width=0)
|
||||||
|
else:
|
||||||
|
self.canvas.itemconfig(part, fill=self.border_color, outline=self.border_color, width=0)
|
||||||
|
|
||||||
|
self.text_part = self.canvas.create_text(self.width / 2,
|
||||||
|
self.height / 2,
|
||||||
|
text=self.text,
|
||||||
|
font=self.text_font)
|
||||||
|
|
||||||
|
if type(self.text_color) == tuple and len(self.text_color) == 2:
|
||||||
|
self.canvas.itemconfig(self.text_part, fill=self.text_color[self.appearance_mode])
|
||||||
|
else:
|
||||||
|
self.canvas.itemconfig(self.text_part, fill=self.text_color)
|
||||||
|
|
||||||
|
self.set_text(self.text)
|
||||||
|
|
||||||
|
def configure_color(self, bg_color=None, fg_color=None, hover_color=None, text_color=None):
|
||||||
|
if bg_color is not None:
|
||||||
|
self.bg_color = bg_color
|
||||||
|
else:
|
||||||
|
self.bg_color = self.master.cget("bg")
|
||||||
|
|
||||||
|
if fg_color is not None:
|
||||||
|
self.fg_color = fg_color
|
||||||
|
|
||||||
|
if hover_color is not None:
|
||||||
|
self.hover_color = hover_color
|
||||||
|
|
||||||
|
if text_color is not None:
|
||||||
|
self.text_color = text_color
|
||||||
|
|
||||||
|
self.draw()
|
||||||
|
|
||||||
|
def set_text(self, text):
|
||||||
|
self.canvas.itemconfig(self.text_part, text=text)
|
||||||
|
|
||||||
|
def on_enter(self, event=0):
|
||||||
|
for part in self.canvas_fg_parts:
|
||||||
|
if type(self.hover_color) == tuple and len(self.hover_color) == 2:
|
||||||
|
self.canvas.itemconfig(part, fill=self.hover_color[self.appearance_mode], width=0)
|
||||||
|
else:
|
||||||
|
self.canvas.itemconfig(part, fill=self.hover_color, width=0)
|
||||||
|
|
||||||
|
def on_leave(self, event=0):
|
||||||
|
for part in self.canvas_fg_parts:
|
||||||
|
if type(self.fg_color) == tuple and len(self.hover_color) == 2:
|
||||||
|
self.canvas.itemconfig(part, fill=self.fg_color[self.appearance_mode], width=0)
|
||||||
|
else:
|
||||||
|
self.canvas.itemconfig(part, fill=self.fg_color, width=0)
|
||||||
|
|
||||||
|
def clicked(self, event=0):
|
||||||
|
if self.function is not None:
|
||||||
|
self.function()
|
||||||
|
self.on_leave()
|
||||||
|
|
||||||
|
def set_appearance_mode(self, mode_string):
|
||||||
|
if mode_string.lower() == "dark":
|
||||||
|
self.appearance_mode = 1
|
||||||
|
elif mode_string.lower() == "light":
|
||||||
|
self.appearance_mode = 0
|
||||||
|
|
||||||
|
self.draw()
|
||||||
|
|
29
customtkinter/customtkinter_color_manager.py
Normal file
29
customtkinter/customtkinter_color_manager.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
|
||||||
|
|
||||||
|
class CTkColorManager:
|
||||||
|
MAIN = ("#1C94CF", "#1C94CF")
|
||||||
|
MAIN_HOVER = ("#5FB4DD", "#5FB4DD")
|
||||||
|
ENTRY = ("white", "#222222")
|
||||||
|
TEXT = ("black", "white")
|
||||||
|
SLIDER_BG = ("#6B6B6B", "#222222")
|
||||||
|
PROGRESS_BG = ("#6B6B6B", "#222222")
|
||||||
|
FRAME = ("#D4D5D6", "#3F3F3F")
|
||||||
|
FRAME_2 = ("#505050", "#505050")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def set_theme_color(cls, hex_color, hex_color_hover):
|
||||||
|
cls.MAIN = (hex_color, hex_color)
|
||||||
|
cls.MAIN_HOVER = (hex_color_hover, hex_color_hover)
|
||||||
|
|
||||||
|
print(CTkColorManager.MAIN, id(CTkColorManager))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def set_theme(cls, main_color):
|
||||||
|
if main_color.lower() == "green":
|
||||||
|
cls.set_theme_color("#2EDEA4", "#82FCD4")
|
||||||
|
|
||||||
|
elif main_color.lower() == "blue":
|
||||||
|
cls.set_theme_color("#1C94CF", "#5FB4DD")
|
||||||
|
|
||||||
|
elif main_color.lower() == "blue":
|
||||||
|
cls.set_theme_color("#1C94CF", "#5FB4DD")
|
113
customtkinter/customtkinter_entry.py
Normal file
113
customtkinter/customtkinter_entry.py
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
import tkinter
|
||||||
|
|
||||||
|
from .customtkinter_frame import CTkFrame
|
||||||
|
from .appearance_mode_tracker import AppearanceModeTracker
|
||||||
|
from .customtkinter_color_manager import CTkColorManager
|
||||||
|
|
||||||
|
|
||||||
|
class CTkEntry(tkinter.Frame):
|
||||||
|
def __init__(self,
|
||||||
|
master=None,
|
||||||
|
bg_color=None,
|
||||||
|
fg_color=CTkColorManager.ENTRY,
|
||||||
|
text_color=CTkColorManager.TEXT,
|
||||||
|
corner_radius=10,
|
||||||
|
width=120,
|
||||||
|
height=25,
|
||||||
|
*args,
|
||||||
|
**kwargs):
|
||||||
|
super().__init__(master=master)
|
||||||
|
|
||||||
|
AppearanceModeTracker.add(self.change_appearance_mode)
|
||||||
|
|
||||||
|
if bg_color is None:
|
||||||
|
if isinstance(self.master, CTkFrame):
|
||||||
|
self.bg_color = self.master.fg_color
|
||||||
|
else:
|
||||||
|
self.bg_color = self.master.cget("bg")
|
||||||
|
else:
|
||||||
|
self.bg_color = bg_color
|
||||||
|
|
||||||
|
self.fg_color = fg_color
|
||||||
|
self.text_color = text_color
|
||||||
|
self.appearance_mode = AppearanceModeTracker.get_mode() # 0: "Light" 1: "Dark"
|
||||||
|
|
||||||
|
self.width = width
|
||||||
|
self.height = height
|
||||||
|
self.corner_radius = corner_radius
|
||||||
|
|
||||||
|
self.configure(width=self.width, height=self.height)
|
||||||
|
|
||||||
|
self.canvas = tkinter.Canvas(master=self,
|
||||||
|
highlightthicknes=0,
|
||||||
|
width=self.width,
|
||||||
|
height=self.height)
|
||||||
|
self.canvas.place(x=0, y=0)
|
||||||
|
|
||||||
|
self.entry = tkinter.Entry(master=self,
|
||||||
|
bd=0,
|
||||||
|
highlightthicknes=0,
|
||||||
|
*args, **kwargs)
|
||||||
|
self.entry.place(relx=0.5, rely=0.5, relwidth=0.8, anchor=tkinter.CENTER)
|
||||||
|
|
||||||
|
self.fg_parts = []
|
||||||
|
|
||||||
|
self.draw()
|
||||||
|
|
||||||
|
def draw(self):
|
||||||
|
self.canvas.delete("all")
|
||||||
|
self.fg_parts = []
|
||||||
|
|
||||||
|
# frame_border
|
||||||
|
self.fg_parts.append(self.canvas.create_oval(0, 0,
|
||||||
|
self.corner_radius*2, self.corner_radius*2))
|
||||||
|
self.fg_parts.append(self.canvas.create_oval(self.width-self.corner_radius*2, 0,
|
||||||
|
self.width, self.corner_radius*2))
|
||||||
|
self.fg_parts.append(self.canvas.create_oval(0, self.height-self.corner_radius*2,
|
||||||
|
self.corner_radius*2, self.height))
|
||||||
|
self.fg_parts.append(self.canvas.create_oval(self.width-self.corner_radius*2, self.height-self.corner_radius*2,
|
||||||
|
self.width, self.height))
|
||||||
|
|
||||||
|
self.fg_parts.append(self.canvas.create_rectangle(0, self.corner_radius,
|
||||||
|
self.width, self.height-self.corner_radius))
|
||||||
|
self.fg_parts.append(self.canvas.create_rectangle(self.corner_radius, 0,
|
||||||
|
self.width-self.corner_radius, self.height))
|
||||||
|
|
||||||
|
for part in self.fg_parts:
|
||||||
|
if type(self.fg_color) == tuple:
|
||||||
|
self.canvas.itemconfig(part, fill=self.fg_color[self.appearance_mode], width=0)
|
||||||
|
else:
|
||||||
|
self.canvas.itemconfig(part, fill=self.fg_color, width=0)
|
||||||
|
|
||||||
|
if type(self.bg_color) == tuple:
|
||||||
|
self.canvas.configure(bg=self.bg_color[self.appearance_mode])
|
||||||
|
else:
|
||||||
|
self.canvas.configure(bg=self.bg_color)
|
||||||
|
|
||||||
|
if type(self.fg_color) == tuple:
|
||||||
|
self.entry.configure(bg=self.fg_color[self.appearance_mode],
|
||||||
|
highlightcolor=self.fg_color[self.appearance_mode])
|
||||||
|
else:
|
||||||
|
self.entry.configure(bg=self.fg_color,
|
||||||
|
highlightcolor=self.fg_color)
|
||||||
|
|
||||||
|
if type(self.text_color) == tuple:
|
||||||
|
self.entry.configure(fg=self.text_color[self.appearance_mode],
|
||||||
|
insertbackground=self.text_color[self.appearance_mode])
|
||||||
|
else:
|
||||||
|
self.entry.configure(fg=self.text_color,
|
||||||
|
insertbackground=self.text_color)
|
||||||
|
|
||||||
|
def change_appearance_mode(self, mode_string):
|
||||||
|
if mode_string.lower() == "dark":
|
||||||
|
self.appearance_mode = 1
|
||||||
|
elif mode_string.lower() == "light":
|
||||||
|
self.appearance_mode = 0
|
||||||
|
|
||||||
|
if isinstance(self.master, CTkFrame):
|
||||||
|
self.bg_color = self.master.fg_color
|
||||||
|
else:
|
||||||
|
self.bg_color = self.master.cget("bg")
|
||||||
|
|
||||||
|
self.draw()
|
||||||
|
|
109
customtkinter/customtkinter_frame.py
Normal file
109
customtkinter/customtkinter_frame.py
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
import tkinter
|
||||||
|
|
||||||
|
from .appearance_mode_tracker import AppearanceModeTracker
|
||||||
|
from .customtkinter_color_manager import CTkColorManager
|
||||||
|
|
||||||
|
|
||||||
|
class CTkFrame(tkinter.Frame):
|
||||||
|
def __init__(self, *args,
|
||||||
|
bg_color=None,
|
||||||
|
fg_color=None,
|
||||||
|
corner_radius=10,
|
||||||
|
width=50,
|
||||||
|
height=20,
|
||||||
|
**kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
AppearanceModeTracker.add(self.change_appearance_mode)
|
||||||
|
|
||||||
|
if bg_color is None:
|
||||||
|
if isinstance(self.master, CTkFrame):
|
||||||
|
self.bg_color = self.master.fg_color
|
||||||
|
else:
|
||||||
|
self.bg_color = self.master.cget("bg")
|
||||||
|
else:
|
||||||
|
self.bg_color = bg_color
|
||||||
|
|
||||||
|
if fg_color is None:
|
||||||
|
if isinstance(self.master, CTkFrame):
|
||||||
|
if self.master.fg_color == CTkColorManager.FRAME:
|
||||||
|
self.fg_color = CTkColorManager.FRAME_2
|
||||||
|
else:
|
||||||
|
self.fg_color = CTkColorManager.FRAME
|
||||||
|
else:
|
||||||
|
self.fg_color = CTkColorManager.FRAME
|
||||||
|
else:
|
||||||
|
self.fg_color = fg_color
|
||||||
|
|
||||||
|
self.appearance_mode = AppearanceModeTracker.get_mode() # 0: "Light" 1: "Dark"
|
||||||
|
|
||||||
|
self.width = width
|
||||||
|
self.height = height
|
||||||
|
self.corner_radius = corner_radius
|
||||||
|
|
||||||
|
self.configure(width=self.width, height=self.height)
|
||||||
|
|
||||||
|
self.canvas = tkinter.Canvas(master=self,
|
||||||
|
highlightthicknes=0,
|
||||||
|
width=self.width,
|
||||||
|
height=self.height)
|
||||||
|
|
||||||
|
if type(self.bg_color) == tuple:
|
||||||
|
self.canvas.configure(bg=self.bg_color[self.appearance_mode])
|
||||||
|
else:
|
||||||
|
self.canvas.configure(bg=self.bg_color)
|
||||||
|
|
||||||
|
self.canvas.place(x=0, y=0)
|
||||||
|
|
||||||
|
self.fg_parts = []
|
||||||
|
|
||||||
|
self.draw()
|
||||||
|
|
||||||
|
def draw(self):
|
||||||
|
#self.canvas.delete("all")
|
||||||
|
for part in self.fg_parts:
|
||||||
|
self.canvas.delete(part)
|
||||||
|
self.fg_parts = []
|
||||||
|
|
||||||
|
# frame_border
|
||||||
|
self.fg_parts.append(self.canvas.create_oval(0, 0,
|
||||||
|
self.corner_radius*2, self.corner_radius*2))
|
||||||
|
self.fg_parts.append(self.canvas.create_oval(self.width-self.corner_radius*2, 0,
|
||||||
|
self.width, self.corner_radius*2))
|
||||||
|
self.fg_parts.append(self.canvas.create_oval(0, self.height-self.corner_radius*2,
|
||||||
|
self.corner_radius*2, self.height))
|
||||||
|
self.fg_parts.append(self.canvas.create_oval(self.width-self.corner_radius*2, self.height-self.corner_radius*2,
|
||||||
|
self.width, self.height))
|
||||||
|
|
||||||
|
self.fg_parts.append(self.canvas.create_rectangle(0, self.corner_radius,
|
||||||
|
self.width, self.height-self.corner_radius))
|
||||||
|
self.fg_parts.append(self.canvas.create_rectangle(self.corner_radius, 0,
|
||||||
|
self.width-self.corner_radius, self.height))
|
||||||
|
|
||||||
|
for part in self.fg_parts:
|
||||||
|
if type(self.fg_color) == tuple:
|
||||||
|
self.canvas.itemconfig(part, fill=self.fg_color[self.appearance_mode], width=0)
|
||||||
|
else:
|
||||||
|
self.canvas.itemconfig(part, fill=self.fg_color, width=0)
|
||||||
|
|
||||||
|
if type(self.bg_color) == tuple:
|
||||||
|
self.canvas.configure(bg=self.bg_color[self.appearance_mode])
|
||||||
|
else:
|
||||||
|
self.canvas.configure(bg=self.bg_color)
|
||||||
|
|
||||||
|
for part in self.fg_parts:
|
||||||
|
self.canvas.tag_lower(part)
|
||||||
|
|
||||||
|
def change_appearance_mode(self, mode_string):
|
||||||
|
if mode_string.lower() == "dark":
|
||||||
|
self.appearance_mode = 1
|
||||||
|
elif mode_string.lower() == "light":
|
||||||
|
self.appearance_mode = 0
|
||||||
|
|
||||||
|
if isinstance(self.master, CTkFrame):
|
||||||
|
self.bg_color = self.master.fg_color
|
||||||
|
else:
|
||||||
|
self.bg_color = self.master.cget("bg")
|
||||||
|
|
||||||
|
self.draw()
|
||||||
|
|
141
customtkinter/customtkinter_label.py
Normal file
141
customtkinter/customtkinter_label.py
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
import tkinter
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from .customtkinter_frame import CTkFrame
|
||||||
|
from .appearance_mode_tracker import AppearanceModeTracker
|
||||||
|
from .customtkinter_color_manager import CTkColorManager
|
||||||
|
|
||||||
|
|
||||||
|
class CTkLabel(tkinter.Frame):
|
||||||
|
def __init__(self,
|
||||||
|
master=None,
|
||||||
|
bg_color=None,
|
||||||
|
fg_color=None,
|
||||||
|
text_color=CTkColorManager.TEXT,
|
||||||
|
corner_radius=8,
|
||||||
|
width=120,
|
||||||
|
height=25,
|
||||||
|
text="CTkLabel",
|
||||||
|
text_font=None,
|
||||||
|
*args,
|
||||||
|
**kwargs):
|
||||||
|
super().__init__(master=master)
|
||||||
|
|
||||||
|
AppearanceModeTracker.add(self.change_appearance_mode)
|
||||||
|
|
||||||
|
if bg_color is None:
|
||||||
|
if isinstance(self.master, CTkFrame):
|
||||||
|
self.bg_color = self.master.fg_color
|
||||||
|
else:
|
||||||
|
self.bg_color = self.master.cget("bg")
|
||||||
|
else:
|
||||||
|
self.bg_color = bg_color
|
||||||
|
|
||||||
|
if fg_color is None:
|
||||||
|
self.fg_color = self.bg_color
|
||||||
|
else:
|
||||||
|
self.fg_color = fg_color
|
||||||
|
self.text_color = text_color
|
||||||
|
self.appearance_mode = AppearanceModeTracker.get_mode() # 0: "Light" 1: "Dark"
|
||||||
|
|
||||||
|
self.width = width
|
||||||
|
self.height = height
|
||||||
|
self.corner_radius = corner_radius
|
||||||
|
self.text = text
|
||||||
|
|
||||||
|
if text_font is None:
|
||||||
|
if sys.platform == "darwin": # macOS
|
||||||
|
self.text_font = ("Avenir", 13)
|
||||||
|
elif "win" in sys.platform: # Windows
|
||||||
|
self.text_font = ("Century Gothic", 11)
|
||||||
|
else:
|
||||||
|
self.text_font = ("TkDefaultFont", 11)
|
||||||
|
else:
|
||||||
|
self.text_font = text_font
|
||||||
|
self.configure(width=self.width, height=self.height)
|
||||||
|
|
||||||
|
self.canvas = tkinter.Canvas(master=self,
|
||||||
|
highlightthicknes=0,
|
||||||
|
width=self.width,
|
||||||
|
height=self.height)
|
||||||
|
self.canvas.place(relx=0, rely=0, anchor=tkinter.NW)
|
||||||
|
|
||||||
|
self.text_label = tkinter.Label(master=self,
|
||||||
|
highlightthicknes=0,
|
||||||
|
bd=0,
|
||||||
|
text=self.text,
|
||||||
|
font=self.text_font,
|
||||||
|
*args, **kwargs)
|
||||||
|
self.text_label.place(relx=0.5, rely=0.5, anchor=tkinter.CENTER)
|
||||||
|
|
||||||
|
self.fg_parts = []
|
||||||
|
|
||||||
|
self.draw()
|
||||||
|
|
||||||
|
def draw(self):
|
||||||
|
self.canvas.delete("all")
|
||||||
|
self.fg_parts = []
|
||||||
|
|
||||||
|
# frame_border
|
||||||
|
self.fg_parts.append(self.canvas.create_oval(0, 0,
|
||||||
|
self.corner_radius*2, self.corner_radius*2))
|
||||||
|
self.fg_parts.append(self.canvas.create_oval(self.width-self.corner_radius*2, 0,
|
||||||
|
self.width, self.corner_radius*2))
|
||||||
|
self.fg_parts.append(self.canvas.create_oval(0, self.height-self.corner_radius*2,
|
||||||
|
self.corner_radius*2, self.height))
|
||||||
|
self.fg_parts.append(self.canvas.create_oval(self.width-self.corner_radius*2, self.height-self.corner_radius*2,
|
||||||
|
self.width, self.height))
|
||||||
|
|
||||||
|
self.fg_parts.append(self.canvas.create_rectangle(0, self.corner_radius,
|
||||||
|
self.width, self.height-self.corner_radius))
|
||||||
|
self.fg_parts.append(self.canvas.create_rectangle(self.corner_radius, 0,
|
||||||
|
self.width-self.corner_radius, self.height))
|
||||||
|
|
||||||
|
if type(self.bg_color) == tuple:
|
||||||
|
self.canvas.configure(bg=self.bg_color[self.appearance_mode])
|
||||||
|
else:
|
||||||
|
self.canvas.configure(bg=self.bg_color)
|
||||||
|
|
||||||
|
for part in self.fg_parts:
|
||||||
|
if type(self.fg_color) == tuple:
|
||||||
|
self.canvas.itemconfig(part, fill=self.fg_color[self.appearance_mode], width=0)
|
||||||
|
else:
|
||||||
|
self.canvas.itemconfig(part, fill=self.fg_color, width=0)
|
||||||
|
|
||||||
|
if type(self.text_color) == tuple:
|
||||||
|
self.text_label.configure(fg=self.text_color[self.appearance_mode])
|
||||||
|
else:
|
||||||
|
self.text_label.configure(fg=self.text_color)
|
||||||
|
|
||||||
|
if type(self.fg_color) == tuple:
|
||||||
|
self.text_label.configure(bg=self.fg_color[self.appearance_mode])
|
||||||
|
else:
|
||||||
|
self.text_label.configure(bg=self.fg_color)
|
||||||
|
|
||||||
|
def configure_color(self, bg_color=None, fg_color=None, text_color=None):
|
||||||
|
if bg_color is not None:
|
||||||
|
self.bg_color = bg_color
|
||||||
|
|
||||||
|
if fg_color is not None:
|
||||||
|
self.fg_color = fg_color
|
||||||
|
|
||||||
|
if text_color is not None:
|
||||||
|
self.text_color = text_color
|
||||||
|
|
||||||
|
self.draw()
|
||||||
|
|
||||||
|
def set_text(self, text):
|
||||||
|
self.text_label.configure(text=text)
|
||||||
|
|
||||||
|
def change_appearance_mode(self, mode_string):
|
||||||
|
if mode_string.lower() == "dark":
|
||||||
|
self.appearance_mode = 1
|
||||||
|
elif mode_string.lower() == "light":
|
||||||
|
self.appearance_mode = 0
|
||||||
|
|
||||||
|
if isinstance(self.master, CTkFrame):
|
||||||
|
self.bg_color = self.master.fg_color
|
||||||
|
else:
|
||||||
|
self.bg_color = self.master.cget("bg")
|
||||||
|
|
||||||
|
self.draw()
|
145
customtkinter/customtkinter_progressbar.py
Normal file
145
customtkinter/customtkinter_progressbar.py
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
import tkinter
|
||||||
|
|
||||||
|
from .customtkinter_frame import CTkFrame
|
||||||
|
from .appearance_mode_tracker import AppearanceModeTracker
|
||||||
|
from .customtkinter_color_manager import CTkColorManager
|
||||||
|
|
||||||
|
|
||||||
|
class CTkProgressBar(tkinter.Frame):
|
||||||
|
def __init__(self,
|
||||||
|
bg_color=None,
|
||||||
|
border_color=None,
|
||||||
|
fg_color=CTkColorManager.PROGRESS_BG,
|
||||||
|
progress_color=CTkColorManager.MAIN,
|
||||||
|
function=None,
|
||||||
|
width=160,
|
||||||
|
height=20,
|
||||||
|
border_width=5,
|
||||||
|
*args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
AppearanceModeTracker.add(self.change_appearance_mode)
|
||||||
|
|
||||||
|
if bg_color is None:
|
||||||
|
if isinstance(self.master, CTkFrame):
|
||||||
|
self.bg_color = self.master.fg_color
|
||||||
|
else:
|
||||||
|
self.bg_color = self.master.cget("bg")
|
||||||
|
else:
|
||||||
|
self.bg_color = bg_color
|
||||||
|
|
||||||
|
self.border_color = border_color
|
||||||
|
self.fg_color = fg_color
|
||||||
|
self.progress_color = progress_color
|
||||||
|
self.appearance_mode = AppearanceModeTracker.get_mode() # 0: "Light" 1: "Dark"
|
||||||
|
|
||||||
|
self.width = width
|
||||||
|
self.height = height
|
||||||
|
self.border_width = border_width
|
||||||
|
self.value = 0.5
|
||||||
|
|
||||||
|
self.configure(width=self.width, height=self.height)
|
||||||
|
|
||||||
|
self.canvas = tkinter.Canvas(master=self,
|
||||||
|
highlightthicknes=0,
|
||||||
|
width=self.width,
|
||||||
|
height=self.height)
|
||||||
|
self.canvas.place(x=0, y=0)
|
||||||
|
|
||||||
|
self.function = function
|
||||||
|
|
||||||
|
self.border_parts = []
|
||||||
|
self.fg_parts = []
|
||||||
|
self.progress_parts = []
|
||||||
|
|
||||||
|
self.draw()
|
||||||
|
|
||||||
|
def draw(self):
|
||||||
|
self.canvas.delete("all")
|
||||||
|
self.border_parts = []
|
||||||
|
self.fg_parts = []
|
||||||
|
self.progress_parts = []
|
||||||
|
|
||||||
|
# frame_border
|
||||||
|
self.border_parts.append(self.canvas.create_oval(0, 0,
|
||||||
|
self.height, self.height))
|
||||||
|
self.border_parts.append(self.canvas.create_rectangle(self.height/2, 0,
|
||||||
|
self.width-(self.height/2), self.height))
|
||||||
|
self.border_parts.append(self.canvas.create_oval(self.width-self.height, 0,
|
||||||
|
self.width, self.height))
|
||||||
|
|
||||||
|
# foreground
|
||||||
|
self.fg_parts.append(self.canvas.create_oval(self.border_width, self.border_width,
|
||||||
|
self.height-self.border_width, self.height-self.border_width))
|
||||||
|
self.fg_parts.append(self.canvas.create_rectangle(self.height/2, self.border_width,
|
||||||
|
self.width-(self.height/2), self.height-self.border_width))
|
||||||
|
self.fg_parts.append(self.canvas.create_oval(self.width-self.height+self.border_width, self.border_width,
|
||||||
|
self.width-self.border_width, self.height-self.border_width))
|
||||||
|
|
||||||
|
if type(self.bg_color) == tuple:
|
||||||
|
self.canvas.configure(bg=self.bg_color[self.appearance_mode])
|
||||||
|
else:
|
||||||
|
self.canvas.configure(bg=self.bg_color)
|
||||||
|
|
||||||
|
for part in self.border_parts:
|
||||||
|
if type(self.border_color) == tuple:
|
||||||
|
self.canvas.itemconfig(part, fill=self.border_color[self.appearance_mode], width=0)
|
||||||
|
else:
|
||||||
|
self.canvas.itemconfig(part, fill=self.border_color, width=0)
|
||||||
|
|
||||||
|
for part in self.fg_parts:
|
||||||
|
if type(self.fg_color) == tuple:
|
||||||
|
self.canvas.itemconfig(part, fill=self.fg_color[self.appearance_mode], width=0)
|
||||||
|
else:
|
||||||
|
self.canvas.itemconfig(part, fill=self.fg_color, width=0)
|
||||||
|
|
||||||
|
self.set(self.value)
|
||||||
|
|
||||||
|
def set(self, value):
|
||||||
|
self.value = value
|
||||||
|
|
||||||
|
if self.value > 1:
|
||||||
|
self.value = 1
|
||||||
|
elif self.value < 0:
|
||||||
|
self.value = 0
|
||||||
|
|
||||||
|
for part in self.progress_parts:
|
||||||
|
self.canvas.delete(part)
|
||||||
|
|
||||||
|
# progress
|
||||||
|
self.progress_parts.append(self.canvas.create_oval(self.border_width,
|
||||||
|
self.border_width,
|
||||||
|
self.height - self.border_width,
|
||||||
|
self.height - self.border_width))
|
||||||
|
|
||||||
|
self.progress_parts.append(self.canvas.create_rectangle(self.height / 2,
|
||||||
|
self.border_width,
|
||||||
|
self.height / 2 + (self.width - self.height) * self.value,
|
||||||
|
self.height - self.border_width))
|
||||||
|
|
||||||
|
self.progress_parts.append(self.canvas.create_oval(self.height / 2 + (self.width - self.height) * self.value - (self.height) / 2 + self.border_width,
|
||||||
|
self.border_width,
|
||||||
|
self.height / 2 + (self.width - self.height) * self.value + (self.height) / 2 - self.border_width,
|
||||||
|
self.height - self.border_width))
|
||||||
|
|
||||||
|
for part in self.progress_parts:
|
||||||
|
if type(self.progress_color) == tuple:
|
||||||
|
self.canvas.itemconfig(part, fill=self.progress_color[self.appearance_mode], width=0)
|
||||||
|
else:
|
||||||
|
self.canvas.itemconfig(part, fill=self.progress_color, width=0)
|
||||||
|
|
||||||
|
self.canvas.update()
|
||||||
|
self.canvas.update_idletasks()
|
||||||
|
|
||||||
|
def change_appearance_mode(self, mode_string):
|
||||||
|
if mode_string.lower() == "dark":
|
||||||
|
self.appearance_mode = 1
|
||||||
|
elif mode_string.lower() == "light":
|
||||||
|
self.appearance_mode = 0
|
||||||
|
|
||||||
|
if isinstance(self.master, CTkFrame):
|
||||||
|
self.bg_color = self.master.fg_color
|
||||||
|
else:
|
||||||
|
self.bg_color = self.master.cget("bg")
|
||||||
|
|
||||||
|
self.draw()
|
186
customtkinter/customtkinter_slider.py
Normal file
186
customtkinter/customtkinter_slider.py
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
import tkinter
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from .customtkinter_frame import CTkFrame
|
||||||
|
from .appearance_mode_tracker import AppearanceModeTracker
|
||||||
|
from .customtkinter_color_manager import CTkColorManager
|
||||||
|
|
||||||
|
|
||||||
|
class CTkSlider(tkinter.Frame):
|
||||||
|
def __init__(self,
|
||||||
|
bg_color=None,
|
||||||
|
border_color=None,
|
||||||
|
fg_color=CTkColorManager.SLIDER_BG,
|
||||||
|
button_color=CTkColorManager.MAIN,
|
||||||
|
button_hover_color=CTkColorManager.MAIN_HOVER,
|
||||||
|
width=160,
|
||||||
|
height=16,
|
||||||
|
border_width=5.5,
|
||||||
|
command=None,
|
||||||
|
*args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
AppearanceModeTracker.add(self.change_appearance_mode)
|
||||||
|
|
||||||
|
if bg_color is None:
|
||||||
|
if isinstance(self.master, CTkFrame):
|
||||||
|
self.bg_color = self.master.fg_color
|
||||||
|
else:
|
||||||
|
self.bg_color = self.master.cget("bg")
|
||||||
|
else:
|
||||||
|
self.bg_color = bg_color
|
||||||
|
|
||||||
|
self.border_color = border_color
|
||||||
|
self.fg_color = fg_color
|
||||||
|
self.button_color = button_color
|
||||||
|
self.button_hover_color = button_hover_color
|
||||||
|
self.appearance_mode = AppearanceModeTracker.get_mode() # 0: "Light" 1: "Dark"
|
||||||
|
|
||||||
|
self.width = width
|
||||||
|
self.height = height
|
||||||
|
self.border_width = border_width
|
||||||
|
self.callback_function = command
|
||||||
|
self.value = 0.5
|
||||||
|
self.hover_state = False
|
||||||
|
|
||||||
|
self.configure(width=self.width, height=self.height)
|
||||||
|
if sys.platform == "darwin":
|
||||||
|
self.configure(cursor="pointinghand")
|
||||||
|
|
||||||
|
self.canvas = tkinter.Canvas(master=self,
|
||||||
|
highlightthicknes=0,
|
||||||
|
width=self.width,
|
||||||
|
height=self.height)
|
||||||
|
self.canvas.place(x=0, y=0)
|
||||||
|
|
||||||
|
self.canvas.bind("<Enter>", self.on_enter)
|
||||||
|
self.canvas.bind("<Leave>", self.on_leave)
|
||||||
|
self.canvas.bind("<Button-1>", self.clicked)
|
||||||
|
self.canvas.bind("<B1-Motion>", self.clicked)
|
||||||
|
|
||||||
|
self.border_parts = []
|
||||||
|
self.fg_parts = []
|
||||||
|
self.button_parts = []
|
||||||
|
|
||||||
|
self.draw()
|
||||||
|
|
||||||
|
def draw(self):
|
||||||
|
self.canvas.delete("all")
|
||||||
|
self.border_parts = []
|
||||||
|
self.fg_parts = []
|
||||||
|
self.button_parts = []
|
||||||
|
|
||||||
|
# frame_border
|
||||||
|
self.border_parts.append(self.canvas.create_oval(0, 0,
|
||||||
|
self.height, self.height))
|
||||||
|
self.border_parts.append(self.canvas.create_rectangle(self.height/2, 0,
|
||||||
|
self.width-(self.height/2), self.height))
|
||||||
|
self.border_parts.append(self.canvas.create_oval(self.width-self.height, 0,
|
||||||
|
self.width, self.height))
|
||||||
|
|
||||||
|
# foreground
|
||||||
|
self.fg_parts.append(self.canvas.create_oval(self.border_width, self.border_width,
|
||||||
|
self.height-self.border_width, self.height-self.border_width))
|
||||||
|
self.fg_parts.append(self.canvas.create_rectangle(self.height/2, self.border_width,
|
||||||
|
self.width-(self.height/2), self.height-self.border_width))
|
||||||
|
self.fg_parts.append(self.canvas.create_oval(self.width-self.height+self.border_width, self.border_width,
|
||||||
|
self.width-self.border_width, self.height-self.border_width))
|
||||||
|
|
||||||
|
# button
|
||||||
|
self.button_parts.append(self.canvas.create_oval(self.value*self.width - self.height/2, 0,
|
||||||
|
self.value*self.width + self.height/2, self.height))
|
||||||
|
|
||||||
|
if type(self.bg_color) == tuple:
|
||||||
|
self.canvas.configure(bg=self.bg_color[self.appearance_mode])
|
||||||
|
else:
|
||||||
|
self.canvas.configure(bg=self.bg_color)
|
||||||
|
|
||||||
|
for part in self.border_parts:
|
||||||
|
if type(self.border_color) == tuple:
|
||||||
|
self.canvas.itemconfig(part, fill=self.border_color[self.appearance_mode], width=0)
|
||||||
|
else:
|
||||||
|
self.canvas.itemconfig(part, fill=self.border_color, width=0)
|
||||||
|
|
||||||
|
for part in self.fg_parts:
|
||||||
|
if type(self.fg_color) == tuple:
|
||||||
|
self.canvas.itemconfig(part, fill=self.fg_color[self.appearance_mode], width=0)
|
||||||
|
else:
|
||||||
|
self.canvas.itemconfig(part, fill=self.fg_color, width=0)
|
||||||
|
|
||||||
|
for part in self.button_parts:
|
||||||
|
if type(self.button_color) == tuple:
|
||||||
|
self.canvas.itemconfig(part, fill=self.button_color[self.appearance_mode], width=0)
|
||||||
|
else:
|
||||||
|
self.canvas.itemconfig(part, fill=self.button_color, width=0)
|
||||||
|
|
||||||
|
def clicked(self, event=0):
|
||||||
|
self.value = event.x / self.width
|
||||||
|
|
||||||
|
if self.value > 1:
|
||||||
|
self.value = 1
|
||||||
|
if self.value < 0:
|
||||||
|
self.value = 0
|
||||||
|
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
if self.callback_function is not None:
|
||||||
|
self.callback_function(self.value)
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
for part in self.button_parts:
|
||||||
|
self.canvas.delete(part)
|
||||||
|
|
||||||
|
self.button_parts.append(self.canvas.create_oval(self.value * (self.width-self.height), 0,
|
||||||
|
self.value * (self.width-self.height) + self.height, self.height))
|
||||||
|
|
||||||
|
for part in self.button_parts:
|
||||||
|
if self.hover_state is True:
|
||||||
|
if type(self.button_hover_color) == tuple:
|
||||||
|
self.canvas.itemconfig(part, fill=self.button_hover_color[self.appearance_mode], width=0)
|
||||||
|
else:
|
||||||
|
self.canvas.itemconfig(part, fill=self.button_hover_color, width=0)
|
||||||
|
else:
|
||||||
|
if type(self.button_color) == tuple:
|
||||||
|
self.canvas.itemconfig(part, fill=self.button_color[self.appearance_mode], width=0)
|
||||||
|
else:
|
||||||
|
self.canvas.itemconfig(part, fill=self.button_color, width=0)
|
||||||
|
|
||||||
|
def on_enter(self, event=0):
|
||||||
|
self.hover_state = True
|
||||||
|
for part in self.button_parts:
|
||||||
|
if type(self.button_hover_color) == tuple:
|
||||||
|
self.canvas.itemconfig(part, fill=self.button_hover_color[self.appearance_mode], width=0)
|
||||||
|
else:
|
||||||
|
self.canvas.itemconfig(part, fill=self.button_hover_color, width=0)
|
||||||
|
|
||||||
|
def on_leave(self, event=0):
|
||||||
|
self.hover_state = False
|
||||||
|
for part in self.button_parts:
|
||||||
|
if type(self.button_color) == tuple:
|
||||||
|
self.canvas.itemconfig(part, fill=self.button_color[self.appearance_mode], width=0)
|
||||||
|
else:
|
||||||
|
self.canvas.itemconfig(part, fill=self.button_color, width=0)
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
return self.value
|
||||||
|
|
||||||
|
def set(self, value):
|
||||||
|
self.value = value
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
if self.callback_function is not None:
|
||||||
|
self.callback_function(self.value)
|
||||||
|
|
||||||
|
def change_appearance_mode(self, mode_string):
|
||||||
|
if mode_string.lower() == "dark":
|
||||||
|
self.appearance_mode = 1
|
||||||
|
elif mode_string.lower() == "light":
|
||||||
|
self.appearance_mode = 0
|
||||||
|
|
||||||
|
if isinstance(self.master, CTkFrame):
|
||||||
|
self.bg_color = self.master.fg_color
|
||||||
|
else:
|
||||||
|
self.bg_color = self.master.cget("bg")
|
||||||
|
|
||||||
|
self.draw()
|
||||||
|
|
39
main.py
Normal file
39
main.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import tkinter
|
||||||
|
import customtkinter
|
||||||
|
|
||||||
|
customtkinter.enable_macos_darkmode()
|
||||||
|
customtkinter.deactivate_threading()
|
||||||
|
|
||||||
|
app = tkinter.Tk()
|
||||||
|
app.geometry("400x240")
|
||||||
|
app.title("TkinterCustomButton")
|
||||||
|
|
||||||
|
|
||||||
|
def button_function():
|
||||||
|
print("button pressed")
|
||||||
|
|
||||||
|
|
||||||
|
def slider_function(value):
|
||||||
|
progressbar_1.set(value)
|
||||||
|
|
||||||
|
|
||||||
|
frame_1 = customtkinter.CTkFrame(master=app, width=300, height=200, corner_radius=15)
|
||||||
|
frame_1.place(relx=0.5, rely=0.5, anchor=tkinter.CENTER)
|
||||||
|
|
||||||
|
label_1 = customtkinter.CTkLabel(master=frame_1)
|
||||||
|
label_1.place(relx=0.5, rely=0.1, anchor=tkinter.CENTER)
|
||||||
|
|
||||||
|
progressbar_1 = customtkinter.CTkProgressBar(master=frame_1)
|
||||||
|
progressbar_1.place(relx=0.5, rely=0.25, anchor=tkinter.CENTER)
|
||||||
|
|
||||||
|
button_1 = customtkinter.CTkButton(master=frame_1, corner_radius=10, command=button_function)
|
||||||
|
button_1.place(relx=0.5, rely=0.5, anchor=tkinter.CENTER)
|
||||||
|
|
||||||
|
slider_1 = customtkinter.CTkSlider(master=frame_1, command=slider_function)
|
||||||
|
slider_1.place(relx=0.5, rely=0.7, anchor=tkinter.CENTER)
|
||||||
|
|
||||||
|
entry_1 = customtkinter.CTkEntry(master=frame_1)
|
||||||
|
entry_1.place(relx=0.5, rely=0.85, anchor=tkinter.CENTER)
|
||||||
|
|
||||||
|
app.mainloop()
|
||||||
|
customtkinter.disable_macos_darkmode()
|
Loading…
Reference in New Issue
Block a user