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): logger.info("Opening folder selection dialog...") # Use a temporary function to run in a way that doesn't block def ask_folder(): try: # Ensure the root window is focused and on top self.root.deiconify() self.root.attributes("-topmost", True) repo_path = filedialog.askdirectory( parent=self.root, title="Select Git Repository Folder" ) self.root.withdraw() 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) else: logger.info("Folder selection cancelled by user.") except Exception as e: logger.error(f"Error in folder selection: {e}") # Schedule the dialog to run self.root.after(0, ask_folder) 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() # Run pystray in a separate thread threading.Thread(target=self.icon.run, daemon=True).start() # Keep the main thread for tkinter mainloop to handle dialogs self.root.mainloop() if __name__ == "__main__": app = TrayApp() app.run()