File size: 19,295 Bytes
a3fda21
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
import os
import threading
import queue
import shutil
import subprocess

# Global variables for controlling filtering and error handling
stop_event = threading.Event()
error_messages = []
error_window = None
selected_files = []
worker_thread = None

def open_photo_fantasy():
    global error_messages, error_window, selected_files
    global save_dir_var, status_var, num_files_var, errors_var, thread_count_var, progress
    global q, worker_thread, root, stop_button, saved_files

    # Create the Tkinter window
    root = tk.Tk()
    root.title("Photo Fantasy")

    # Initialize Tkinter variables
    save_dir_var = tk.StringVar()
    status_var = tk.StringVar()
    num_files_var = tk.StringVar()
    errors_var = tk.StringVar(value="Errors: 0")
    thread_count_var = tk.StringVar(value="4")
    progress = tk.IntVar()
    q = queue.Queue()

    def center_window(window):
        window.update_idletasks()
        width = window.winfo_width() + 120  # Add 120 pixels to width
        height = window.winfo_height()
        x = (window.winfo_screenwidth() // 2) - (width // 2)
        y = (window.winfo_screenheight() // 2) - (height // 2)
        window.geometry(f'{width}x{height}+{x}+{y}')

    def select_directory():
        filepaths = filedialog.askopenfilenames(
            title="Select Images",
            filetypes=[("All Image files", "*.jpg;*.jpeg;*.png;*.gif;*.bmp;*.tiff")]
        )
        if filepaths:
            selected_files.clear()
            selected_files.extend(filepaths)
            update_selected_files_label()

    def choose_directory():
        directory = filedialog.askdirectory()
        if directory:
            save_dir_var.set(directory)
            save_dir_entry.config(state='normal')
            save_dir_entry.delete(0, tk.END)
            save_dir_entry.insert(0, directory)
            save_dir_entry.config(state='readonly')

    def save_file_with_unique_name(filepath, save_directory, saved_files):
        """Save file with a unique name to avoid overwriting."""
        if filepath in saved_files:
            return  # File already saved, do not save again
    
        base_name, ext = os.path.splitext(os.path.basename(filepath))
        save_path = os.path.join(save_directory, f"{base_name}{ext}")
        counter = 1
        while os.path.exists(save_path):
            save_path = os.path.join(save_directory, f"{base_name} ({counter}){ext}")
            counter += 1
        try:
            shutil.copy(filepath, save_path)
            saved_files.add(filepath)  # Mark this file as saved
        except Exception as e:
            error_messages.append(f"Error saving file {filepath}: {e}")
            update_error_count()

    def update_selected_files_label():
        """Update the label showing the number of selected files."""
        num_files_var.set(f"{len(selected_files)} files selected.")

    def update_error_count():
        """Update the error count displayed in the Errors button."""
        errors_var.set(f"Errors: {len(error_messages)}")

    def run_task(task_func):
        """Run the given task function in a separate thread."""
        global worker_thread
        stop_event.clear()
        disable_buttons()
        worker_thread = threading.Thread(target=task_func)
        worker_thread.start()
        root.after(100, check_thread)

    def check_thread():
        """Check if the worker thread is still running."""
        if worker_thread.is_alive():
            root.after(100, check_thread)
        else:
            enable_buttons()
            if stop_event.is_set():
                status_var.set("Task stopped.")
            else:
                status_var.set("Task completed.")

    def disable_buttons():
        """Disable all buttons except the stop button."""
        select_directory_button.config(state='disabled')
        choose_dir_button.config(state='disabled')
        auto_adjust_button.config(state='disabled')
        enhance_vivid_button.config(state='disabled')
        horror_theme_button.config(state='disabled')
        cinematic_theme_button.config(state='disabled')
        cyberpunk_theme_button.config(state='disabled')
        fairytale_theme_button.config(state='disabled')
        classic_vintage_button.config(state='disabled')
        dark_fantasy_button.config(state='disabled')
        stop_button.config(state='normal')

    def enable_buttons():
        """Enable all buttons."""
        select_directory_button.config(state='normal')
        choose_dir_button.config(state='normal')
        auto_adjust_button.config(state='normal')
        enhance_vivid_button.config(state='normal')
        horror_theme_button.config(state='normal')
        cinematic_theme_button.config(state='normal')
        cyberpunk_theme_button.config(state='normal')
        fairytale_theme_button.config(state='normal')
        classic_vintage_button.config(state='normal')
        dark_fantasy_button.config(state='normal')
        stop_button.config(state='normal')

    def process_images(process_func):
        global saved_files
        saved_files = set()  # Initialize saved_files set

        if not selected_files or not save_dir_var.get():
            messagebox.showerror("Input Error", "Please select images and a save directory.")
            enable_buttons()
            return

        save_directory = save_dir_var.get()
        if not os.path.exists(save_directory):
            os.makedirs(save_directory)

        for file in selected_files:
            if stop_event.is_set():
                break

            base_name, ext = os.path.splitext(os.path.basename(file))
            save_path = os.path.join(save_directory, f"{base_name}{ext}")
            counter = 1
            while os.path.exists(save_path):
                save_path = os.path.join(save_directory, f"{base_name} ({counter}){ext}")
                counter += 1

            try:
                process_func(file, save_path)
                saved_files.add(file)  # Mark this file as saved
            except subprocess.CalledProcessError as e:
                error_messages.append(f"Error processing file {file}: {e}")
                update_error_count()

        messagebox.showinfo("Processing Complete", f"Processed {len(saved_files)} files.")

    def auto_adjust_images():
        process_images(lambda file, save_path: subprocess.run(['magick', 'convert', file, 
                                                               '-enhance', 
                                                               '-contrast-stretch', '0.1x0.1%', 
                                                               '-sharpen', '0x1', 
                                                               save_path], check=True))

    def enhance_vivid_images():
        process_images(lambda file, save_path: subprocess.run(['magick', 'convert', file, 
                                                               '-enhance', 
                                                               '-contrast-stretch', '0.1x0.1%', 
                                                               '-sharpen', '0x1', 
                                                               '-modulate', '105,120,100', 
                                                               save_path], check=True))

    def horror_theme_images():
        process_images(lambda file, save_path: subprocess.run(['magick', 'convert', file, 
                                                               '-modulate', '100,90,100',
                                                               '-level', '-5%,95%', 
                                                               '-brightness-contrast', '1x1',
                                                               '-sigmoidal-contrast', '3x50%', 
                                                               '-noise', '3',
                                                               '-sharpen', '0x1.5', 
                                                               '(', '+clone', '-fill', 'black', '-colorize', '5%', ')', 
                                                               '-compose', 'multiply', '-flatten', 
                                                               save_path], check=True))

    def cinematic_theme_images():
        process_images(lambda file, save_path: subprocess.run(['magick', 'convert', file,
                                                               '-level', '-5%,95%', 
                                                               '-modulate', '100,150,100',
                                                               '-colorize', '0,5,0',   
                                                               '-brightness-contrast', '5x-0',
                                                               '-sigmoidal-contrast', '3x50%', 
                                                               '-sharpen', '0x1.5', 
                                                               '-noise', '0.1', 
                                                               '(', '+clone', '-blur', '0x1', ')', 
                                                               '-compose', 'blend', '-define', 'compose:args=10', '-composite', 
                                                               '(', '+clone', '-fill', 'black', '-colorize', '10%', ')', 
                                                               '-compose', 'multiply', '-flatten', 
                                                               save_path], check=True))

    def cyberpunk_theme_images():
        process_images(lambda file, save_path: subprocess.run(['magick', 'convert', file,
                                                               '-modulate', '100,130,100',
                                                               '-level', '-5%,95%', 
                                                               '-colorize', '10,0,20', 
                                                               '-brightness-contrast', '1x1',
                                                               '-sigmoidal-contrast', '3x50%', 
                                                               '-sharpen', '0x0.5', 
                                                               '-noise', '0.5', 
                                                               '(', '+clone', '-blur', '0x2', ')', 
                                                               '-compose', 'blend', '-define', 'compose:args=20', '-composite',
                                                               '(', '+clone', '-fill', 'black', '-colorize', '10%', ')', 
                                                               '-compose', 'multiply', '-flatten',
                                                               save_path], check=True))

    def fairytale_theme_images():
        process_images(lambda file, save_path: subprocess.run(['magick', 'convert', file,
                                                               '-modulate', '100,120,100', 
                                                               '-blur', '0x1.2', 
                                                               '-brightness-contrast', '2x-1', 
                                                               '(', '+clone', '-alpha', 'extract', '-virtual-pixel', 'black',
                                                               '-blur', '0x15', '-shade', '120x45', ')', 
                                                               '-compose', 'softlight', '-composite',
                                                               save_path], check=True))

    def classic_vintage_images():
        process_images(lambda file, save_path: subprocess.run(['magick', 'convert', file,
                                                               '-modulate', '110,80,100', 
                                                               '-fill', '#704214', '-colorize', '10%', 
                                                               '-attenuate', '0.3', '+noise', 'Multiplicative', 
                                                               '-blur', '0x1.2',
                                                               '-level', '5%,90%', 
                                                               '-unsharp', '0x5',
                                                               '-colorspace', 'sRGB',
                                                               '-brightness-contrast', '-5x15',
                                                               save_path], check=True))

    def dark_fantasy_images():
        process_images(lambda file, save_path: subprocess.run(['magick', 'convert', file,
                                                               '-modulate', '110,130,100', 
                                                               '-blur', '0x1', 
                                                               '-brightness-contrast', '5x-10', 
                                                               '-attenuate', '0.1', '+noise', 'Multiplicative', 
                                                               '-unsharp', '0x5',
                                                               '-level', '5%,95%', 
                                                               '-modulate', '105,125,100', 
                                                               '-brightness-contrast', '0x1', 
                                                               '(', '+clone', '-fill', 'black', '-colorize', '10%', ')', 
                                                               '-compose', 'multiply', '-flatten',
                                                               '-colorspace', 'sRGB',
                                                               save_path], check=True))

    def stop_filtering_func():
        stop_event.set()  # Signal the worker thread to stop
        status_var.set("Filtering stopped.")
        # Do not disable the Stop button to keep it always enabled
        # Re-enable all buttons
        enable_buttons()
        if worker_thread is not None:
            worker_thread.join()  # Wait for worker thread to finish

    def return_to_menu():
        stop_filtering_func()
        root.destroy()
        # Import main menu and open it
        from main import open_main_menu
        open_main_menu()

    def on_closing():
        stop_filtering_func()
        return_to_menu()

    def show_errors():
        global error_window
        if error_window is not None:
            return

        error_window = tk.Toplevel(root)
        error_window.title("Error Details")
        error_window.geometry("500x400")

        error_text = tk.Text(error_window, wrap='word')
        error_text.pack(expand=True, fill='both')

        if error_messages:
            for error in error_messages:
                error_text.insert('end', error + '\n')
        else:
            error_text.insert('end', "No errors recorded.")

        error_text.config(state='disabled')

        def on_close_error_window():
            global error_window
            error_window.destroy()
            error_window = None

        error_window.protocol("WM_DELETE_WINDOW", on_close_error_window)

    def validate_number(P):
        if P.isdigit() or P == "":
            return True
        else:
            messagebox.showerror("Input Error", "Please enter only numbers.")
            return False

    validate_command = root.register(validate_number)

    # Create UI elements
    back_button = tk.Button(root, text="<-", font=('Helvetica', 14), command=return_to_menu)
    back_button.pack(anchor='nw', padx=10, pady=10)

    title_label = tk.Label(root, text="Photo Fantasy", font=('Helvetica', 16))
    title_label.pack(pady=10)

    select_directory_button = tk.Button(root, text="Select Images", command=select_directory)
    select_directory_button.pack(pady=5)

    num_files_label = tk.Label(root, textvariable=num_files_var)
    num_files_label.pack(pady=5)

    choose_dir_button = tk.Button(root, text="Choose Save Directory", command=choose_directory)
    choose_dir_button.pack(pady=5)

    save_dir_entry = tk.Entry(root, textvariable=save_dir_var, state='readonly', justify='center')
    save_dir_entry.pack(pady=5, fill=tk.X)

    # Add Auto Adjust button
    auto_adjust_button = tk.Button(root, text="Auto Adjust Images", command=lambda: run_task(auto_adjust_images))
    auto_adjust_button.pack(pady=10)

    # Add Enhance Vivid button
    enhance_vivid_button = tk.Button(root, text="Enhance Vivid Images", command=lambda: run_task(enhance_vivid_images))
    enhance_vivid_button.pack(pady=10)

    # Add Horror Theme button
    horror_theme_button = tk.Button(root, text="Horror Theme Images", command=lambda: run_task(horror_theme_images))
    horror_theme_button.pack(pady=10)

    # Add Cinematic Theme button
    cinematic_theme_button = tk.Button(root, text="Cinematic Theme Images", command=lambda: run_task(cinematic_theme_images))
    cinematic_theme_button.pack(pady=10)

    # Add Cyberpunk Theme button
    cyberpunk_theme_button = tk.Button(root, text="Cyberpunk Theme Images", command=lambda: run_task(cyberpunk_theme_images))
    cyberpunk_theme_button.pack(pady=10)

    # Add Fairytale Theme button
    fairytale_theme_button = tk.Button(root, text="Fairytale Theme Images", command=lambda: run_task(fairytale_theme_images))
    fairytale_theme_button.pack(pady=10)

    # Add Classic Vintage button
    classic_vintage_button = tk.Button(root, text="Classic Vintage Images", command=lambda: run_task(classic_vintage_images))
    classic_vintage_button.pack(pady=10)

    # Add Dark Fantasy button
    dark_fantasy_button = tk.Button(root, text="Dark Fantasy Images", command=lambda: run_task(dark_fantasy_images))
    dark_fantasy_button.pack(pady=10)

    # Add label and entry for thread count
    thread_count_label = tk.Label(root, text="Number of Threads:")
    thread_count_label.pack(pady=5)

    thread_count_entry = tk.Entry(root, textvariable=thread_count_var, validate="key", validatecommand=(validate_command, '%P'), justify='center', width=4)
    thread_count_entry.pack(pady=5)

    stop_button = tk.Button(root, text="Stop", command=stop_filtering_func)  # Ensure stop button is a global variable
    stop_button.pack(pady=5)

    errors_button = tk.Button(root, textvariable=errors_var, command=show_errors)
    errors_button.pack(pady=5)

    progress_bar = ttk.Progressbar(root, variable=progress, maximum=100)
    progress_bar.pack(pady=5, fill=tk.X)

    status_label = tk.Label(root, textvariable=status_var, fg="green")
    status_label.pack(pady=5)

    center_window(root)
    root.protocol("WM_DELETE_WINDOW", on_closing)
    root.mainloop()

if __name__ == "__main__":
    open_photo_fantasy()