import os import threading import tkinter as tk from tkinter import filedialog from PIL import Image, ImageDraw import pystray from pystray import MenuItem as item from logger import logger from config import config from git_manager import git_manager from file_watcher import RepositoryWatcher from notifier import notifier class TrayApp: def __init__(self): self.icon = None self.watcher = None self.root = tk.Tk() self.root.withdraw() # Hide main tkinter window # Load last used repo if exists last_repo = config.repository_path if last_repo: if git_manager.load_repository(last_repo): self.start_monitoring(last_repo) def create_icon(self): # Create a simple icon width = 64 height = 64 image = Image.new('RGB', (width, height), color=(73, 109, 137)) dc = ImageDraw.Draw(image) dc.rectangle([width // 4, height // 4, width * 3 // 4, height * 3 // 4], fill=(255, 255, 255)) menu = ( item('Git Monitor', lambda: None, enabled=False), pystray.Menu.SEPARATOR, item('Select Repository', self.select_repository), item('Exit', self.exit_app), ) self.icon = pystray.Icon("GitMonitor", image, "Git Monitor", menu) def select_repository(self, icon=None, item=None): # Open folder dialog in a separate thread or use the hidden root # tkinter dialogs need to run in the main thread or with care repo_path = filedialog.askdirectory(title="Select Git Repository Folder") if repo_path: logger.info(f"User selected repository: {repo_path}") if git_manager.load_repository(repo_path): config.repository_path = repo_path self.start_monitoring(repo_path) def start_monitoring(self, path): if self.watcher: self.stop_monitoring() self.watcher = RepositoryWatcher(path) self.watcher.start() notifier.notify("Git Monitor", f"Started monitoring: {os.path.basename(path)}") def stop_monitoring(self): if self.watcher: self.watcher.stop() self.watcher = None notifier.notify("Git Monitor", "Stopped monitoring") def exit_app(self, icon=None, item=None): logger.info("Exiting application...") self.stop_monitoring() if self.icon: self.icon.stop() self.root.quit() os._exit(0) # Ensure all threads terminate def run(self): self.create_icon() self.icon.run() if __name__ == "__main__": app = TrayApp() app.run()