urocibg revisó este gist . Ir a la revisión
1 file changed, 331 insertions
desktop_organizer.py(archivo creado)
@@ -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() |
Siguiente
Anterior