DucHaiten commited on
Commit
5838d8a
1 Parent(s): 007aedf

Update photo_fantasy.py

Browse files
Files changed (1) hide show
  1. photo_fantasy.py +393 -393
photo_fantasy.py CHANGED
@@ -1,393 +1,393 @@
1
- import tkinter as tk
2
- from tkinter import filedialog, messagebox, ttk
3
- import os
4
- import threading
5
- import queue
6
- import shutil
7
- import subprocess
8
-
9
- # Global variables for controlling filtering and error handling
10
- stop_event = threading.Event()
11
- error_messages = []
12
- error_window = None
13
- selected_files = []
14
- worker_thread = None
15
-
16
- def open_photo_fantasy():
17
- global error_messages, error_window, selected_files
18
- global save_dir_var, status_var, num_files_var, errors_var, thread_count_var, progress
19
- global q, worker_thread, root, stop_button, saved_files
20
-
21
- # Create the Tkinter window
22
- root = tk.Tk()
23
- root.title("Photo Fantasy")
24
-
25
- # Initialize Tkinter variables
26
- save_dir_var = tk.StringVar()
27
- status_var = tk.StringVar()
28
- num_files_var = tk.StringVar()
29
- errors_var = tk.StringVar(value="Errors: 0")
30
- thread_count_var = tk.StringVar(value="4")
31
- progress = tk.IntVar()
32
- q = queue.Queue()
33
-
34
- def center_window(window):
35
- window.update_idletasks()
36
- width = window.winfo_width() + 120 # Add 120 pixels to width
37
- height = window.winfo_height()
38
- x = (window.winfo_screenwidth() // 2) - (width // 2)
39
- y = (window.winfo_screenheight() // 2) - (height // 2)
40
- window.geometry(f'{width}x{height}+{x}+{y}')
41
-
42
- def select_directory():
43
- filepaths = filedialog.askopenfilenames(
44
- title="Select Images",
45
- filetypes=[("All Image files", "*.jpg;*.jpeg;*.png;*.gif;*.bmp;*.tiff")]
46
- )
47
- if filepaths:
48
- selected_files.clear()
49
- selected_files.extend(filepaths)
50
- update_selected_files_label()
51
-
52
- def choose_directory():
53
- directory = filedialog.askdirectory()
54
- if directory:
55
- save_dir_var.set(directory)
56
- save_dir_entry.config(state='normal')
57
- save_dir_entry.delete(0, tk.END)
58
- save_dir_entry.insert(0, directory)
59
- save_dir_entry.config(state='readonly')
60
-
61
- def save_file_with_unique_name(filepath, save_directory, saved_files):
62
- """Save file with a unique name to avoid overwriting."""
63
- if filepath in saved_files:
64
- return # File already saved, do not save again
65
-
66
- base_name, ext = os.path.splitext(os.path.basename(filepath))
67
- save_path = os.path.join(save_directory, f"{base_name}{ext}")
68
- counter = 1
69
- while os.path.exists(save_path):
70
- save_path = os.path.join(save_directory, f"{base_name} ({counter}){ext}")
71
- counter += 1
72
- try:
73
- shutil.copy(filepath, save_path)
74
- saved_files.add(filepath) # Mark this file as saved
75
- except Exception as e:
76
- error_messages.append(f"Error saving file {filepath}: {e}")
77
- update_error_count()
78
-
79
- def update_selected_files_label():
80
- """Update the label showing the number of selected files."""
81
- num_files_var.set(f"{len(selected_files)} files selected.")
82
-
83
- def update_error_count():
84
- """Update the error count displayed in the Errors button."""
85
- errors_var.set(f"Errors: {len(error_messages)}")
86
-
87
- def run_task(task_func):
88
- """Run the given task function in a separate thread."""
89
- global worker_thread
90
- stop_event.clear()
91
- disable_buttons()
92
- worker_thread = threading.Thread(target=task_func)
93
- worker_thread.start()
94
- root.after(100, check_thread)
95
-
96
- def check_thread():
97
- """Check if the worker thread is still running."""
98
- if worker_thread.is_alive():
99
- root.after(100, check_thread)
100
- else:
101
- enable_buttons()
102
- if stop_event.is_set():
103
- status_var.set("Task stopped.")
104
- else:
105
- status_var.set("Task completed.")
106
-
107
- def disable_buttons():
108
- """Disable all buttons except the stop button."""
109
- select_directory_button.config(state='disabled')
110
- choose_dir_button.config(state='disabled')
111
- auto_adjust_button.config(state='disabled')
112
- enhance_vivid_button.config(state='disabled')
113
- horror_theme_button.config(state='disabled')
114
- cinematic_theme_button.config(state='disabled')
115
- cyberpunk_theme_button.config(state='disabled')
116
- fairytale_theme_button.config(state='disabled')
117
- classic_vintage_button.config(state='disabled')
118
- dark_fantasy_button.config(state='disabled')
119
- stop_button.config(state='normal')
120
-
121
- def enable_buttons():
122
- """Enable all buttons."""
123
- select_directory_button.config(state='normal')
124
- choose_dir_button.config(state='normal')
125
- auto_adjust_button.config(state='normal')
126
- enhance_vivid_button.config(state='normal')
127
- horror_theme_button.config(state='normal')
128
- cinematic_theme_button.config(state='normal')
129
- cyberpunk_theme_button.config(state='normal')
130
- fairytale_theme_button.config(state='normal')
131
- classic_vintage_button.config(state='normal')
132
- dark_fantasy_button.config(state='normal')
133
- stop_button.config(state='normal')
134
-
135
- def process_images(process_func):
136
- global saved_files
137
- saved_files = set() # Initialize saved_files set
138
-
139
- if not selected_files or not save_dir_var.get():
140
- messagebox.showerror("Input Error", "Please select images and a save directory.")
141
- enable_buttons()
142
- return
143
-
144
- save_directory = save_dir_var.get()
145
- if not os.path.exists(save_directory):
146
- os.makedirs(save_directory)
147
-
148
- for file in selected_files:
149
- if stop_event.is_set():
150
- break
151
-
152
- base_name, ext = os.path.splitext(os.path.basename(file))
153
- save_path = os.path.join(save_directory, f"{base_name}{ext}")
154
- counter = 1
155
- while os.path.exists(save_path):
156
- save_path = os.path.join(save_directory, f"{base_name} ({counter}){ext}")
157
- counter += 1
158
-
159
- try:
160
- process_func(file, save_path)
161
- saved_files.add(file) # Mark this file as saved
162
- except subprocess.CalledProcessError as e:
163
- error_messages.append(f"Error processing file {file}: {e}")
164
- update_error_count()
165
-
166
- messagebox.showinfo("Processing Complete", f"Processed {len(saved_files)} files.")
167
-
168
- def auto_adjust_images():
169
- process_images(lambda file, save_path: subprocess.run(['magick', 'convert', file,
170
- '-enhance',
171
- '-contrast-stretch', '0.1x0.1%',
172
- '-sharpen', '0x1',
173
- save_path], check=True))
174
-
175
- def enhance_vivid_images():
176
- process_images(lambda file, save_path: subprocess.run(['magick', 'convert', file,
177
- '-enhance',
178
- '-contrast-stretch', '0.1x0.1%',
179
- '-sharpen', '0x1',
180
- '-modulate', '105,120,100',
181
- save_path], check=True))
182
-
183
- def horror_theme_images():
184
- process_images(lambda file, save_path: subprocess.run(['magick', 'convert', file,
185
- '-modulate', '100,90,100',
186
- '-level', '-5%,95%',
187
- '-brightness-contrast', '1x1',
188
- '-sigmoidal-contrast', '3x50%',
189
- '-noise', '3',
190
- '-sharpen', '0x1.5',
191
- '(', '+clone', '-fill', 'black', '-colorize', '5%', ')',
192
- '-compose', 'multiply', '-flatten',
193
- save_path], check=True))
194
-
195
- def cinematic_theme_images():
196
- process_images(lambda file, save_path: subprocess.run(['magick', 'convert', file,
197
- '-level', '-5%,95%',
198
- '-modulate', '100,150,100',
199
- '-colorize', '0,5,0',
200
- '-brightness-contrast', '5x-0',
201
- '-sigmoidal-contrast', '3x50%',
202
- '-sharpen', '0x1.5',
203
- '-noise', '0.1',
204
- '(', '+clone', '-blur', '0x1', ')',
205
- '-compose', 'blend', '-define', 'compose:args=10', '-composite',
206
- '(', '+clone', '-fill', 'black', '-colorize', '10%', ')',
207
- '-compose', 'multiply', '-flatten',
208
- save_path], check=True))
209
-
210
- def cyberpunk_theme_images():
211
- process_images(lambda file, save_path: subprocess.run(['magick', 'convert', file,
212
- '-modulate', '100,130,100',
213
- '-level', '-5%,95%',
214
- '-colorize', '10,0,20',
215
- '-brightness-contrast', '1x1',
216
- '-sigmoidal-contrast', '3x50%',
217
- '-sharpen', '0x0.5',
218
- '-noise', '0.5',
219
- '(', '+clone', '-blur', '0x2', ')',
220
- '-compose', 'blend', '-define', 'compose:args=20', '-composite',
221
- '(', '+clone', '-fill', 'black', '-colorize', '10%', ')',
222
- '-compose', 'multiply', '-flatten',
223
- save_path], check=True))
224
-
225
- def fairytale_theme_images():
226
- process_images(lambda file, save_path: subprocess.run(['magick', 'convert', file,
227
- '-modulate', '100,120,100',
228
- '-blur', '0x1.2',
229
- '-brightness-contrast', '2x-1',
230
- '(', '+clone', '-alpha', 'extract', '-virtual-pixel', 'black',
231
- '-blur', '0x15', '-shade', '120x45', ')',
232
- '-compose', 'softlight', '-composite',
233
- save_path], check=True))
234
-
235
- def classic_vintage_images():
236
- process_images(lambda file, save_path: subprocess.run(['magick', 'convert', file,
237
- '-modulate', '110,80,100',
238
- '-fill', '#704214', '-colorize', '10%',
239
- '-attenuate', '0.3', '+noise', 'Multiplicative',
240
- '-blur', '0x1.2',
241
- '-level', '5%,90%',
242
- '-unsharp', '0x5',
243
- '-colorspace', 'sRGB',
244
- '-brightness-contrast', '-5x15',
245
- save_path], check=True))
246
-
247
- def dark_fantasy_images():
248
- process_images(lambda file, save_path: subprocess.run(['magick', 'convert', file,
249
- '-modulate', '110,130,100',
250
- '-blur', '0x1',
251
- '-brightness-contrast', '5x-10',
252
- '-attenuate', '0.1', '+noise', 'Multiplicative',
253
- '-unsharp', '0x5',
254
- '-level', '5%,95%',
255
- '-modulate', '105,125,100',
256
- '-brightness-contrast', '0x1',
257
- '(', '+clone', '-fill', 'black', '-colorize', '10%', ')',
258
- '-compose', 'multiply', '-flatten',
259
- '-colorspace', 'sRGB',
260
- save_path], check=True))
261
-
262
- def stop_filtering_func():
263
- stop_event.set() # Signal the worker thread to stop
264
- status_var.set("Filtering stopped.")
265
- # Do not disable the Stop button to keep it always enabled
266
- # Re-enable all buttons
267
- enable_buttons()
268
- if worker_thread is not None:
269
- worker_thread.join() # Wait for worker thread to finish
270
-
271
- def return_to_menu():
272
- stop_filtering_func()
273
- root.destroy()
274
- # Import main menu and open it
275
- from main import open_main_menu
276
- open_main_menu()
277
-
278
- def on_closing():
279
- stop_filtering_func()
280
- return_to_menu()
281
-
282
- def show_errors():
283
- global error_window
284
- if error_window is not None:
285
- return
286
-
287
- error_window = tk.Toplevel(root)
288
- error_window.title("Error Details")
289
- error_window.geometry("500x400")
290
-
291
- error_text = tk.Text(error_window, wrap='word')
292
- error_text.pack(expand=True, fill='both')
293
-
294
- if error_messages:
295
- for error in error_messages:
296
- error_text.insert('end', error + '\n')
297
- else:
298
- error_text.insert('end', "No errors recorded.")
299
-
300
- error_text.config(state='disabled')
301
-
302
- def on_close_error_window():
303
- global error_window
304
- error_window.destroy()
305
- error_window = None
306
-
307
- error_window.protocol("WM_DELETE_WINDOW", on_close_error_window)
308
-
309
- def validate_number(P):
310
- if P.isdigit() or P == "":
311
- return True
312
- else:
313
- messagebox.showerror("Input Error", "Please enter only numbers.")
314
- return False
315
-
316
- validate_command = root.register(validate_number)
317
-
318
- # Create UI elements
319
- back_button = tk.Button(root, text="<-", font=('Helvetica', 14), command=return_to_menu)
320
- back_button.pack(anchor='nw', padx=10, pady=10)
321
-
322
- title_label = tk.Label(root, text="Photo Fantasy", font=('Helvetica', 16))
323
- title_label.pack(pady=10)
324
-
325
- select_directory_button = tk.Button(root, text="Select Images", command=select_directory)
326
- select_directory_button.pack(pady=5)
327
-
328
- num_files_label = tk.Label(root, textvariable=num_files_var)
329
- num_files_label.pack(pady=5)
330
-
331
- choose_dir_button = tk.Button(root, text="Choose Save Directory", command=choose_directory)
332
- choose_dir_button.pack(pady=5)
333
-
334
- save_dir_entry = tk.Entry(root, textvariable=save_dir_var, state='readonly', justify='center')
335
- save_dir_entry.pack(pady=5, fill=tk.X)
336
-
337
- # Add Auto Adjust button
338
- auto_adjust_button = tk.Button(root, text="Auto Adjust Images", command=lambda: run_task(auto_adjust_images))
339
- auto_adjust_button.pack(pady=10)
340
-
341
- # Add Enhance Vivid button
342
- enhance_vivid_button = tk.Button(root, text="Enhance Vivid Images", command=lambda: run_task(enhance_vivid_images))
343
- enhance_vivid_button.pack(pady=10)
344
-
345
- # Add Horror Theme button
346
- horror_theme_button = tk.Button(root, text="Horror Theme Images", command=lambda: run_task(horror_theme_images))
347
- horror_theme_button.pack(pady=10)
348
-
349
- # Add Cinematic Theme button
350
- cinematic_theme_button = tk.Button(root, text="Cinematic Theme Images", command=lambda: run_task(cinematic_theme_images))
351
- cinematic_theme_button.pack(pady=10)
352
-
353
- # Add Cyberpunk Theme button
354
- cyberpunk_theme_button = tk.Button(root, text="Cyberpunk Theme Images", command=lambda: run_task(cyberpunk_theme_images))
355
- cyberpunk_theme_button.pack(pady=10)
356
-
357
- # Add Fairytale Theme button
358
- fairytale_theme_button = tk.Button(root, text="Fairytale Theme Images", command=lambda: run_task(fairytale_theme_images))
359
- fairytale_theme_button.pack(pady=10)
360
-
361
- # Add Classic Vintage button
362
- classic_vintage_button = tk.Button(root, text="Classic Vintage Images", command=lambda: run_task(classic_vintage_images))
363
- classic_vintage_button.pack(pady=10)
364
-
365
- # Add Dark Fantasy button
366
- dark_fantasy_button = tk.Button(root, text="Dark Fantasy Images", command=lambda: run_task(dark_fantasy_images))
367
- dark_fantasy_button.pack(pady=10)
368
-
369
- # Add label and entry for thread count
370
- thread_count_label = tk.Label(root, text="Number of Threads:")
371
- thread_count_label.pack(pady=5)
372
-
373
- thread_count_entry = tk.Entry(root, textvariable=thread_count_var, validate="key", validatecommand=(validate_command, '%P'), justify='center', width=4)
374
- thread_count_entry.pack(pady=5)
375
-
376
- stop_button = tk.Button(root, text="Stop", command=stop_filtering_func) # Ensure stop button is a global variable
377
- stop_button.pack(pady=5)
378
-
379
- errors_button = tk.Button(root, textvariable=errors_var, command=show_errors)
380
- errors_button.pack(pady=5)
381
-
382
- progress_bar = ttk.Progressbar(root, variable=progress, maximum=100)
383
- progress_bar.pack(pady=5, fill=tk.X)
384
-
385
- status_label = tk.Label(root, textvariable=status_var, fg="green")
386
- status_label.pack(pady=5)
387
-
388
- center_window(root)
389
- root.protocol("WM_DELETE_WINDOW", on_closing)
390
- root.mainloop()
391
-
392
- if __name__ == "__main__":
393
- open_photo_fantasy()
 
1
+ import tkinter as tk
2
+ from tkinter import filedialog, messagebox, ttk
3
+ import os
4
+ import threading
5
+ import queue
6
+ import shutil
7
+ import subprocess
8
+
9
+ # Global variables for controlling filtering and error handling
10
+ stop_event = threading.Event()
11
+ error_messages = []
12
+ error_window = None
13
+ selected_files = []
14
+ worker_thread = None
15
+
16
+ def open_photo_fantasy():
17
+ global error_messages, error_window, selected_files
18
+ global save_dir_var, status_var, num_files_var, errors_var, thread_count_var, progress
19
+ global q, worker_thread, root, stop_button, saved_files
20
+
21
+ # Create the Tkinter window
22
+ root = tk.Tk()
23
+ root.title("Photo Fantasy")
24
+
25
+ # Initialize Tkinter variables
26
+ save_dir_var = tk.StringVar()
27
+ status_var = tk.StringVar()
28
+ num_files_var = tk.StringVar()
29
+ errors_var = tk.StringVar(value="Errors: 0")
30
+ thread_count_var = tk.StringVar(value="1")
31
+ progress = tk.IntVar()
32
+ q = queue.Queue()
33
+
34
+ def center_window(window):
35
+ window.update_idletasks()
36
+ width = window.winfo_width() + 120 # Add 120 pixels to width
37
+ height = window.winfo_height()
38
+ x = (window.winfo_screenwidth() // 2) - (width // 2)
39
+ y = (window.winfo_screenheight() // 2) - (height // 2)
40
+ window.geometry(f'{width}x{height}+{x}+{y}')
41
+
42
+ def select_directory():
43
+ filepaths = filedialog.askopenfilenames(
44
+ title="Select Images",
45
+ filetypes=[("All Image files", "*.jpg;*.jpeg;*.png;*.gif;*.bmp;*.tiff")]
46
+ )
47
+ if filepaths:
48
+ selected_files.clear()
49
+ selected_files.extend(filepaths)
50
+ update_selected_files_label()
51
+
52
+ def choose_directory():
53
+ directory = filedialog.askdirectory()
54
+ if directory:
55
+ save_dir_var.set(directory)
56
+ save_dir_entry.config(state='normal')
57
+ save_dir_entry.delete(0, tk.END)
58
+ save_dir_entry.insert(0, directory)
59
+ save_dir_entry.config(state='readonly')
60
+
61
+ def save_file_with_unique_name(filepath, save_directory, saved_files):
62
+ """Save file with a unique name to avoid overwriting."""
63
+ if filepath in saved_files:
64
+ return # File already saved, do not save again
65
+
66
+ base_name, ext = os.path.splitext(os.path.basename(filepath))
67
+ save_path = os.path.join(save_directory, f"{base_name}{ext}")
68
+ counter = 1
69
+ while os.path.exists(save_path):
70
+ save_path = os.path.join(save_directory, f"{base_name} ({counter}){ext}")
71
+ counter += 1
72
+ try:
73
+ shutil.copy(filepath, save_path)
74
+ saved_files.add(filepath) # Mark this file as saved
75
+ except Exception as e:
76
+ error_messages.append(f"Error saving file {filepath}: {e}")
77
+ update_error_count()
78
+
79
+ def update_selected_files_label():
80
+ """Update the label showing the number of selected files."""
81
+ num_files_var.set(f"{len(selected_files)} files selected.")
82
+
83
+ def update_error_count():
84
+ """Update the error count displayed in the Errors button."""
85
+ errors_var.set(f"Errors: {len(error_messages)}")
86
+
87
+ def run_task(task_func):
88
+ """Run the given task function in a separate thread."""
89
+ global worker_thread
90
+ stop_event.clear()
91
+ disable_buttons()
92
+ worker_thread = threading.Thread(target=task_func)
93
+ worker_thread.start()
94
+ root.after(100, check_thread)
95
+
96
+ def check_thread():
97
+ """Check if the worker thread is still running."""
98
+ if worker_thread.is_alive():
99
+ root.after(100, check_thread)
100
+ else:
101
+ enable_buttons()
102
+ if stop_event.is_set():
103
+ status_var.set("Task stopped.")
104
+ else:
105
+ status_var.set("Task completed.")
106
+
107
+ def disable_buttons():
108
+ """Disable all buttons except the stop button."""
109
+ select_directory_button.config(state='disabled')
110
+ choose_dir_button.config(state='disabled')
111
+ auto_adjust_button.config(state='disabled')
112
+ enhance_vivid_button.config(state='disabled')
113
+ horror_theme_button.config(state='disabled')
114
+ cinematic_theme_button.config(state='disabled')
115
+ cyberpunk_theme_button.config(state='disabled')
116
+ fairytale_theme_button.config(state='disabled')
117
+ classic_vintage_button.config(state='disabled')
118
+ dark_fantasy_button.config(state='disabled')
119
+ stop_button.config(state='normal')
120
+
121
+ def enable_buttons():
122
+ """Enable all buttons."""
123
+ select_directory_button.config(state='normal')
124
+ choose_dir_button.config(state='normal')
125
+ auto_adjust_button.config(state='normal')
126
+ enhance_vivid_button.config(state='normal')
127
+ horror_theme_button.config(state='normal')
128
+ cinematic_theme_button.config(state='normal')
129
+ cyberpunk_theme_button.config(state='normal')
130
+ fairytale_theme_button.config(state='normal')
131
+ classic_vintage_button.config(state='normal')
132
+ dark_fantasy_button.config(state='normal')
133
+ stop_button.config(state='normal')
134
+
135
+ def process_images(process_func):
136
+ global saved_files
137
+ saved_files = set() # Initialize saved_files set
138
+
139
+ if not selected_files or not save_dir_var.get():
140
+ messagebox.showerror("Input Error", "Please select images and a save directory.")
141
+ enable_buttons()
142
+ return
143
+
144
+ save_directory = save_dir_var.get()
145
+ if not os.path.exists(save_directory):
146
+ os.makedirs(save_directory)
147
+
148
+ for file in selected_files:
149
+ if stop_event.is_set():
150
+ break
151
+
152
+ base_name, ext = os.path.splitext(os.path.basename(file))
153
+ save_path = os.path.join(save_directory, f"{base_name}{ext}")
154
+ counter = 1
155
+ while os.path.exists(save_path):
156
+ save_path = os.path.join(save_directory, f"{base_name} ({counter}){ext}")
157
+ counter += 1
158
+
159
+ try:
160
+ process_func(file, save_path)
161
+ saved_files.add(file) # Mark this file as saved
162
+ except subprocess.CalledProcessError as e:
163
+ error_messages.append(f"Error processing file {file}: {e}")
164
+ update_error_count()
165
+
166
+ messagebox.showinfo("Processing Complete", f"Processed {len(saved_files)} files.")
167
+
168
+ def auto_adjust_images():
169
+ process_images(lambda file, save_path: subprocess.run(['magick', 'convert', file,
170
+ '-enhance',
171
+ '-contrast-stretch', '0.1x0.1%',
172
+ '-sharpen', '0x1',
173
+ save_path], check=True))
174
+
175
+ def enhance_vivid_images():
176
+ process_images(lambda file, save_path: subprocess.run(['magick', 'convert', file,
177
+ '-enhance',
178
+ '-contrast-stretch', '0.1x0.1%',
179
+ '-sharpen', '0x1',
180
+ '-modulate', '105,120,100',
181
+ save_path], check=True))
182
+
183
+ def horror_theme_images():
184
+ process_images(lambda file, save_path: subprocess.run(['magick', 'convert', file,
185
+ '-modulate', '100,90,100',
186
+ '-level', '-5%,95%',
187
+ '-brightness-contrast', '1x1',
188
+ '-sigmoidal-contrast', '3x50%',
189
+ '-noise', '3',
190
+ '-sharpen', '0x1.5',
191
+ '(', '+clone', '-fill', 'black', '-colorize', '5%', ')',
192
+ '-compose', 'multiply', '-flatten',
193
+ save_path], check=True))
194
+
195
+ def cinematic_theme_images():
196
+ process_images(lambda file, save_path: subprocess.run(['magick', 'convert', file,
197
+ '-level', '-5%,95%',
198
+ '-modulate', '100,150,100',
199
+ '-colorize', '0,5,0',
200
+ '-brightness-contrast', '5x-0',
201
+ '-sigmoidal-contrast', '3x50%',
202
+ '-sharpen', '0x1.5',
203
+ '-noise', '0.1',
204
+ '(', '+clone', '-blur', '0x1', ')',
205
+ '-compose', 'blend', '-define', 'compose:args=10', '-composite',
206
+ '(', '+clone', '-fill', 'black', '-colorize', '10%', ')',
207
+ '-compose', 'multiply', '-flatten',
208
+ save_path], check=True))
209
+
210
+ def cyberpunk_theme_images():
211
+ process_images(lambda file, save_path: subprocess.run(['magick', 'convert', file,
212
+ '-modulate', '100,130,100',
213
+ '-level', '-5%,95%',
214
+ '-colorize', '10,0,20',
215
+ '-brightness-contrast', '1x1',
216
+ '-sigmoidal-contrast', '3x50%',
217
+ '-sharpen', '0x0.5',
218
+ '-noise', '0.5',
219
+ '(', '+clone', '-blur', '0x2', ')',
220
+ '-compose', 'blend', '-define', 'compose:args=20', '-composite',
221
+ '(', '+clone', '-fill', 'black', '-colorize', '10%', ')',
222
+ '-compose', 'multiply', '-flatten',
223
+ save_path], check=True))
224
+
225
+ def fairytale_theme_images():
226
+ process_images(lambda file, save_path: subprocess.run(['magick', 'convert', file,
227
+ '-modulate', '100,120,100',
228
+ '-blur', '0x1.2',
229
+ '-brightness-contrast', '2x-1',
230
+ '(', '+clone', '-alpha', 'extract', '-virtual-pixel', 'black',
231
+ '-blur', '0x15', '-shade', '120x45', ')',
232
+ '-compose', 'softlight', '-composite',
233
+ save_path], check=True))
234
+
235
+ def classic_vintage_images():
236
+ process_images(lambda file, save_path: subprocess.run(['magick', 'convert', file,
237
+ '-modulate', '110,80,100',
238
+ '-fill', '#704214', '-colorize', '10%',
239
+ '-attenuate', '0.3', '+noise', 'Multiplicative',
240
+ '-blur', '0x1.2',
241
+ '-level', '5%,90%',
242
+ '-unsharp', '0x5',
243
+ '-colorspace', 'sRGB',
244
+ '-brightness-contrast', '-5x15',
245
+ save_path], check=True))
246
+
247
+ def dark_fantasy_images():
248
+ process_images(lambda file, save_path: subprocess.run(['magick', 'convert', file,
249
+ '-modulate', '110,130,100',
250
+ '-blur', '0x1',
251
+ '-brightness-contrast', '5x-10',
252
+ '-attenuate', '0.1', '+noise', 'Multiplicative',
253
+ '-unsharp', '0x5',
254
+ '-level', '5%,95%',
255
+ '-modulate', '105,125,100',
256
+ '-brightness-contrast', '0x1',
257
+ '(', '+clone', '-fill', 'black', '-colorize', '10%', ')',
258
+ '-compose', 'multiply', '-flatten',
259
+ '-colorspace', 'sRGB',
260
+ save_path], check=True))
261
+
262
+ def stop_filtering_func():
263
+ stop_event.set() # Signal the worker thread to stop
264
+ status_var.set("Filtering stopped.")
265
+ # Do not disable the Stop button to keep it always enabled
266
+ # Re-enable all buttons
267
+ enable_buttons()
268
+ if worker_thread is not None:
269
+ worker_thread.join() # Wait for worker thread to finish
270
+
271
+ def return_to_menu():
272
+ stop_filtering_func()
273
+ root.destroy()
274
+ # Import main menu and open it
275
+ from main import open_main_menu
276
+ open_main_menu()
277
+
278
+ def on_closing():
279
+ stop_filtering_func()
280
+ return_to_menu()
281
+
282
+ def show_errors():
283
+ global error_window
284
+ if error_window is not None:
285
+ return
286
+
287
+ error_window = tk.Toplevel(root)
288
+ error_window.title("Error Details")
289
+ error_window.geometry("500x400")
290
+
291
+ error_text = tk.Text(error_window, wrap='word')
292
+ error_text.pack(expand=True, fill='both')
293
+
294
+ if error_messages:
295
+ for error in error_messages:
296
+ error_text.insert('end', error + '\n')
297
+ else:
298
+ error_text.insert('end', "No errors recorded.")
299
+
300
+ error_text.config(state='disabled')
301
+
302
+ def on_close_error_window():
303
+ global error_window
304
+ error_window.destroy()
305
+ error_window = None
306
+
307
+ error_window.protocol("WM_DELETE_WINDOW", on_close_error_window)
308
+
309
+ def validate_number(P):
310
+ if P.isdigit() or P == "":
311
+ return True
312
+ else:
313
+ messagebox.showerror("Input Error", "Please enter only numbers.")
314
+ return False
315
+
316
+ validate_command = root.register(validate_number)
317
+
318
+ # Create UI elements
319
+ back_button = tk.Button(root, text="<-", font=('Helvetica', 14), command=return_to_menu)
320
+ back_button.pack(anchor='nw', padx=10, pady=10)
321
+
322
+ title_label = tk.Label(root, text="Photo Fantasy", font=('Helvetica', 16))
323
+ title_label.pack(pady=10)
324
+
325
+ select_directory_button = tk.Button(root, text="Select Images", command=select_directory)
326
+ select_directory_button.pack(pady=5)
327
+
328
+ num_files_label = tk.Label(root, textvariable=num_files_var)
329
+ num_files_label.pack(pady=5)
330
+
331
+ choose_dir_button = tk.Button(root, text="Choose Save Directory", command=choose_directory)
332
+ choose_dir_button.pack(pady=5)
333
+
334
+ save_dir_entry = tk.Entry(root, textvariable=save_dir_var, state='readonly', justify='center')
335
+ save_dir_entry.pack(pady=5, fill=tk.X)
336
+
337
+ # Add Auto Adjust button
338
+ auto_adjust_button = tk.Button(root, text="Auto Adjust Images", command=lambda: run_task(auto_adjust_images))
339
+ auto_adjust_button.pack(pady=10)
340
+
341
+ # Add Enhance Vivid button
342
+ enhance_vivid_button = tk.Button(root, text="Enhance Vivid Images", command=lambda: run_task(enhance_vivid_images))
343
+ enhance_vivid_button.pack(pady=10)
344
+
345
+ # Add Horror Theme button
346
+ horror_theme_button = tk.Button(root, text="Horror Theme Images", command=lambda: run_task(horror_theme_images))
347
+ horror_theme_button.pack(pady=10)
348
+
349
+ # Add Cinematic Theme button
350
+ cinematic_theme_button = tk.Button(root, text="Cinematic Theme Images", command=lambda: run_task(cinematic_theme_images))
351
+ cinematic_theme_button.pack(pady=10)
352
+
353
+ # Add Cyberpunk Theme button
354
+ cyberpunk_theme_button = tk.Button(root, text="Cyberpunk Theme Images", command=lambda: run_task(cyberpunk_theme_images))
355
+ cyberpunk_theme_button.pack(pady=10)
356
+
357
+ # Add Fairytale Theme button
358
+ fairytale_theme_button = tk.Button(root, text="Fairytale Theme Images", command=lambda: run_task(fairytale_theme_images))
359
+ fairytale_theme_button.pack(pady=10)
360
+
361
+ # Add Classic Vintage button
362
+ classic_vintage_button = tk.Button(root, text="Classic Vintage Images", command=lambda: run_task(classic_vintage_images))
363
+ classic_vintage_button.pack(pady=10)
364
+
365
+ # Add Dark Fantasy button
366
+ dark_fantasy_button = tk.Button(root, text="Dark Fantasy Images", command=lambda: run_task(dark_fantasy_images))
367
+ dark_fantasy_button.pack(pady=10)
368
+
369
+ # Add label and entry for thread count
370
+ thread_count_label = tk.Label(root, text="Number of Threads:")
371
+ thread_count_label.pack(pady=5)
372
+
373
+ thread_count_entry = tk.Entry(root, textvariable=thread_count_var, validate="key", validatecommand=(validate_command, '%P'), justify='center', width=4)
374
+ thread_count_entry.pack(pady=5)
375
+
376
+ stop_button = tk.Button(root, text="Stop", command=stop_filtering_func) # Ensure stop button is a global variable
377
+ stop_button.pack(pady=5)
378
+
379
+ errors_button = tk.Button(root, textvariable=errors_var, command=show_errors)
380
+ errors_button.pack(pady=5)
381
+
382
+ progress_bar = ttk.Progressbar(root, variable=progress, maximum=100)
383
+ progress_bar.pack(pady=5, fill=tk.X)
384
+
385
+ status_label = tk.Label(root, textvariable=status_var, fg="green")
386
+ status_label.pack(pady=5)
387
+
388
+ center_window(root)
389
+ root.protocol("WM_DELETE_WINDOW", on_closing)
390
+ root.mainloop()
391
+
392
+ if __name__ == "__main__":
393
+ open_photo_fantasy()