Add theme management and settings functionality
Build and Push Docker Image / build-and-push (push) Has been cancelled
Build and Push Docker Image / build-and-push (push) Has been cancelled
- Introduced `ThemeManager` to handle application themes using `ttkthemes`. - Added `SettingsWindow` for user preferences including theme selection and UI settings. - Integrated theme selection into the main application with a menu for quick access. - Enhanced UI components with custom styles based on the selected theme. - Implemented tooltips for better user guidance across various UI elements. - Updated dependencies to include `ttkthemes` for improved visual appeal.
This commit is contained in:
+98
-16
@@ -11,6 +11,7 @@ from PIL import Image, ImageTk
|
||||
|
||||
from medicine_manager import MedicineManager
|
||||
from pathology_manager import PathologyManager
|
||||
from tooltip_system import TooltipManager
|
||||
|
||||
|
||||
class UIManager:
|
||||
@@ -22,17 +23,22 @@ class UIManager:
|
||||
logger: logging.Logger,
|
||||
medicine_manager: MedicineManager,
|
||||
pathology_manager: PathologyManager,
|
||||
theme_manager, # Import would create circular dependency
|
||||
) -> None:
|
||||
self.root: tk.Tk = root
|
||||
self.logger: logging.Logger = logger
|
||||
self.medicine_manager = medicine_manager
|
||||
self.pathology_manager = pathology_manager
|
||||
self.theme_manager = theme_manager
|
||||
|
||||
# Status bar attributes
|
||||
self.status_bar: tk.Frame | None = None
|
||||
self.status_label: tk.Label | None = None
|
||||
self.file_info_label: tk.Label | None = None
|
||||
|
||||
# Initialize tooltip manager
|
||||
self.tooltip_manager = TooltipManager(theme_manager)
|
||||
|
||||
def setup_application_icon(self, img_path: str) -> bool:
|
||||
"""Set up the application icon."""
|
||||
try:
|
||||
@@ -70,13 +76,20 @@ class UIManager:
|
||||
def create_input_frame(self, parent_frame: ttk.Frame) -> dict[str, Any]:
|
||||
"""Create and configure the input frame with all widgets."""
|
||||
# Create main container for the scrollable input frame
|
||||
main_container = ttk.LabelFrame(parent_frame, text="New Entry")
|
||||
main_container = ttk.LabelFrame(
|
||||
parent_frame, text="New Entry", style="Card.TLabelframe"
|
||||
)
|
||||
main_container.grid(row=1, column=0, padx=10, pady=10, sticky="nsew")
|
||||
main_container.grid_rowconfigure(0, weight=1)
|
||||
main_container.grid_columnconfigure(0, weight=1)
|
||||
|
||||
# Create canvas and scrollbar for scrolling
|
||||
canvas = tk.Canvas(main_container, highlightthickness=0)
|
||||
theme_colors = self.theme_manager.get_theme_colors()
|
||||
canvas = tk.Canvas(
|
||||
main_container,
|
||||
highlightthickness=0,
|
||||
bg=theme_colors["bg"],
|
||||
)
|
||||
scrollbar = ttk.Scrollbar(
|
||||
main_container, orient="vertical", command=canvas.yview
|
||||
)
|
||||
@@ -164,7 +177,9 @@ class UIManager:
|
||||
ttk.Label(input_frame, text="Treatment:").grid(
|
||||
row=medicine_row, column=0, sticky="w", padx=5, pady=2
|
||||
)
|
||||
medicine_frame = ttk.LabelFrame(input_frame, text="Medicine")
|
||||
medicine_frame = ttk.LabelFrame(
|
||||
input_frame, text="Medicine", style="Card.TLabelframe"
|
||||
)
|
||||
medicine_frame.grid(row=medicine_row, column=1, padx=0, pady=10, sticky="nsew")
|
||||
medicine_frame.grid_columnconfigure(0, weight=1)
|
||||
|
||||
@@ -178,11 +193,19 @@ class UIManager:
|
||||
text = f"{medicine.display_name} {medicine.dosage_info}"
|
||||
medicine_vars[medicine_key] = (var, text)
|
||||
|
||||
for idx, (_med_name, (var, text)) in enumerate(medicine_vars.items()):
|
||||
for idx, (med_key, (var, text)) in enumerate(medicine_vars.items()):
|
||||
# Just checkbox for medicine taken
|
||||
ttk.Checkbutton(medicine_frame, text=text, variable=var).grid(
|
||||
row=idx, column=0, sticky="w", padx=5, pady=2
|
||||
checkbox = ttk.Checkbutton(
|
||||
medicine_frame, text=text, variable=var, style="Modern.TCheckbutton"
|
||||
)
|
||||
checkbox.grid(row=idx, column=0, sticky="w", padx=5, pady=2)
|
||||
|
||||
# Add tooltip for medicine checkbox
|
||||
medicine = self.medicine_manager.get_medicine(med_key)
|
||||
if medicine:
|
||||
self.tooltip_manager.add_medicine_tooltip(
|
||||
checkbox, medicine.display_name
|
||||
)
|
||||
|
||||
# Note and Date fields - adjust row numbers
|
||||
note_row = medicine_row + 1
|
||||
@@ -194,16 +217,19 @@ class UIManager:
|
||||
ttk.Label(input_frame, text="Note:").grid(
|
||||
row=note_row, column=0, sticky="w", padx=5, pady=2
|
||||
)
|
||||
ttk.Entry(input_frame, textvariable=note_var).grid(
|
||||
ttk.Entry(input_frame, textvariable=note_var, style="Modern.TEntry").grid(
|
||||
row=note_row, column=1, sticky="ew", padx=5, pady=2
|
||||
)
|
||||
|
||||
ttk.Label(input_frame, text="Date (mm/dd/yyyy):").grid(
|
||||
row=date_row, column=0, sticky="w", padx=5, pady=2
|
||||
)
|
||||
ttk.Entry(input_frame, textvariable=date_var, justify="center").grid(
|
||||
row=date_row, column=1, sticky="ew", padx=5, pady=2
|
||||
)
|
||||
ttk.Entry(
|
||||
input_frame,
|
||||
textvariable=date_var,
|
||||
justify="center",
|
||||
style="Modern.TEntry",
|
||||
).grid(row=date_row, column=1, sticky="ew", padx=5, pady=2)
|
||||
|
||||
# Set default date to today
|
||||
date_var.set(datetime.now().strftime("%m/%d/%Y"))
|
||||
@@ -225,7 +251,7 @@ class UIManager:
|
||||
def create_table_frame(self, parent_frame: ttk.Frame) -> dict[str, Any]:
|
||||
"""Create and configure the table frame with a treeview."""
|
||||
table_frame: ttk.LabelFrame = ttk.LabelFrame(
|
||||
parent_frame, text="Log (Double-click to edit)"
|
||||
parent_frame, text="Log (Double-click to edit)", style="Card.TLabelframe"
|
||||
)
|
||||
table_frame.grid(row=1, column=1, padx=10, pady=10, sticky="nsew")
|
||||
|
||||
@@ -258,7 +284,34 @@ class UIManager:
|
||||
col_labels.append("Note")
|
||||
col_settings.append(("Note", 300, "w"))
|
||||
|
||||
tree: ttk.Treeview = ttk.Treeview(table_frame, columns=columns, show="headings")
|
||||
tree: ttk.Treeview = ttk.Treeview(
|
||||
table_frame, columns=columns, show="headings", style="Modern.Treeview"
|
||||
)
|
||||
|
||||
# Configure treeview selection behavior
|
||||
tree.configure(selectmode="browse") # Single selection mode
|
||||
|
||||
# Configure row tags for alternating colors
|
||||
theme_colors = self.theme_manager.get_theme_colors()
|
||||
tree.tag_configure("evenrow", background=theme_colors["bg"])
|
||||
tree.tag_configure("oddrow", background=theme_colors["alt_bg"])
|
||||
|
||||
# Configure selection highlighting
|
||||
tree.tag_configure(
|
||||
"selected",
|
||||
background=theme_colors["select_bg"],
|
||||
foreground=theme_colors["select_fg"],
|
||||
)
|
||||
|
||||
# Bind selection events to ensure proper highlighting
|
||||
def on_selection_change(event):
|
||||
"""Handle treeview selection changes to ensure proper highlighting."""
|
||||
selection = tree.selection()
|
||||
if selection:
|
||||
# Force focus to ensure selection is visible
|
||||
tree.focus(selection[0])
|
||||
|
||||
tree.bind("<<TreeviewSelect>>", on_selection_change)
|
||||
|
||||
for col, label in zip(columns, col_labels, strict=False):
|
||||
tree.heading(col, text=label)
|
||||
@@ -277,7 +330,9 @@ class UIManager:
|
||||
|
||||
def create_graph_frame(self, parent_frame: ttk.Frame) -> ttk.LabelFrame:
|
||||
"""Create and configure the graph frame."""
|
||||
graph_frame: ttk.LabelFrame = ttk.LabelFrame(parent_frame, text="Evolution")
|
||||
graph_frame: ttk.LabelFrame = ttk.LabelFrame(
|
||||
parent_frame, text="Evolution", style="Card.TLabelframe"
|
||||
)
|
||||
graph_frame.grid(row=0, column=0, columnspan=2, padx=10, pady=10, sticky="nsew")
|
||||
return graph_frame
|
||||
|
||||
@@ -289,23 +344,40 @@ class UIManager:
|
||||
button_frame.grid(row=7, column=0, columnspan=2, pady=10)
|
||||
|
||||
for btn_config in buttons_config:
|
||||
ttk.Button(
|
||||
button = ttk.Button(
|
||||
button_frame,
|
||||
text=btn_config["text"],
|
||||
command=btn_config["command"],
|
||||
).pack(
|
||||
style="Action.TButton",
|
||||
)
|
||||
button.pack(
|
||||
side="left",
|
||||
padx=5,
|
||||
fill=btn_config.get("fill", None),
|
||||
expand=btn_config.get("expand", False),
|
||||
)
|
||||
|
||||
# Add tooltips based on button text
|
||||
button_text = btn_config["text"].lower()
|
||||
if "add" in button_text or "save" in button_text:
|
||||
self.tooltip_manager.add_button_tooltip(button, "save")
|
||||
elif "quit" in button_text or "exit" in button_text:
|
||||
self.tooltip_manager.add_button_tooltip(button, "quit")
|
||||
|
||||
return button_frame
|
||||
|
||||
def create_status_bar(self, parent_frame: tk.Widget) -> tk.Frame:
|
||||
"""Create and configure the status bar at the bottom of the application."""
|
||||
# Get theme colors for consistent styling
|
||||
theme_colors = self.theme_manager.get_theme_colors()
|
||||
|
||||
# Create the status bar frame
|
||||
self.status_bar = tk.Frame(parent_frame, relief=tk.SUNKEN, bd=1)
|
||||
self.status_bar = tk.Frame(
|
||||
parent_frame,
|
||||
relief=tk.SUNKEN,
|
||||
bd=1,
|
||||
bg=theme_colors["bg"],
|
||||
)
|
||||
self.status_bar.grid(row=2, column=0, columnspan=2, sticky="ew", padx=5, pady=2)
|
||||
|
||||
# Configure the parent to make the status bar stretch
|
||||
@@ -319,6 +391,8 @@ class UIManager:
|
||||
font=("TkDefaultFont", 9),
|
||||
padx=10,
|
||||
pady=2,
|
||||
bg=theme_colors["bg"],
|
||||
fg=theme_colors["fg"],
|
||||
)
|
||||
self.status_label.pack(side=tk.LEFT, fill=tk.X, expand=True)
|
||||
|
||||
@@ -330,6 +404,8 @@ class UIManager:
|
||||
font=("TkDefaultFont", 9),
|
||||
padx=10,
|
||||
pady=2,
|
||||
bg=theme_colors["bg"],
|
||||
fg=theme_colors["fg"],
|
||||
)
|
||||
self.file_info_label.pack(side=tk.RIGHT)
|
||||
|
||||
@@ -793,9 +869,15 @@ class UIManager:
|
||||
variable=vars_dict[key],
|
||||
orient=tk.HORIZONTAL,
|
||||
length=250,
|
||||
style="Modern.Horizontal.TScale",
|
||||
)
|
||||
scale.grid(row=0, column=1, sticky="ew")
|
||||
|
||||
# Add tooltip for the scale
|
||||
pathology = self.pathology_manager.get_pathology(key)
|
||||
if pathology:
|
||||
self.tooltip_manager.add_scale_tooltip(scale, pathology.display_name)
|
||||
|
||||
# Scale labels
|
||||
labels_frame = ttk.Frame(scale_container)
|
||||
labels_frame.grid(row=1, column=0, sticky="ew", pady=(5, 0))
|
||||
|
||||
Reference in New Issue
Block a user