desktop_organizer.py
· 13 KiB · Python
Raw
import os
import shutil
import tkinter as tk
from tkinter import ttk, messagebox, scrolledtext
from pathlib import Path
import threading
from datetime import datetime
class DesktopOrganizerGUI:
def __init__(self, root):
self.root = root
self.root.title("Desktop Organizer - Windows 11")
self.root.geometry("700x600")
self.root.resizable(False, False)
# Стилизация
style = ttk.Style()
style.theme_use('clam')
# Определяне на Desktop пътя
self.desktop_path = Path.home() / "Desktop"
# Категории за файлове
self.categories = {
"Документи": [".pdf", ".doc", ".docx", ".txt", ".rtf", ".odt", ".xls", ".xlsx", ".ppt", ".pptx"],
"Изображения": [".jpg", ".jpeg", ".png", ".gif", ".bmp", ".svg", ".ico", ".webp", ".tiff"],
"Видео": [".mp4", ".avi", ".mkv", ".mov", ".wmv", ".flv", ".webm"],
"Музика": [".mp3", ".wav", ".flac", ".aac", ".ogg", ".wma", ".m4a"],
"Архиви": [".zip", ".rar", ".7z", ".tar", ".gz", ".bz2"],
"Програми": [".exe", ".msi", ".bat", ".cmd"],
"Код": [".py", ".js", ".html", ".css", ".cpp", ".java", ".c", ".h", ".json", ".xml"],
"Други": []
}
# Системни файлове и папки за игнориране
self.system_items = ["desktop.ini", "Thumbs.db", "$RECYCLE.BIN"]
self.create_widgets()
def create_widgets(self):
# Заглавие
title_frame = tk.Frame(self.root, bg="#0078D4", height=80)
title_frame.pack(fill="x")
title_frame.pack_propagate(False)
title_label = tk.Label(
title_frame,
text="🧹 Desktop Organizer",
font=("Segoe UI", 20, "bold"),
bg="#0078D4",
fg="white"
)
title_label.pack(pady=20)
# Основна рамка
main_frame = tk.Frame(self.root, padx=20, pady=20)
main_frame.pack(fill="both", expand=True)
# Информация
info_label = tk.Label(
main_frame,
text=f"📂 Desktop път: {self.desktop_path}",
font=("Segoe UI", 10),
justify="left",
anchor="w"
)
info_label.pack(fill="x", pady=(0, 10))
# Настройки рамка
settings_frame = tk.LabelFrame(
main_frame,
text="⚙️ Настройки",
font=("Segoe UI", 11, "bold"),
padx=15,
pady=15
)
settings_frame.pack(fill="x", pady=(0, 15))
self.keep_shortcuts = tk.BooleanVar(value=True)
tk.Checkbutton(
settings_frame,
text="✓ Запази всички пряки пътища (.lnk)",
variable=self.keep_shortcuts,
font=("Segoe UI", 10)
).pack(anchor="w", pady=5)
self.keep_folders = tk.BooleanVar(value=True)
tk.Checkbutton(
settings_frame,
text="✓ Запази съществуващите папки на мястото им",
variable=self.keep_folders,
font=("Segoe UI", 10)
).pack(anchor="w", pady=5)
self.main_folder_name = tk.StringVar(value="Организиран Desktop")
folder_frame = tk.Frame(settings_frame)
folder_frame.pack(fill="x", pady=5)
tk.Label(
folder_frame,
text="📁 Име на главната папка:",
font=("Segoe UI", 10)
).pack(side="left")
tk.Entry(
folder_frame,
textvariable=self.main_folder_name,
font=("Segoe UI", 10),
width=30
).pack(side="left", padx=10)
# Бутони
button_frame = tk.Frame(main_frame)
button_frame.pack(fill="x", pady=(0, 15))
self.preview_btn = tk.Button(
button_frame,
text="👁️ Преглед",
font=("Segoe UI", 11, "bold"),
bg="#0078D4",
fg="white",
padx=20,
pady=10,
cursor="hand2",
command=self.preview_organization
)
self.preview_btn.pack(side="left", padx=(0, 10))
self.organize_btn = tk.Button(
button_frame,
text="🚀 Организирай Desktop",
font=("Segoe UI", 11, "bold"),
bg="#107C10",
fg="white",
padx=20,
pady=10,
cursor="hand2",
command=self.start_organization
)
self.organize_btn.pack(side="left")
# Лог рамка
log_frame = tk.LabelFrame(
main_frame,
text="📋 Дневник на действията",
font=("Segoe UI", 11, "bold"),
padx=10,
pady=10
)
log_frame.pack(fill="both", expand=True)
self.log_text = scrolledtext.ScrolledText(
log_frame,
font=("Consolas", 9),
wrap=tk.WORD,
height=15,
state="disabled"
)
self.log_text.pack(fill="both", expand=True)
# Progress bar
self.progress = ttk.Progressbar(
main_frame,
mode="indeterminate"
)
self.progress.pack(fill="x", pady=(10, 0))
def log(self, message):
self.log_text.config(state="normal")
timestamp = datetime.now().strftime("%H:%M:%S")
self.log_text.insert(tk.END, f"[{timestamp}] {message}\n")
self.log_text.see(tk.END)
self.log_text.config(state="disabled")
self.root.update()
def get_category(self, file_path):
ext = file_path.suffix.lower()
for category, extensions in self.categories.items():
if ext in extensions:
return category
return "Други"
def should_ignore(self, item_path):
# Игнорира системни файлове
if item_path.name in self.system_items:
return True
# Игнорира скрити файлове
if item_path.name.startswith('.'):
return True
return False
def analyze_desktop(self):
items_to_move = {}
items_to_keep = []
for item in self.desktop_path.iterdir():
if self.should_ignore(item):
continue
# Запазва пряки пътища
if self.keep_shortcuts.get() and item.suffix.lower() == ".lnk":
items_to_keep.append(item.name)
continue
# Запазва папки
if item.is_dir():
if self.keep_folders.get():
items_to_keep.append(item.name)
continue
else:
# Папките ще бъдат преместени в главната папка
if "Папки" not in items_to_move:
items_to_move["Папки"] = []
items_to_move["Папки"].append(item)
continue
# Категоризира файлове
if item.is_file():
category = self.get_category(item)
if category not in items_to_move:
items_to_move[category] = []
items_to_move[category].append(item)
return items_to_move, items_to_keep
def preview_organization(self):
self.log("🔍 Анализиране на Desktop...")
items_to_move, items_to_keep = self.analyze_desktop()
self.log("\n✅ Файлове/папки, които ще ОСТАНАТ на Desktop:")
if items_to_keep:
for item in items_to_keep:
self.log(f" • {item}")
else:
self.log(" (няма)")
self.log("\n📦 Файлове, които ще бъдат ОРГАНИЗИРАНИ:")
total_files = 0
for category, files in items_to_move.items():
if files:
self.log(f" 📁 {category}: {len(files)} файла")
total_files += len(files)
self.log(f"\n📊 Общо файлове за организиране: {total_files}")
self.log("=" * 60)
def organize_desktop(self):
try:
self.log("🚀 Започване на организация...")
items_to_move, items_to_keep = self.analyze_desktop()
if not items_to_move:
self.log("✓ Desktop-ът вече е организиран!")
messagebox.showinfo("Готово", "Няма файлове за организиране!")
return
# Създаване на главна папка
main_folder = self.desktop_path / self.main_folder_name.get()
main_folder.mkdir(exist_ok=True)
self.log(f"✓ Създадена главна папка: {main_folder.name}")
# Преместване на файлове по категории
moved_count = 0
for category, files in items_to_move.items():
if not files:
continue
category_folder = main_folder / category
category_folder.mkdir(exist_ok=True)
self.log(f"\n📁 Обработка на категория: {category}")
for file_path in files:
try:
dest = category_folder / file_path.name
# Ако файлът съществува, добави номер
if dest.exists():
base = dest.stem
ext = dest.suffix
counter = 1
while dest.exists():
dest = category_folder / f"{base}_{counter}{ext}"
counter += 1
shutil.move(str(file_path), str(dest))
self.log(f" ✓ Преместен: {file_path.name}")
moved_count += 1
except Exception as e:
self.log(f" ✗ Грешка при преместване на {file_path.name}: {str(e)}")
self.log(f"\n🎉 Организацията завърши успешно!")
self.log(f"📊 Преместени файлове: {moved_count}")
self.log(f"✓ Запазени на Desktop: {len(items_to_keep)} елемента")
messagebox.showinfo(
"Успех!",
f"Desktop-ът е организиран успешно!\n\n"
f"Преместени: {moved_count} файла\n"
f"Запазени: {len(items_to_keep)} елемента"
)
except Exception as e:
self.log(f"❌ ГРЕШКА: {str(e)}")
messagebox.showerror("Грешка", f"Възникна грешка:\n{str(e)}")
finally:
self.progress.stop()
self.organize_btn.config(state="normal")
self.preview_btn.config(state="normal")
def start_organization(self):
result = messagebox.askyesno(
"Потвърждение",
"Сигурни ли сте, че искате да организирате Desktop-а?\n\n"
"Препоръчваме първо да използвате 'Преглед'."
)
if result:
self.organize_btn.config(state="disabled")
self.preview_btn.config(state="disabled")
self.progress.start()
self.log_text.config(state="normal")
self.log_text.delete(1.0, tk.END)
self.log_text.config(state="disabled")
# Стартиране в отделна нишка
thread = threading.Thread(target=self.organize_desktop)
thread.daemon = True
thread.start()
if __name__ == "__main__":
root = tk.Tk()
app = DesktopOrganizerGUI(root)
root.mainloop()
| 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() |