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 git_monitor.logger import logger from git_monitor.config import config from git_monitor.git_manager import git_manager from git_monitor.file_watcher import RepositoryWatcher from git_monitor.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()