added customtkinter
This commit is contained in:
parent
2fa75da0db
commit
702ddf3802
2 changed files with 101 additions and 148 deletions
|
|
@ -1 +1,2 @@
|
|||
customtkinter
|
||||
reportlab
|
||||
248
time_logix.py
248
time_logix.py
|
|
@ -1,3 +1,4 @@
|
|||
import customtkinter as ctk
|
||||
import tkinter as tk
|
||||
from tkinter import ttk
|
||||
import time
|
||||
|
|
@ -9,51 +10,30 @@ from reportlab.lib import colors
|
|||
from reportlab.platypus import Table, TableStyle, Paragraph
|
||||
from reportlab.lib.styles import getSampleStyleSheet
|
||||
from reportlab.lib.units import inch
|
||||
import sys # Import the sys module
|
||||
import sys
|
||||
|
||||
|
||||
class TimeLogix:
|
||||
def __init__(self, root):
|
||||
self.root = root
|
||||
self.root.title("TimeLogix Application")
|
||||
def __init__(self):
|
||||
self.root = ctk.CTk()
|
||||
self.root.title("TimeLogix")
|
||||
|
||||
# Styling
|
||||
self.bg_color = "#f0f0f0" # Light gray background
|
||||
self.font_family = "Segoe UI" # Modern font
|
||||
self.font_size = 10
|
||||
self.button_bg = "#4CAF50" # Green button background
|
||||
self.button_fg = "white" # White button text
|
||||
self.button_width = 15 # Consistent button width
|
||||
# Theme Configuration
|
||||
ctk.set_appearance_mode("Dark") # Or "Light", "System"
|
||||
ctk.set_default_color_theme("blue")
|
||||
|
||||
self.root.configure(bg=self.bg_color)
|
||||
|
||||
style = ttk.Style()
|
||||
style.configure(
|
||||
"TLabel", background=self.bg_color, font=(self.font_family, self.font_size)
|
||||
)
|
||||
style.configure(
|
||||
"TButton",
|
||||
background=self.button_bg,
|
||||
foreground=self.button_fg,
|
||||
font=(self.font_family, self.font_size),
|
||||
padding=5,
|
||||
width=self.button_width, # Set button width here
|
||||
)
|
||||
style.configure(
|
||||
"TCombobox", background="white", font=(self.font_family, self.font_size)
|
||||
)
|
||||
style.configure(
|
||||
"TEntry", background="white", font=(self.font_family, self.font_size)
|
||||
)
|
||||
style.configure("My.TFrame", background=self.bg_color) # Frame style
|
||||
# --- Styling ---
|
||||
self.font_family = "Segoe UI"
|
||||
self.font_size = 12
|
||||
|
||||
# --- App Data ---
|
||||
self.project_file = "projects.txt"
|
||||
self.projects = self.load_projects()
|
||||
self.is_running = False
|
||||
self.start_time = None
|
||||
self.elapsed_time = 0
|
||||
self.timer_id = None
|
||||
self.log_entries = []
|
||||
self.project_file = "projects.txt"
|
||||
self.projects = self.load_projects()
|
||||
self.invoice_number = 1
|
||||
self.company_name = "Your Company Name"
|
||||
self.company_address = "123 Main St, Anytown, USA"
|
||||
|
|
@ -61,123 +41,98 @@ class TimeLogix:
|
|||
self.client_address = "Client Address"
|
||||
self.hourly_rate = 60.00
|
||||
|
||||
# UI elements
|
||||
self.task_label = ttk.Label(root, text="Task Description:")
|
||||
self.task_label.grid(row=0, column=0, sticky=tk.W, padx=10, pady=5)
|
||||
self.task_entry = ttk.Entry(root, width=40)
|
||||
self.task_entry.grid(row=0, column=1, sticky=tk.W, padx=10, pady=5)
|
||||
# --- Scrollable Frame ---
|
||||
self.scrollable_frame = ctk.CTkScrollableFrame(self.root, width=450, height=600) # Consider making height adaptable
|
||||
self.scrollable_frame.pack(fill="both", expand=True, padx=10, pady=10)
|
||||
|
||||
self.project_label = ttk.Label(root, text="Project:")
|
||||
self.project_label.grid(row=1, column=0, sticky=tk.W, padx=10, pady=5)
|
||||
self.project_combo = ttk.Combobox(root, values=self.projects)
|
||||
self.project_combo.grid(row=1, column=1, sticky=tk.W, padx=10, pady=5)
|
||||
# --- UI Elements ---
|
||||
self.task_label = ctk.CTkLabel(self.scrollable_frame, text="Task Description:", font=(self.font_family, self.font_size))
|
||||
self.task_label.pack(pady=(10, 2))
|
||||
|
||||
self.task_entry = ctk.CTkEntry(self.scrollable_frame, width=250)
|
||||
self.task_entry.pack(pady=(2, 10))
|
||||
|
||||
self.project_label = ctk.CTkLabel(self.scrollable_frame, text="Project:", font=(self.font_family, self.font_size))
|
||||
self.project_label.pack(pady=(10, 2))
|
||||
|
||||
self.project_combo = ctk.CTkComboBox(self.scrollable_frame, values=self.projects, width=250)
|
||||
self.project_combo.pack(pady=(2, 10))
|
||||
if self.projects:
|
||||
self.project_combo.set(self.projects[0])
|
||||
|
||||
self.time_label = ttk.Label(
|
||||
root, text="00:00:00", font=(self.font_family, 36)
|
||||
) # Larger time
|
||||
self.time_label.grid(row=2, column=0, columnspan=2, pady=15)
|
||||
self.time_label = ctk.CTkLabel(self.scrollable_frame, text="00:00:00", font=(self.font_family, 36))
|
||||
self.time_label.pack(pady=(15, 20))
|
||||
|
||||
self.start_stop_button = ttk.Button(
|
||||
root, text="Start", command=self.toggle_timer
|
||||
)
|
||||
self.start_stop_button.grid(row=3, column=0, columnspan=2, pady=10)
|
||||
self.start_stop_button = ctk.CTkButton(self.scrollable_frame, text="Start", command=self.toggle_timer, corner_radius=8)
|
||||
self.start_stop_button.pack(pady=(5, 20))
|
||||
|
||||
self.log_label = ttk.Label(root, text="Log Entries:")
|
||||
self.log_label.grid(row=4, column=0, sticky=tk.W, padx=10, pady=5)
|
||||
self.log_label = ctk.CTkLabel(self.scrollable_frame, text="Log Entries:", font=(self.font_family, self.font_size))
|
||||
self.log_label.pack(pady=(10, 2))
|
||||
|
||||
self.log_text = tk.Text(
|
||||
root,
|
||||
height=10,
|
||||
width=50,
|
||||
bg="white",
|
||||
font=(self.font_family, self.font_size),
|
||||
)
|
||||
self.log_text.grid(row=5, column=0, columnspan=2, padx=10, pady=5)
|
||||
self.log_text = ctk.CTkTextbox(self.scrollable_frame, width=400, height=100, font=(self.font_family, self.font_size))
|
||||
self.log_text.pack(pady=5, padx=10, fill='both', expand=True)
|
||||
|
||||
# Add Project UI
|
||||
self.new_project_label = ttk.Label(root, text="New Project:")
|
||||
self.new_project_label.grid(row=6, column=0, sticky=tk.W, padx=10, pady=5)
|
||||
self.new_project_entry = ttk.Entry(root, width=30)
|
||||
self.new_project_entry.grid(row=6, column=1, sticky=tk.W, padx=10, pady=5)
|
||||
self.new_project_label = ctk.CTkLabel(self.scrollable_frame, text="New Project:", font=(self.font_family, self.font_size))
|
||||
self.new_project_label.pack(pady=(10, 2))
|
||||
|
||||
self.add_project_button = ttk.Button(
|
||||
root, text="Add Project", command=self.add_project
|
||||
)
|
||||
self.add_project_button.grid(row=7, column=0, columnspan=2, pady=5)
|
||||
self.new_project_entry = ctk.CTkEntry(self.scrollable_frame, width=250)
|
||||
self.new_project_entry.pack(pady=(2, 10))
|
||||
|
||||
# Button Grouping
|
||||
button_frame = ttk.Frame(root, padding=10, style="My.TFrame")
|
||||
button_frame.grid(row=8, column=0, columnspan=2, pady=10)
|
||||
self.add_project_button = ctk.CTkButton(self.scrollable_frame, text="Add Project", command=self.add_project, corner_radius=8)
|
||||
self.add_project_button.pack(pady=(5, 15))
|
||||
|
||||
self.export_csv_button = ttk.Button(
|
||||
button_frame, text="Export to CSV", command=self.export_to_csv
|
||||
)
|
||||
self.export_csv_button.grid(row=0, column=0, padx=5, pady=5)
|
||||
# --- Button Frame ---
|
||||
button_frame = ctk.CTkFrame(self.scrollable_frame)
|
||||
button_frame.pack(pady=(10, 15))
|
||||
|
||||
self.export_pdf_button = ttk.Button(
|
||||
button_frame, text="Export to PDF", command=self.export_to_pdf
|
||||
)
|
||||
self.export_pdf_button.grid(row=0, column=1, padx=5, pady=5)
|
||||
self.export_csv_button = ctk.CTkButton(button_frame, text="Export to CSV", command=self.export_to_csv, corner_radius=8)
|
||||
self.export_csv_button.pack(side="left", padx=5, pady=5) # Using side="left" for horizontal layout
|
||||
|
||||
self.exit_button = ttk.Button( # Create Exit button
|
||||
button_frame, text="Exit", command=self.exit_app
|
||||
)
|
||||
self.exit_button.grid(row=0, column=2, padx=5, pady=5) # Place in button_frame
|
||||
self.export_pdf_button = ctk.CTkButton(button_frame, text="Export to PDF", command=self.export_to_pdf, corner_radius=8)
|
||||
self.export_pdf_button.pack(side="left", padx=5, pady=5)
|
||||
|
||||
self.total_time_button = ttk.Button(
|
||||
root,
|
||||
text="Calculate Total Time",
|
||||
command=self.calculate_total_time,
|
||||
width=20, # Set specific width for this button
|
||||
)
|
||||
self.total_time_button.grid(row=9, column=0, columnspan=2, pady=5)
|
||||
self.exit_button = ctk.CTkButton(button_frame, text="Exit", command=self.exit_app, corner_radius=8)
|
||||
self.exit_button.pack(side="left", padx=5, pady=5)
|
||||
|
||||
self.total_time_label = ttk.Label(root, text="Total Time: 00:00:00")
|
||||
self.total_time_label.grid(row=10, column=0, columnspan=2, pady=5)
|
||||
self.total_time_button = ctk.CTkButton(self.scrollable_frame, text="Calculate Total Time", command=self.calculate_total_time, corner_radius=8)
|
||||
self.total_time_button.pack(pady=(5, 15))
|
||||
|
||||
# Settings UI
|
||||
self.company_name_label = ttk.Label(root, text="Company Name:")
|
||||
self.company_name_label.grid(row=11, column=0, sticky=tk.W, padx=10, pady=5)
|
||||
self.company_name_entry = ttk.Entry(root, width=30)
|
||||
self.company_name_entry.grid(row=11, column=1, sticky=tk.W, padx=10, pady=5)
|
||||
self.company_name_entry.insert(0, self.company_name)
|
||||
self.total_time_label = ctk.CTkLabel(self.scrollable_frame, text="Total Time: 00:00:00", font=(self.font_family, self.font_size))
|
||||
self.total_time_label.pack(pady=(5, 15))
|
||||
|
||||
self.company_address_label = ttk.Label(root, text="Company Address:")
|
||||
self.company_address_label.grid(row=12, column=0, sticky=tk.W, padx=10, pady=5)
|
||||
self.company_address_entry = ttk.Entry(root, width=30)
|
||||
self.company_address_entry.grid(row=12, column=1, sticky=tk.W, padx=10, pady=5)
|
||||
self.company_address_entry.insert(0, self.company_address)
|
||||
# --- Settings UI ---
|
||||
self.company_name_label = ctk.CTkLabel(self.scrollable_frame, text="Company Name:", font=(self.font_family, self.font_size))
|
||||
self.company_name_label.pack(pady=(10, 2))
|
||||
|
||||
self.client_name_label = ttk.Label(root, text="Client Name:")
|
||||
self.client_name_label.grid(row=13, column=0, sticky=tk.W, padx=10, pady=5)
|
||||
self.client_name_entry = ttk.Entry(root, width=30)
|
||||
self.client_name_entry.grid(row=13, column=1, sticky=tk.W, padx=10, pady=5)
|
||||
self.client_name_entry.insert(0, self.client_name)
|
||||
self.company_name_entry = ctk.CTkEntry(self.scrollable_frame, width=250)
|
||||
self.company_name_entry.pack(pady=(2, 10))
|
||||
|
||||
self.client_address_label = ttk.Label(root, text="Client Address:")
|
||||
self.client_address_label.grid(row=14, column=0, sticky=tk.W, padx=10, pady=5)
|
||||
self.client_address_entry = ttk.Entry(root, width=30)
|
||||
self.client_address_entry.grid(row=14, column=1, sticky=tk.W, padx=10, pady=5)
|
||||
self.client_address_entry.insert(0, self.client_address)
|
||||
self.company_address_label = ctk.CTkLabel(self.scrollable_frame, text="Company Address:", font=(self.font_family, self.font_size))
|
||||
self.company_address_label.pack(pady=(10, 2))
|
||||
|
||||
self.hourly_rate_label = ttk.Label(root, text="Hourly Rate:")
|
||||
self.hourly_rate_label.grid(row=15, column=0, sticky=tk.W, padx=10, pady=5)
|
||||
self.hourly_rate_entry = ttk.Entry(root, width=10)
|
||||
self.hourly_rate_entry.grid(row=15, column=1, sticky=tk.W, padx=10, pady=5)
|
||||
self.hourly_rate_entry.insert(0, str(self.hourly_rate))
|
||||
self.company_address_entry = ctk.CTkEntry(self.scrollable_frame, width=250)
|
||||
self.company_address_entry.pack(pady=(2, 10))
|
||||
|
||||
self.update_settings_button = ttk.Button(
|
||||
root, text="Update Settings", command=self.update_settings
|
||||
)
|
||||
self.update_settings_button.grid(row=16, column=0, columnspan=2, pady=10)
|
||||
self.client_name_label = ctk.CTkLabel(self.scrollable_frame, text="Client Name:", font=(self.font_family, self.font_size))
|
||||
self.client_name_label.pack(pady=(10, 2))
|
||||
|
||||
# Configure grid weights to make the layout expand
|
||||
for i in range(17):
|
||||
root.grid_rowconfigure(i, weight=1)
|
||||
root.grid_columnconfigure(0, weight=1)
|
||||
root.grid_columnconfigure(1, weight=1)
|
||||
self.client_name_entry = ctk.CTkEntry(self.scrollable_frame, width=250)
|
||||
self.client_name_entry.pack(pady=(2, 10))
|
||||
|
||||
self.client_address_label = ctk.CTkLabel(self.scrollable_frame, text="Client Address:", font=(self.font_family, self.font_size))
|
||||
self.client_address_label.pack(pady=(10, 2))
|
||||
|
||||
self.client_address_entry = ctk.CTkEntry(self.scrollable_frame, width=250)
|
||||
self.client_address_entry.pack(pady=(2, 10))
|
||||
|
||||
self.hourly_rate_label = ctk.CTkLabel(self.scrollable_frame, text="Hourly Rate:", font=(self.font_family, self.font_size))
|
||||
self.hourly_rate_label.pack(pady=(10, 2))
|
||||
|
||||
self.hourly_rate_entry = ctk.CTkEntry(self.scrollable_frame, width=100)
|
||||
self.hourly_rate_entry.pack(pady=(2, 10))
|
||||
|
||||
self.update_settings_button = ctk.CTkButton(self.scrollable_frame, text="Update Settings", command=self.update_settings, corner_radius=8)
|
||||
self.update_settings_button.pack(pady=(15, 20))
|
||||
|
||||
def load_projects(self):
|
||||
try:
|
||||
|
|
@ -199,14 +154,14 @@ class TimeLogix:
|
|||
new_project = self.new_project_entry.get().strip()
|
||||
if new_project and new_project not in self.projects:
|
||||
self.projects.append(new_project)
|
||||
self.project_combo["values"] = self.projects # Update Combobox
|
||||
self.project_combo.set(new_project) # Set to the new project
|
||||
self.project_combo.configure(values=self.projects)
|
||||
self.project_combo.set(new_project)
|
||||
self.save_projects()
|
||||
self.new_project_entry.delete(0, tk.END) # Clear the entry
|
||||
self.new_project_entry.delete(0, tk.END)
|
||||
elif new_project in self.projects:
|
||||
print("Project already exists.") # Replace with a GUI message box
|
||||
print("Project already exists.")
|
||||
else:
|
||||
print("Project name cannot be empty.") # Replace with a GUI message box
|
||||
print("Project name cannot be empty.")
|
||||
|
||||
def update_settings(self):
|
||||
self.company_name = self.company_name_entry.get()
|
||||
|
|
@ -216,10 +171,8 @@ class TimeLogix:
|
|||
try:
|
||||
self.hourly_rate = float(self.hourly_rate_entry.get())
|
||||
except ValueError:
|
||||
print("Invalid hourly rate. Using default.") # Replace with GUI message
|
||||
self.hourly_rate = 50.00 # Revert to default, or handle the error
|
||||
|
||||
# OPTIONAL: Save settings to a file here (e.g., JSON)
|
||||
print("Invalid hourly rate. Using default.")
|
||||
self.hourly_rate = 50.00
|
||||
|
||||
def toggle_timer(self):
|
||||
if self.is_running:
|
||||
|
|
@ -229,13 +182,13 @@ class TimeLogix:
|
|||
|
||||
def start_timer(self):
|
||||
self.is_running = True
|
||||
self.start_stop_button.config(text="Stop")
|
||||
self.start_stop_button.configure(text="Stop")
|
||||
self.start_time = time.time()
|
||||
self.update_timer()
|
||||
|
||||
def stop_timer(self):
|
||||
self.is_running = False
|
||||
self.start_stop_button.config(text="Start")
|
||||
self.start_stop_button.configure(text="Start")
|
||||
self.root.after_cancel(self.timer_id)
|
||||
self.log_time_entry()
|
||||
|
||||
|
|
@ -247,7 +200,7 @@ class TimeLogix:
|
|||
time_str = "{:02d}:{:02d}:{:02d}".format(
|
||||
int(hours), int(minutes), int(seconds)
|
||||
)
|
||||
self.time_label.config(text=time_str)
|
||||
self.time_label.configure(text=time_str)
|
||||
self.timer_id = self.root.after(100, self.update_timer)
|
||||
|
||||
def log_time_entry(self):
|
||||
|
|
@ -267,7 +220,7 @@ class TimeLogix:
|
|||
self.log_entries.append(entry)
|
||||
self.update_log_display()
|
||||
self.elapsed_time = 0
|
||||
self.time_label.config(text="00:00:00")
|
||||
self.time_label.configure(text="00:00:00")
|
||||
|
||||
def update_log_display(self):
|
||||
self.log_text.delete("1.0", tk.END)
|
||||
|
|
@ -388,14 +341,13 @@ class TimeLogix:
|
|||
time_str = "{:02d}:{:02d}:{:02d}".format(
|
||||
int(hours), int(minutes), int(seconds)
|
||||
)
|
||||
self.total_time_label.config(text=f"Total Time: {time_str}")
|
||||
self.total_time_label.configure(text=f"Total Time: {time_str}")
|
||||
|
||||
def exit_app(self): # Define the exit_app method
|
||||
self.root.destroy() # Close the Tkinter window
|
||||
def exit_app(self):
|
||||
self.root.destroy()
|
||||
sys.exit()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
root = tk.Tk()
|
||||
app = TimeLogix(root)
|
||||
root.mainloop()
|
||||
app = TimeLogix()
|
||||
app.root.mainloop()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue