urocibg zrewidował ten Gist . Przejdź do rewizji
1 file changed, 331 insertions
desktop_organizer.py(stworzono plik)
| @@ -0,0 +1,331 @@ | |||
| 1 | + | import os | |
| 2 | + | import shutil | |
| 3 | + | import tkinter as tk | |
| 4 | + | from tkinter import ttk, messagebox, scrolledtext | |
| 5 | + | from pathlib import Path | |
| 6 | + | import threading | |
| 7 | + | from datetime import datetime | |
| 8 | + | ||
| 9 | + | class DesktopOrganizerGUI: | |
| 10 | + | def __init__(self, root): | |
| 11 | + | self.root = root | |
| 12 | + | self.root.title("Desktop Organizer - Windows 11") | |
| 13 | + | self.root.geometry("700x600") | |
| 14 | + | self.root.resizable(False, False) | |
| 15 | + | ||
| 16 | + | # Стилизация | |
| 17 | + | style = ttk.Style() | |
| 18 | + | style.theme_use('clam') | |
| 19 | + | ||
| 20 | + | # Определяне на Desktop пътя | |
| 21 | + | self.desktop_path = Path.home() / "Desktop" | |
| 22 | + | ||
| 23 | + | # Категории за файлове | |
| 24 | + | self.categories = { | |
| 25 | + | "Документи": [".pdf", ".doc", ".docx", ".txt", ".rtf", ".odt", ".xls", ".xlsx", ".ppt", ".pptx"], | |
| 26 | + | "Изображения": [".jpg", ".jpeg", ".png", ".gif", ".bmp", ".svg", ".ico", ".webp", ".tiff"], | |
| 27 | + | "Видео": [".mp4", ".avi", ".mkv", ".mov", ".wmv", ".flv", ".webm"], | |
| 28 | + | "Музика": [".mp3", ".wav", ".flac", ".aac", ".ogg", ".wma", ".m4a"], | |
| 29 | + | "Архиви": [".zip", ".rar", ".7z", ".tar", ".gz", ".bz2"], | |
| 30 | + | "Програми": [".exe", ".msi", ".bat", ".cmd"], | |
| 31 | + | "Код": [".py", ".js", ".html", ".css", ".cpp", ".java", ".c", ".h", ".json", ".xml"], | |
| 32 | + | "Други": [] | |
| 33 | + | } | |
| 34 | + | ||
| 35 | + | # Системни файлове и папки за игнориране | |
| 36 | + | self.system_items = ["desktop.ini", "Thumbs.db", "$RECYCLE.BIN"] | |
| 37 | + | ||
| 38 | + | self.create_widgets() | |
| 39 | + | ||
| 40 | + | def create_widgets(self): | |
| 41 | + | # Заглавие | |
| 42 | + | title_frame = tk.Frame(self.root, bg="#0078D4", height=80) | |
| 43 | + | title_frame.pack(fill="x") | |
| 44 | + | title_frame.pack_propagate(False) | |
| 45 | + | ||
| 46 | + | title_label = tk.Label( | |
| 47 | + | title_frame, | |
| 48 | + | text="🧹 Desktop Organizer", | |
| 49 | + | font=("Segoe UI", 20, "bold"), | |
| 50 | + | bg="#0078D4", | |
| 51 | + | fg="white" | |
| 52 | + | ) | |
| 53 | + | title_label.pack(pady=20) | |
| 54 | + | ||
| 55 | + | # Основна рамка | |
| 56 | + | main_frame = tk.Frame(self.root, padx=20, pady=20) | |
| 57 | + | main_frame.pack(fill="both", expand=True) | |
| 58 | + | ||
| 59 | + | # Информация | |
| 60 | + | info_label = tk.Label( | |
| 61 | + | main_frame, | |
| 62 | + | text=f"📂 Desktop път: {self.desktop_path}", | |
| 63 | + | font=("Segoe UI", 10), | |
| 64 | + | justify="left", | |
| 65 | + | anchor="w" | |
| 66 | + | ) | |
| 67 | + | info_label.pack(fill="x", pady=(0, 10)) | |
| 68 | + | ||
| 69 | + | # Настройки рамка | |
| 70 | + | settings_frame = tk.LabelFrame( | |
| 71 | + | main_frame, | |
| 72 | + | text="⚙️ Настройки", | |
| 73 | + | font=("Segoe UI", 11, "bold"), | |
| 74 | + | padx=15, | |
| 75 | + | pady=15 | |
| 76 | + | ) | |
| 77 | + | settings_frame.pack(fill="x", pady=(0, 15)) | |
| 78 | + | ||
| 79 | + | self.keep_shortcuts = tk.BooleanVar(value=True) | |
| 80 | + | tk.Checkbutton( | |
| 81 | + | settings_frame, | |
| 82 | + | text="✓ Запази всички пряки пътища (.lnk)", | |
| 83 | + | variable=self.keep_shortcuts, | |
| 84 | + | font=("Segoe UI", 10) | |
| 85 | + | ).pack(anchor="w", pady=5) | |
| 86 | + | ||
| 87 | + | self.keep_folders = tk.BooleanVar(value=True) | |
| 88 | + | tk.Checkbutton( | |
| 89 | + | settings_frame, | |
| 90 | + | text="✓ Запази съществуващите папки на мястото им", | |
| 91 | + | variable=self.keep_folders, | |
| 92 | + | font=("Segoe UI", 10) | |
| 93 | + | ).pack(anchor="w", pady=5) | |
| 94 | + | ||
| 95 | + | self.main_folder_name = tk.StringVar(value="Организиран Desktop") | |
| 96 | + | folder_frame = tk.Frame(settings_frame) | |
| 97 | + | folder_frame.pack(fill="x", pady=5) | |
| 98 | + | tk.Label( | |
| 99 | + | folder_frame, | |
| 100 | + | text="📁 Име на главната папка:", | |
| 101 | + | font=("Segoe UI", 10) | |
| 102 | + | ).pack(side="left") | |
| 103 | + | tk.Entry( | |
| 104 | + | folder_frame, | |
| 105 | + | textvariable=self.main_folder_name, | |
| 106 | + | font=("Segoe UI", 10), | |
| 107 | + | width=30 | |
| 108 | + | ).pack(side="left", padx=10) | |
| 109 | + | ||
| 110 | + | # Бутони | |
| 111 | + | button_frame = tk.Frame(main_frame) | |
| 112 | + | button_frame.pack(fill="x", pady=(0, 15)) | |
| 113 | + | ||
| 114 | + | self.preview_btn = tk.Button( | |
| 115 | + | button_frame, | |
| 116 | + | text="👁️ Преглед", | |
| 117 | + | font=("Segoe UI", 11, "bold"), | |
| 118 | + | bg="#0078D4", | |
| 119 | + | fg="white", | |
| 120 | + | padx=20, | |
| 121 | + | pady=10, | |
| 122 | + | cursor="hand2", | |
| 123 | + | command=self.preview_organization | |
| 124 | + | ) | |
| 125 | + | self.preview_btn.pack(side="left", padx=(0, 10)) | |
| 126 | + | ||
| 127 | + | self.organize_btn = tk.Button( | |
| 128 | + | button_frame, | |
| 129 | + | text="🚀 Организирай Desktop", | |
| 130 | + | font=("Segoe UI", 11, "bold"), | |
| 131 | + | bg="#107C10", | |
| 132 | + | fg="white", | |
| 133 | + | padx=20, | |
| 134 | + | pady=10, | |
| 135 | + | cursor="hand2", | |
| 136 | + | command=self.start_organization | |
| 137 | + | ) | |
| 138 | + | self.organize_btn.pack(side="left") | |
| 139 | + | ||
| 140 | + | # Лог рамка | |
| 141 | + | log_frame = tk.LabelFrame( | |
| 142 | + | main_frame, | |
| 143 | + | text="📋 Дневник на действията", | |
| 144 | + | font=("Segoe UI", 11, "bold"), | |
| 145 | + | padx=10, | |
| 146 | + | pady=10 | |
| 147 | + | ) | |
| 148 | + | log_frame.pack(fill="both", expand=True) | |
| 149 | + | ||
| 150 | + | self.log_text = scrolledtext.ScrolledText( | |
| 151 | + | log_frame, | |
| 152 | + | font=("Consolas", 9), | |
| 153 | + | wrap=tk.WORD, | |
| 154 | + | height=15, | |
| 155 | + | state="disabled" | |
| 156 | + | ) | |
| 157 | + | self.log_text.pack(fill="both", expand=True) | |
| 158 | + | ||
| 159 | + | # Progress bar | |
| 160 | + | self.progress = ttk.Progressbar( | |
| 161 | + | main_frame, | |
| 162 | + | mode="indeterminate" | |
| 163 | + | ) | |
| 164 | + | self.progress.pack(fill="x", pady=(10, 0)) | |
| 165 | + | ||
| 166 | + | def log(self, message): | |
| 167 | + | self.log_text.config(state="normal") | |
| 168 | + | timestamp = datetime.now().strftime("%H:%M:%S") | |
| 169 | + | self.log_text.insert(tk.END, f"[{timestamp}] {message}\n") | |
| 170 | + | self.log_text.see(tk.END) | |
| 171 | + | self.log_text.config(state="disabled") | |
| 172 | + | self.root.update() | |
| 173 | + | ||
| 174 | + | def get_category(self, file_path): | |
| 175 | + | ext = file_path.suffix.lower() | |
| 176 | + | for category, extensions in self.categories.items(): | |
| 177 | + | if ext in extensions: | |
| 178 | + | return category | |
| 179 | + | return "Други" | |
| 180 | + | ||
| 181 | + | def should_ignore(self, item_path): | |
| 182 | + | # Игнорира системни файлове | |
| 183 | + | if item_path.name in self.system_items: | |
| 184 | + | return True | |
| 185 | + | # Игнорира скрити файлове | |
| 186 | + | if item_path.name.startswith('.'): | |
| 187 | + | return True | |
| 188 | + | return False | |
| 189 | + | ||
| 190 | + | def analyze_desktop(self): | |
| 191 | + | items_to_move = {} | |
| 192 | + | items_to_keep = [] | |
| 193 | + | ||
| 194 | + | for item in self.desktop_path.iterdir(): | |
| 195 | + | if self.should_ignore(item): | |
| 196 | + | continue | |
| 197 | + | ||
| 198 | + | # Запазва пряки пътища | |
| 199 | + | if self.keep_shortcuts.get() and item.suffix.lower() == ".lnk": | |
| 200 | + | items_to_keep.append(item.name) | |
| 201 | + | continue | |
| 202 | + | ||
| 203 | + | # Запазва папки | |
| 204 | + | if item.is_dir(): | |
| 205 | + | if self.keep_folders.get(): | |
| 206 | + | items_to_keep.append(item.name) | |
| 207 | + | continue | |
| 208 | + | else: | |
| 209 | + | # Папките ще бъдат преместени в главната папка | |
| 210 | + | if "Папки" not in items_to_move: | |
| 211 | + | items_to_move["Папки"] = [] | |
| 212 | + | items_to_move["Папки"].append(item) | |
| 213 | + | continue | |
| 214 | + | ||
| 215 | + | # Категоризира файлове | |
| 216 | + | if item.is_file(): | |
| 217 | + | category = self.get_category(item) | |
| 218 | + | if category not in items_to_move: | |
| 219 | + | items_to_move[category] = [] | |
| 220 | + | items_to_move[category].append(item) | |
| 221 | + | ||
| 222 | + | return items_to_move, items_to_keep | |
| 223 | + | ||
| 224 | + | def preview_organization(self): | |
| 225 | + | self.log("🔍 Анализиране на Desktop...") | |
| 226 | + | items_to_move, items_to_keep = self.analyze_desktop() | |
| 227 | + | ||
| 228 | + | self.log("\n✅ Файлове/папки, които ще ОСТАНАТ на Desktop:") | |
| 229 | + | if items_to_keep: | |
| 230 | + | for item in items_to_keep: | |
| 231 | + | self.log(f" • {item}") | |
| 232 | + | else: | |
| 233 | + | self.log(" (няма)") | |
| 234 | + | ||
| 235 | + | self.log("\n📦 Файлове, които ще бъдат ОРГАНИЗИРАНИ:") | |
| 236 | + | total_files = 0 | |
| 237 | + | for category, files in items_to_move.items(): | |
| 238 | + | if files: | |
| 239 | + | self.log(f" 📁 {category}: {len(files)} файла") | |
| 240 | + | total_files += len(files) | |
| 241 | + | ||
| 242 | + | self.log(f"\n📊 Общо файлове за организиране: {total_files}") | |
| 243 | + | self.log("=" * 60) | |
| 244 | + | ||
| 245 | + | def organize_desktop(self): | |
| 246 | + | try: | |
| 247 | + | self.log("🚀 Започване на организация...") | |
| 248 | + | items_to_move, items_to_keep = self.analyze_desktop() | |
| 249 | + | ||
| 250 | + | if not items_to_move: | |
| 251 | + | self.log("✓ Desktop-ът вече е организиран!") | |
| 252 | + | messagebox.showinfo("Готово", "Няма файлове за организиране!") | |
| 253 | + | return | |
| 254 | + | ||
| 255 | + | # Създаване на главна папка | |
| 256 | + | main_folder = self.desktop_path / self.main_folder_name.get() | |
| 257 | + | main_folder.mkdir(exist_ok=True) | |
| 258 | + | self.log(f"✓ Създадена главна папка: {main_folder.name}") | |
| 259 | + | ||
| 260 | + | # Преместване на файлове по категории | |
| 261 | + | moved_count = 0 | |
| 262 | + | for category, files in items_to_move.items(): | |
| 263 | + | if not files: | |
| 264 | + | continue | |
| 265 | + | ||
| 266 | + | category_folder = main_folder / category | |
| 267 | + | category_folder.mkdir(exist_ok=True) | |
| 268 | + | self.log(f"\n📁 Обработка на категория: {category}") | |
| 269 | + | ||
| 270 | + | for file_path in files: | |
| 271 | + | try: | |
| 272 | + | dest = category_folder / file_path.name | |
| 273 | + | ||
| 274 | + | # Ако файлът съществува, добави номер | |
| 275 | + | if dest.exists(): | |
| 276 | + | base = dest.stem | |
| 277 | + | ext = dest.suffix | |
| 278 | + | counter = 1 | |
| 279 | + | while dest.exists(): | |
| 280 | + | dest = category_folder / f"{base}_{counter}{ext}" | |
| 281 | + | counter += 1 | |
| 282 | + | ||
| 283 | + | shutil.move(str(file_path), str(dest)) | |
| 284 | + | self.log(f" ✓ Преместен: {file_path.name}") | |
| 285 | + | moved_count += 1 | |
| 286 | + | except Exception as e: | |
| 287 | + | self.log(f" ✗ Грешка при преместване на {file_path.name}: {str(e)}") | |
| 288 | + | ||
| 289 | + | self.log(f"\n🎉 Организацията завърши успешно!") | |
| 290 | + | self.log(f"📊 Преместени файлове: {moved_count}") | |
| 291 | + | self.log(f"✓ Запазени на Desktop: {len(items_to_keep)} елемента") | |
| 292 | + | ||
| 293 | + | messagebox.showinfo( | |
| 294 | + | "Успех!", | |
| 295 | + | f"Desktop-ът е организиран успешно!\n\n" | |
| 296 | + | f"Преместени: {moved_count} файла\n" | |
| 297 | + | f"Запазени: {len(items_to_keep)} елемента" | |
| 298 | + | ) | |
| 299 | + | ||
| 300 | + | except Exception as e: | |
| 301 | + | self.log(f"❌ ГРЕШКА: {str(e)}") | |
| 302 | + | messagebox.showerror("Грешка", f"Възникна грешка:\n{str(e)}") | |
| 303 | + | finally: | |
| 304 | + | self.progress.stop() | |
| 305 | + | self.organize_btn.config(state="normal") | |
| 306 | + | self.preview_btn.config(state="normal") | |
| 307 | + | ||
| 308 | + | def start_organization(self): | |
| 309 | + | result = messagebox.askyesno( | |
| 310 | + | "Потвърждение", | |
| 311 | + | "Сигурни ли сте, че искате да организирате Desktop-а?\n\n" | |
| 312 | + | "Препоръчваме първо да използвате 'Преглед'." | |
| 313 | + | ) | |
| 314 | + | ||
| 315 | + | if result: | |
| 316 | + | self.organize_btn.config(state="disabled") | |
| 317 | + | self.preview_btn.config(state="disabled") | |
| 318 | + | self.progress.start() | |
| 319 | + | self.log_text.config(state="normal") | |
| 320 | + | self.log_text.delete(1.0, tk.END) | |
| 321 | + | self.log_text.config(state="disabled") | |
| 322 | + | ||
| 323 | + | # Стартиране в отделна нишка | |
| 324 | + | thread = threading.Thread(target=self.organize_desktop) | |
| 325 | + | thread.daemon = True | |
| 326 | + | thread.start() | |
| 327 | + | ||
| 328 | + | if __name__ == "__main__": | |
| 329 | + | root = tk.Tk() | |
| 330 | + | app = DesktopOrganizerGUI(root) | |
| 331 | + | root.mainloop() | |
Nowsze
Starsze