""" Winget Manager GUI - Пълно функционално приложение Изисквания: pip install customtkinter """ import customtkinter as ctk import subprocess import threading from tkinter import messagebox import sys class WingetGUI(ctk.CTk): def __init__(self): super().__init__() # Конфигурация на прозореца self.title("Winget Manager Pro") self.geometry("1200x800") ctk.set_appearance_mode("dark") ctk.set_default_color_theme("blue") # Проверка за winget if not self.check_winget(): messagebox.showerror("Грешка", "Winget не е намерен! Моля, инсталирайте App Installer от Microsoft Store.") sys.exit(1) # Основен контейнер self.grid_columnconfigure(1, weight=1) self.grid_rowconfigure(0, weight=1) # Странична навигация self.create_sidebar() # Основно съдържание self.create_main_content() # Статус бар self.create_status_bar() def check_winget(self): """Проверява дали winget е инсталиран""" try: subprocess.run(["winget", "--version"], capture_output=True, timeout=5, creationflags=subprocess.CREATE_NO_WINDOW) return True except: return False def create_sidebar(self): """Създава странична навигация""" self.sidebar = ctk.CTkFrame(self, width=200, corner_radius=0) self.sidebar.grid(row=0, column=0, sticky="nsew", padx=0, pady=0) self.sidebar.grid_rowconfigure(7, weight=1) # Лого self.logo_label = ctk.CTkLabel( self.sidebar, text="🚀 Winget Manager", font=ctk.CTkFont(size=20, weight="bold") ) self.logo_label.grid(row=0, column=0, padx=20, pady=(20, 30)) # Бутони за навигация self.nav_buttons = {} nav_items = [ ("🔍 Търсене", "search"), ("📥 Инсталиране", "install"), ("🔄 Обновяване", "upgrade"), ("🗑️ Деинсталиране", "uninstall"), ("📋 Списък", "list"), ("ℹ️ Информация", "info"), ("🖥️ Терминал", "terminal"), ] for idx, (text, key) in enumerate(nav_items, start=1): btn = ctk.CTkButton( self.sidebar, text=text, command=lambda k=key: self.show_tab(k), height=40, font=ctk.CTkFont(size=14) ) btn.grid(row=idx, column=0, padx=20, pady=10, sticky="ew") self.nav_buttons[key] = btn # Бутон за изчистване self.clear_btn = ctk.CTkButton( self.sidebar, text="🧹 Изчисти изход", command=self.clear_output, fg_color="transparent", border_width=2, height=40 ) self.clear_btn.grid(row=9, column=0, padx=20, pady=20, sticky="ew") def create_main_content(self): """Създава основното съдържание""" self.main_frame = ctk.CTkFrame(self, corner_radius=0) self.main_frame.grid(row=0, column=1, sticky="nsew", padx=0, pady=0) self.main_frame.grid_rowconfigure(1, weight=1) self.main_frame.grid_columnconfigure(0, weight=1) # Контейнер за табове self.tabs_container = ctk.CTkFrame(self.main_frame) self.tabs_container.grid(row=0, column=0, sticky="ew", padx=20, pady=20) self.tabs_container.grid_columnconfigure(0, weight=1) # Създаване на табове self.tabs = {} self.create_search_tab() self.create_install_tab() self.create_upgrade_tab() self.create_uninstall_tab() self.create_list_tab() self.create_info_tab() self.create_terminal_tab() # Терминален изход (само за GUI табовете) self.output_label = ctk.CTkLabel( self.main_frame, text="📟 Терминален изход", font=ctk.CTkFont(size=16, weight="bold") ) self.output_label.grid(row=2, column=0, padx=20, pady=(20, 5), sticky="w") self.output_text = ctk.CTkTextbox( self.main_frame, height=350, font=("Consolas", 13), wrap="word" ) self.output_text.grid(row=3, column=0, sticky="nsew", padx=20, pady=(0, 20)) # Показване на първия таб self.show_tab("search") def create_search_tab(self): """Таб за търсене""" tab = ctk.CTkFrame(self.tabs_container, fg_color="transparent") self.tabs["search"] = tab ctk.CTkLabel(tab, text="🔍 Търсене на пакети", font=ctk.CTkFont(size=18, weight="bold")).grid( row=0, column=0, padx=10, pady=10, sticky="w") self.search_entry = ctk.CTkEntry( tab, placeholder_text="Въведете име на пакет (напр. chrome, firefox)...", height=40, font=ctk.CTkFont(size=13) ) self.search_entry.grid(row=1, column=0, padx=10, pady=10, sticky="ew") self.search_entry.bind("", lambda e: self.search_packages()) ctk.CTkButton( tab, text="🔍 Търси", command=self.search_packages, height=40, font=ctk.CTkFont(size=14, weight="bold") ).grid(row=2, column=0, padx=10, pady=10, sticky="ew") tab.grid_columnconfigure(0, weight=1) def create_install_tab(self): """Таб за инсталиране""" tab = ctk.CTkFrame(self.tabs_container, fg_color="transparent") self.tabs["install"] = tab ctk.CTkLabel(tab, text="📥 Инсталиране на пакет", font=ctk.CTkFont(size=18, weight="bold")).grid( row=0, column=0, padx=10, pady=10, sticky="w") self.install_entry = ctk.CTkEntry( tab, placeholder_text="Въведете точния ID (напр. Google.Chrome)...", height=40, font=ctk.CTkFont(size=13) ) self.install_entry.grid(row=1, column=0, padx=10, pady=10, sticky="ew") self.install_entry.bind("", lambda e: self.install_package()) ctk.CTkButton( tab, text="📥 Инсталирай", command=self.install_package, height=40, fg_color="green", hover_color="darkgreen", font=ctk.CTkFont(size=14, weight="bold") ).grid(row=2, column=0, padx=10, pady=10, sticky="ew") ctk.CTkLabel( tab, text="💡 Използвайте таб 'Търсене' за намиране на точния ID", font=ctk.CTkFont(size=11), text_color="gray" ).grid(row=3, column=0, padx=10, pady=5, sticky="w") tab.grid_columnconfigure(0, weight=1) def create_upgrade_tab(self): """Таб за обновяване""" tab = ctk.CTkFrame(self.tabs_container, fg_color="transparent") self.tabs["upgrade"] = tab ctk.CTkLabel(tab, text="🔄 Обновяване на пакети", font=ctk.CTkFont(size=18, weight="bold")).grid( row=0, column=0, padx=10, pady=10, sticky="w") ctk.CTkButton( tab, text="🔄 Обнови всички пакети", command=self.upgrade_all, height=50, fg_color="#1e88e5", hover_color="#1565c0", font=ctk.CTkFont(size=15, weight="bold") ).grid(row=1, column=0, padx=10, pady=15, sticky="ew") ctk.CTkLabel( tab, text="── или обнови конкретен пакет ──", font=ctk.CTkFont(size=12), text_color="gray" ).grid(row=2, column=0, padx=10, pady=10) self.upgrade_entry = ctk.CTkEntry( tab, placeholder_text="ID на пакет (напр. Microsoft.VisualStudioCode)...", height=40, font=ctk.CTkFont(size=13) ) self.upgrade_entry.grid(row=3, column=0, padx=10, pady=10, sticky="ew") self.upgrade_entry.bind("", lambda e: self.upgrade_package()) ctk.CTkButton( tab, text="🔄 Обнови", command=self.upgrade_package, height=40, font=ctk.CTkFont(size=14, weight="bold") ).grid(row=4, column=0, padx=10, pady=10, sticky="ew") tab.grid_columnconfigure(0, weight=1) def create_uninstall_tab(self): """Таб за деинсталиране""" tab = ctk.CTkFrame(self.tabs_container, fg_color="transparent") self.tabs["uninstall"] = tab ctk.CTkLabel(tab, text="🗑️ Деинсталиране на пакет", font=ctk.CTkFont(size=18, weight="bold")).grid( row=0, column=0, padx=10, pady=10, sticky="w") self.uninstall_entry = ctk.CTkEntry( tab, placeholder_text="Въведете ID на пакет (напр. Google.Chrome)...", height=40, font=ctk.CTkFont(size=13) ) self.uninstall_entry.grid(row=1, column=0, padx=10, pady=10, sticky="ew") self.uninstall_entry.bind("", lambda e: self.uninstall_package()) ctk.CTkButton( tab, text="🗑️ Деинсталирай", command=self.uninstall_package, height=40, fg_color="red", hover_color="darkred", font=ctk.CTkFont(size=14, weight="bold") ).grid(row=2, column=0, padx=10, pady=10, sticky="ew") ctk.CTkLabel( tab, text="⚠️ Внимание: Операцията ще премахне приложението от системата", font=ctk.CTkFont(size=11), text_color="#ff6b6b" ).grid(row=3, column=0, padx=10, pady=5, sticky="w") tab.grid_columnconfigure(0, weight=1) def create_list_tab(self): """Таб за списък""" tab = ctk.CTkFrame(self.tabs_container, fg_color="transparent") self.tabs["list"] = tab ctk.CTkLabel(tab, text="📋 Списък на приложенията", font=ctk.CTkFont(size=18, weight="bold")).grid( row=0, column=0, padx=10, pady=10, sticky="w") ctk.CTkButton( tab, text="📋 Покажи всички приложения", command=self.list_packages, height=45, font=ctk.CTkFont(size=14, weight="bold") ).grid(row=1, column=0, padx=10, pady=10, sticky="ew") ctk.CTkLabel( tab, text="── или филтрирай списъка ──", font=ctk.CTkFont(size=12), text_color="gray" ).grid(row=2, column=0, padx=10, pady=10) self.filter_entry = ctk.CTkEntry( tab, placeholder_text="Филтър (напр. Microsoft, Git)...", height=40, font=ctk.CTkFont(size=13) ) self.filter_entry.grid(row=3, column=0, padx=10, pady=10, sticky="ew") self.filter_entry.bind("", lambda e: self.filter_packages()) ctk.CTkButton( tab, text="🔍 Филтрирай", command=self.filter_packages, height=40, font=ctk.CTkFont(size=14, weight="bold") ).grid(row=4, column=0, padx=10, pady=10, sticky="ew") tab.grid_columnconfigure(0, weight=1) def create_info_tab(self): """Таб за информация""" tab = ctk.CTkFrame(self.tabs_container, fg_color="transparent") self.tabs["info"] = tab ctk.CTkLabel(tab, text="ℹ️ Информация за пакет", font=ctk.CTkFont(size=18, weight="bold")).grid( row=0, column=0, padx=10, pady=10, sticky="w") self.info_entry = ctk.CTkEntry( tab, placeholder_text="Въведете ID за подробности (напр. Microsoft.VisualStudioCode)...", height=40, font=ctk.CTkFont(size=13) ) self.info_entry.grid(row=1, column=0, padx=10, pady=10, sticky="ew") self.info_entry.bind("", lambda e: self.show_info()) ctk.CTkButton( tab, text="ℹ️ Покажи информация", command=self.show_info, height=40, font=ctk.CTkFont(size=14, weight="bold") ).grid(row=2, column=0, padx=10, pady=10, sticky="ew") tab.grid_columnconfigure(0, weight=1) def create_terminal_tab(self): """Таб за интерактивен терминал""" tab = ctk.CTkFrame(self.tabs_container, fg_color="transparent") self.tabs["terminal"] = tab # Конфигурация за разтегляне tab.grid_rowconfigure(1, weight=1) tab.grid_columnconfigure(0, weight=1) # Заглавие header_frame = ctk.CTkFrame(tab, fg_color="transparent") header_frame.grid(row=0, column=0, sticky="ew", padx=10, pady=10) header_frame.grid_columnconfigure(0, weight=1) ctk.CTkLabel( header_frame, text="🖥️ Интерактивен Winget Терминал", font=ctk.CTkFont(size=18, weight="bold") ).grid(row=0, column=0, sticky="w") ctk.CTkLabel( header_frame, text="💡 Пишете winget команди директно (напр. winget list, winget search chrome)", font=ctk.CTkFont(size=11), text_color="gray" ).grid(row=1, column=0, sticky="w", pady=(5, 0)) # Терминален прозорец self.terminal_output = ctk.CTkTextbox( tab, font=("Consolas", 13), wrap="word" ) self.terminal_output.grid(row=1, column=0, sticky="nsew", padx=10, pady=(10, 10)) # Поле за команда command_frame = ctk.CTkFrame(tab, fg_color="transparent") command_frame.grid(row=2, column=0, sticky="ew", padx=10, pady=(0, 10)) command_frame.grid_columnconfigure(1, weight=1) ctk.CTkLabel( command_frame, text="$", font=ctk.CTkFont(size=16, weight="bold"), text_color="#00ff00" ).grid(row=0, column=0, padx=(5, 10)) self.terminal_entry = ctk.CTkEntry( command_frame, placeholder_text="winget ...", height=45, font=ctk.CTkFont(size=13) ) self.terminal_entry.grid(row=0, column=1, sticky="ew", padx=(0, 10)) self.terminal_entry.bind("", lambda e: self.execute_terminal_command()) self.terminal_entry.bind("", lambda e: self.history_up()) self.terminal_entry.bind("", lambda e: self.history_down()) ctk.CTkButton( command_frame, text="▶ Изпълни", command=self.execute_terminal_command, height=45, width=120, fg_color="#00aa00", hover_color="#008800", font=ctk.CTkFont(size=13, weight="bold") ).grid(row=0, column=2) # Бутони button_frame = ctk.CTkFrame(tab, fg_color="transparent") button_frame.grid(row=3, column=0, sticky="ew", padx=10, pady=(0, 10)) ctk.CTkButton( button_frame, text="🧹 Изчисти", command=self.clear_terminal, height=35, width=120, fg_color="transparent", border_width=2 ).pack(side="left", padx=5) ctk.CTkButton( button_frame, text="📋 Копирай изход", command=self.copy_terminal_output, height=35, width=150, fg_color="transparent", border_width=2 ).pack(side="left", padx=5) # История на командите self.command_history = [] self.history_index = -1 # Инициализиране self.terminal_output.insert("1.0", "═══════════════════════════════════════════════════════════\n") self.terminal_output.insert("end", " 🖥️ Winget Интерактивен Терминал\n") self.terminal_output.insert("end", "═══════════════════════════════════════════════════════════\n\n") self.terminal_output.insert("end", "Добре дошли! Можете да изпълнявате всички winget команди.\n\n") self.terminal_output.insert("end", "Примери:\n") self.terminal_output.insert("end", " • winget search chrome\n") self.terminal_output.insert("end", " • winget list\n") self.terminal_output.insert("end", " • winget install --id Google.Chrome\n") self.terminal_output.insert("end", " • winget upgrade --all\n\n") self.terminal_output.insert("end", "Използвайте ↑↓ стрелки за история на командите.\n") self.terminal_output.insert("end", "═══════════════════════════════════════════════════════════\n\n") def execute_terminal_command(self): """Изпълнява команда в терминала""" command = self.terminal_entry.get().strip() if not command: return # Добавяне в история self.command_history.append(command) self.history_index = len(self.command_history) # Показване на командата self.terminal_output.insert("end", f"$ {command}\n") self.terminal_output.see("end") # Изчистване на входа self.terminal_entry.delete(0, "end") # Изпълнение def execute(): try: process = subprocess.Popen( command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, encoding='utf-8', errors='replace', creationflags=subprocess.CREATE_NO_WINDOW ) stdout, stderr = process.communicate() output = stdout if stdout else stderr self.terminal_output.insert("end", output) self.terminal_output.insert("end", "\n") self.terminal_output.see("end") except Exception as e: self.terminal_output.insert("end", f"❌ Грешка: {str(e)}\n\n") self.terminal_output.see("end") threading.Thread(target=execute, daemon=True).start() def history_up(self): """Навигация нагоре в историята""" if self.command_history and self.history_index > 0: self.history_index -= 1 self.terminal_entry.delete(0, "end") self.terminal_entry.insert(0, self.command_history[self.history_index]) def history_down(self): """Навигация надолу в историята""" if self.command_history and self.history_index < len(self.command_history) - 1: self.history_index += 1 self.terminal_entry.delete(0, "end") self.terminal_entry.insert(0, self.command_history[self.history_index]) elif self.history_index == len(self.command_history) - 1: self.history_index = len(self.command_history) self.terminal_entry.delete(0, "end") def clear_terminal(self): """Изчиства терминала""" self.terminal_output.delete("1.0", "end") self.terminal_output.insert("1.0", "═══════════════════════════════════════════════════════════\n") self.terminal_output.insert("end", " 🖥️ Терминалът е изчистен\n") self.terminal_output.insert("end", "═══════════════════════════════════════════════════════════\n\n") def copy_terminal_output(self): """Копира изхода в клипборда""" try: output = self.terminal_output.get("1.0", "end") self.clipboard_clear() self.clipboard_append(output) self.update_status("✅ Изходът е копиран в клипборда") except Exception as e: messagebox.showerror("Грешка", f"Не може да се копира: {str(e)}") def create_status_bar(self): """Създава статус бар""" self.status_bar = ctk.CTkFrame(self, height=30, corner_radius=0) self.status_bar.grid(row=1, column=0, columnspan=2, sticky="ew") self.status_label = ctk.CTkLabel( self.status_bar, text="✅ Готов", font=ctk.CTkFont(size=11) ) self.status_label.pack(side="left", padx=10) def show_tab(self, tab_name): """Показва избрания таб""" for name, tab in self.tabs.items(): tab.grid_forget() self.tabs[tab_name].grid(row=0, column=0, sticky="nsew", padx=0, pady=0) # Скриване/показване на терминалния изход за GUI табовете if tab_name == "terminal": self.output_label.grid_remove() self.output_text.grid_remove() else: self.output_label.grid() self.output_text.grid() # Актуализиране на бутоните for name, btn in self.nav_buttons.items(): if name == tab_name: btn.configure(fg_color=("gray75", "gray25")) else: btn.configure(fg_color=["#3B8ED0", "#1F6AA5"]) def run_command(self, command, success_msg="Операцията завърши успешно"): """Изпълнява winget команда в отделна нишка""" def execute(): self.update_status("🔄 Изпълнява се...") self.output_text.delete("1.0", "end") self.output_text.insert("1.0", f"$ {command}\n\n") try: process = subprocess.Popen( command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, encoding='utf-8', errors='replace', creationflags=subprocess.CREATE_NO_WINDOW ) stdout, stderr = process.communicate() output = stdout if stdout else stderr self.output_text.insert("end", output) if process.returncode == 0: self.update_status(f"✅ {success_msg}") else: self.update_status("❌ Грешка при изпълнение") except Exception as e: self.output_text.insert("end", f"\n❌ Грешка: {str(e)}") self.update_status("❌ Грешка") threading.Thread(target=execute, daemon=True).start() def search_packages(self): query = self.search_entry.get().strip() if not query: messagebox.showwarning("Внимание", "Моля, въведете име на пакет") return self.run_command(f'winget search "{query}"', "Търсенето завърши") def install_package(self): pkg_id = self.install_entry.get().strip() if not pkg_id: messagebox.showwarning("Внимание", "Моля, въведете ID на пакет") return self.run_command( f'winget install --id {pkg_id} -e --silent --accept-package-agreements --accept-source-agreements', f"Пакетът {pkg_id} е инсталиран" ) def upgrade_all(self): self.run_command( 'winget upgrade --all --silent --include-unknown --accept-package-agreements', "Всички пакети са обновени" ) def upgrade_package(self): pkg_id = self.upgrade_entry.get().strip() if not pkg_id: messagebox.showwarning("Внимание", "Моля, въведете ID на пакет") return self.run_command( f'winget upgrade --id {pkg_id} -e --silent --accept-package-agreements', f"Пакетът {pkg_id} е обновен" ) def uninstall_package(self): pkg_id = self.uninstall_entry.get().strip() if not pkg_id: messagebox.showwarning("Внимание", "Моля, въведете ID на пакет") return if messagebox.askyesno("Потвърждение", f"Сигурни ли сте, че искате да деинсталирате {pkg_id}?"): self.run_command( f'winget uninstall --id {pkg_id} -e', f"Пакетът {pkg_id} е деинсталиран" ) def list_packages(self): self.run_command('winget list', "Списъкът е зареден") def filter_packages(self): filter_text = self.filter_entry.get().strip() if not filter_text: messagebox.showwarning("Внимание", "Моля, въведете текст за филтриране") return self.run_command(f'winget list | findstr /i "{filter_text}"', "Филтрирането завърши") def show_info(self): pkg_id = self.info_entry.get().strip() if not pkg_id: messagebox.showwarning("Внимание", "Моля, въведете ID на пакет") return self.run_command(f'winget show --id {pkg_id}', "Информацията е заредена") def clear_output(self): self.output_text.delete("1.0", "end") self.update_status("✅ Изходът е изчистен") def update_status(self, text): """Актуализира статус бара""" self.status_label.configure(text=text) if __name__ == "__main__": app = WingetGUI() app.mainloop()