desktop_organizer.py
· 13 KiB · Python
Brut
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() |