diff --git "a/Danbooru Prompt Selector/TEST2024/NAIA_0218_testv2.py" "b/Danbooru Prompt Selector/TEST2024/NAIA_0218_testv2.py" new file mode 100644--- /dev/null +++ "b/Danbooru Prompt Selector/TEST2024/NAIA_0218_testv2.py" @@ -0,0 +1,6220 @@ +import customtkinter +import tkinter as tk +from tkinter import font +from tkinter import Tk +from PIL import Image, ImageOps, ImageDraw, ImageFont,ImageTk +import os, sys +import ctypes +import pandas as pd +import numpy as np +import NAIA_search, NAIA_random_function_core, NAIA_Login, NAIA_generation +import json +import arti_list, tagbag, wlist, copyright_list_reformatted, remove_result_e, remove_result_qe, copyright_dict_0103 +import character_dictionary as cd +import time +from datetime import datetime +import random +import threading, pyperclip +from ctypes import windll +from CTkListbox import * +from tkinter import filedialog +import io +from openpyxl import Workbook +from openpyxl.styles import Alignment +from openpyxl.drawing.image import Image as OpenpyxlImage +from collections import Counter +from artist_dictionary import artist_dict +import re +import requests +import base64 +import queue +import webbrowser +import ctypes +from plyer import notification +import subprocess, copy +from tkinterdnd2 import TkinterDnD, DND_ALL +import gzip +from pathlib import Path +import configparser + +class Data: + def __init__(self, wlist, tagbag, arti_list, copyright_list_reformatted, cd, remove_result_e, remove_result_qe): + self.whitelist = wlist.whitelist + self.bag_of_tags = tagbag.bag_of_tags + self.afilter_30000 = arti_list.afilter_30000 + self.copyright_keys = copyright_list_reformatted.copyright_list + self.character_keys = list(cd.character_dictionary.keys()) + nsfw_word = list(remove_result_e.keyword_counts.keys()) + questionable_word = list(remove_result_qe.keyword_counts.keys()) + self.qe_word = nsfw_word + questionable_word + + def get_qe_word(self): + return self.qe_word + +class AccountSetting(customtkinter.CTkToplevel): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.title("NAI Account Setting") + self.attributes('-topmost', True) + + def on_close(self): + self.withdraw() + + def NAI_connect(self): + username = self.NAI_ID_entry.get().strip() + password = self.NAI_PW_entry.get().strip() + access_key = NAIA_Login.get_access_key(username, password) + try: + app.access_token = NAIA_Login.login(access_key) + app.NAI_ID = username[:4] + self.state_label.configure(text="(로그인 성공, 닫기 버튼을 눌러주세요.)", font=my_font) + app.NAI_Account_Login.configure(state="disabled") + app.NAI_Token_Remove.configure(state="normal") + app.NAI_Account_State.configure(text="NAI Login : OK") + app.image_generation_button.configure(state="normal") + except Exception as e: + print(e) + self.state_label.configure(text="아이디 혹은 비밀번호 오류입니다.", font=my_font) + + self.button_frame = customtkinter.CTkFrame(self) + self.button_frame.grid(row=0, padx=5, pady=5, sticky="nsew") + self.button_frame.columnconfigure(0, weight=1) + self.button_frame.columnconfigure(1, weight=4) + + my_font = customtkinter.CTkFont('Pretendard', 13) + self.NAI_ID_label = customtkinter.CTkLabel(self.button_frame, text="NAI ID:", font=my_font) + self.NAI_ID_label.grid(row = 0, column = 0, padx=5, pady=5, sticky="nsew") + self.NAI_PW_label = customtkinter.CTkLabel(self.button_frame, text="NAI PW:", font=my_font) + self.NAI_PW_label.grid(row = 1, column = 0, padx=5, pady=5, sticky="nsew") + self.NAI_ID_entry = customtkinter.CTkEntry(self.button_frame) + self.NAI_ID_entry.grid(row = 0, column = 1, padx=5, pady=5, sticky="nsew") + self.NAI_PW_entry = customtkinter.CTkEntry(self.button_frame, show="*") + self.NAI_PW_entry.grid(row = 1, column = 1, padx=5, pady=5, sticky="nsew") + self.connect_button = customtkinter.CTkButton(self.button_frame, text="Connect", command=lambda:NAI_connect(self)) + self.connect_button.grid(row = 2, column = 0, columnspan=2, padx=5, pady=5, sticky="n") + self.state_label = customtkinter.CTkLabel(self.button_frame, text="해당 접속기능은 정상적인 접속 패턴이 아닌점 참고 부탁드립니다.", font=my_font) + self.state_label.grid(row = 3, column = 0, columnspan=2, padx=5, pady=5, sticky="nsew") + + self.protocol("WM_DELETE_WINDOW", lambda: on_close(self)) + +class Advanced_setting(customtkinter.CTkToplevel): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.title("기타 설정") + self.resizable(width=False, height=False) + self.geometry("270x250") + self.attributes('-topmost', True) + my_font = customtkinter.CTkFont('Pretendard', 13) + + def on_close(self): + self.withdraw() + + def select_folder(self): + folder_selected = filedialog.askdirectory() + try: + self.save_path.configure(state="normal") + self.save_path.delete(0,"end") + self.save_path.insert(0, folder_selected) + self.save_path.configure(state="disabled") + app.output_file_path_personal = True + app.output_file_path = folder_selected + except: + pass + + def deselect_folder(self): + self.save_path.configure(state="normal") + app.output_file_path = f"output_NAI\\{app.start_time}\\txt2img" + self.save_path.delete(0,"end") + self.save_path.insert(0, app.output_file_path) + self.save_path.configure(state="disabled") + app.output_file_path_personal = True + + self.advanced_setting_frame = customtkinter.CTkFrame(self, width=250) + self.advanced_setting_frame.grid(row=0, column=0, padx=10, pady=5, sticky="nsew") + self.change_save_path_button = customtkinter.CTkButton(self.advanced_setting_frame, text="이미지 저장 경로 변경", command= lambda: select_folder(self), font=my_font, width=250) + self.change_save_path_button.grid(row=0, column=0, pady=5,sticky="nsew") + self.save_path = customtkinter.CTkEntry(self.advanced_setting_frame, font=my_font, width=250) + self.save_path.insert(0, app.output_file_path) + self.save_path.configure(state="disabled") + self.save_path.grid(row=1, column=0, pady=5,sticky="nsew") + self.change_save_path_button2 = customtkinter.CTkButton(self.advanced_setting_frame, text="경로 초기화", command= lambda: deselect_folder(self), font=my_font, width=250) + self.change_save_path_button2.grid(row=2, column=0, pady=5,sticky="nsew") + self.file_name_label = customtkinter.CTkLabel(self.advanced_setting_frame, text=" -- 파일명.png 규칙 설정 -- " , font=my_font, width=250) + self.file_name_label.grid(row=3, column=0, pady=5,sticky="n") + self.radio1 = customtkinter.CTkRadioButton(self.advanced_setting_frame, text="생성시간.png : 20231230_123001.png", variable=app.name_var, value="time", font=my_font) + self.radio1.grid(row=4, column=0) + self.radio2 = customtkinter.CTkRadioButton(self.advanced_setting_frame, text="생성순서.png : 00001.png,00002.png", variable=app.name_var, value="count", font=my_font) + self.radio2.grid(row=5, column=0) + self.file_name_label2 = customtkinter.CTkLabel(self.advanced_setting_frame, text="*생성순서 적용시 날짜_시간 폴더에 저장" , font=my_font, width=250) + self.file_name_label2.grid(row=6, column=0, pady=5,sticky="n") + + self.protocol("WM_DELETE_WINDOW", lambda: on_close(self)) + +class Automation_setting(customtkinter.CTkToplevel): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.title("Automation 종료/지연 설정") + + self.var = customtkinter.StringVar() + self.attributes('-topmost', True) + my_font = customtkinter.CTkFont('Pretendard', 13) + + def show_option(self): + if self.var.get() == 'timer': + self.timer_label.grid(row=4, column=0, pady=5) + self.timer_entry.grid(row=4, column=1, pady=5) + self.windows_shutdown_check.grid(row=5, column=0, columnspan=3, sticky="n", pady=5) + self.count_label.grid_forget() + self.count_entry.grid_forget() + elif self.var.get() == 'count': + self.count_label.grid(row=4, column=0, pady=5) + self.count_entry.grid(row=4, column=1, pady=5) + self.windows_shutdown_check.grid(row=5, column=0, columnspan=3, sticky="n", pady=5) + self.timer_label.grid_forget() + self.timer_entry.grid_forget() + else: + self.timer_label.grid_forget() + self.timer_entry.grid_forget() + self.count_label.grid_forget() + self.count_entry.grid_forget() + self.windows_shutdown_check.grid_forget() + + def on_close(self): + self.withdraw() + + self.upper_frame = customtkinter.CTkFrame(self, fg_color="#242424") + self.upper_frame.grid(row=0, column=0, columnspan=3, sticky="n") + + self.label_time = customtkinter.CTkLabel(self.upper_frame, text="이미지 생성당 지연시간 추가 (초)", font = my_font) + self.label_time.grid(row=0, column=0, columnspan=3, sticky="n") + + self.delay_entry = customtkinter.CTkEntry(self.upper_frame, font = my_font) + self.delay_entry.grid(row=1, column=0,columnspan=3, sticky="n") + + self.repeat_frame = customtkinter.CTkFrame(self, fg_color="#242424") + self.repeat_frame.grid(row=2, column=0, columnspan=3, sticky="n") + + self.label_repeat = customtkinter.CTkLabel(self.repeat_frame, text="동일 이미지를 다음 횟수만큼 반복하여 생성 (Default=1)", font = my_font) + self.label_repeat.grid(row=0, column=0, columnspan=3, sticky="n") + + self.repeat_entry = customtkinter.CTkEntry(self.repeat_frame, font = my_font, width=70) + self.repeat_entry.grid(row=1, column=0, columnspan=2, sticky="e") + + def set_wc_hold(): + if self.repeat_wildcard_hold_var.get() == 1: + app.hold_wildcard = True + else: + app.hold_wildcard = False + + self.repeat_wildcard_hold_var = customtkinter.IntVar() + self.repeat_wildcard_hold = customtkinter.CTkCheckBox(self.repeat_frame, font = my_font, text="와일드카드 고정", variable=self.repeat_wildcard_hold_var, command=set_wc_hold, border_color="grey10") + self.repeat_wildcard_hold.grid(row=1, column=2, sticky="n") + + self.label_wildcard_repeat = customtkinter.CTkLabel(self.repeat_frame, text="와일드카드 *사전개봉*시 다음 횟수만큼 연속으로 할당 (Default=1)", font = my_font) + self.label_wildcard_repeat.grid(row=2, column=0, columnspan=3, sticky="n") + + self.wildcard_repeat_entry = customtkinter.CTkEntry(self.repeat_frame, font = my_font, width=70) + self.wildcard_repeat_entry.grid(row=3, column=0,columnspan=3, sticky="n") + + self.label = customtkinter.CTkLabel(self.repeat_frame, text="자동화 종료 조건", font = my_font) + self.label.grid(row=4, column=0, columnspan=3, sticky="n") + + self.radio1 = customtkinter.CTkRadioButton(self, text="무제한", variable=self.var, value="unlimited", command=lambda: show_option(self), font = my_font) + self.radio1.grid(row=3, column=0) + self.radio2 = customtkinter.CTkRadioButton(self, text="타이머", variable=self.var, value="timer", command=lambda: show_option(self), font = my_font) + self.radio2.grid(row=3, column=1) + self.radio3 = customtkinter.CTkRadioButton(self, text="생성카운트", variable=self.var, value="count", command=lambda: show_option(self), font = my_font) + self.radio3.grid(row=3, column=2) + + self.timer_label = customtkinter.CTkLabel(self, text="자동화 동작 시간(분) : ", font = my_font) + self.timer_entry = customtkinter.CTkEntry(self) + + self.count_label = customtkinter.CTkLabel(self, text="자동 생성 횟수 : ", font = my_font) + self.count_entry = customtkinter.CTkEntry(self) + + self.windows_shutdown = customtkinter.IntVar() + self.windows_shutdown_check = customtkinter.CTkCheckBox(self, text="자동화가 자동으로 끝나면 윈도우를 종료합니다.", variable=self.windows_shutdown, font = my_font) + + self.apply_button = customtkinter.CTkButton(self, text="적용", font = my_font, command=lambda: on_apply(self)) + self.stop_button = customtkinter.CTkButton(self, text="중단", font = my_font, command=lambda: on_stop(self)) + self.close_button = customtkinter.CTkButton(self, text="닫기", font = my_font, command=lambda: on_close(self)) + + self.apply_button.grid(row=6, column=0, pady=5) + self.stop_button.grid(row=6, column=1, pady=5) + self.close_button.grid(row=6, column=2, pady=5) + + def on_apply(self): + selected_option = self.var.get() + if self.delay_entry.get(): + try: + app.delay_offset = round(float(self.delay_entry.get()), 1) + if app.delay_offset > 60: + app.delay_offset = 60 + elif app.delay_offset < -8: + app.delay_offset = -8 + app.automation_setting_button.configure(text=f"자동화 설정 ({str(round(app.delay_offset, 1))}초)") + except ValueError as e: + print(e) + app.delay_offset = 0 + else: + app.delay_offset = 0 + app.automation_setting_button.configure(text=f"자동화 설정") + if self.repeat_entry.get(): + try: + app.image_generation_repeat = int(self.repeat_entry.get()) + except: + self.repeat_entry.delete(0, "end") + app.image_generation_repeat = 1 + if self.wildcard_repeat_entry.get(): + try: + app.wildcard_preopen_repeat = int(self.wildcard_repeat_entry.get()) + app.wildcard_preopen_repeat_current = app.wildcard_preopen_repeat + except: + self.wildcard_repeat_entry.delete(0, "end") + app.image_generation_repeat = 1 + if selected_option == "timer": + app.auto_time_left_flag = True + app.auto_count_left_flag = False + try: + # 엔트리에서 입력된 시간을 분 단위에서 초 단위로 변환 + app.auto_time_left = int(self.timer_entry.get()) * 60 + except ValueError: + # 잘못된 입력 처리 + print("Invalid input for timer. Please enter a number.") + return + elif selected_option == "count": + app.auto_count_left_flag = True + app.auto_time_left_flag = False + try: + app.auto_count_left = int(self.count_entry.get()) + except ValueError: + # 잘못된 입력 처리 + print("Invalid input for count. Please enter a number.") + return + else: + self.auto_time_left_flag = False + self.auto_count_left_flag = False + on_stop(self) # 이전 스레드 종료 + start_auto_thread() # 새 스레드 시작 + + def show_shutdown_warning(): + def cancel_shutdown(): + os.system("shutdown -a") + warning_window.destroy() + + def on_warning_window_close(): + if warning_window.after_id is not None: + app.after_cancel(warning_window.after_id) + warning_window.destroy() + + app.deiconify() + app.lift() + + warning_window = customtkinter.CTkToplevel(app) + warning_window.title("Shutdown Warning") + warning_window.attributes("-topmost", True) + warning_window.protocol("WM_DELETE_WINDOW", on_warning_window_close) + warning_window.after_id = None + + label = customtkinter.CTkLabel(warning_window, text=" 프로그램 및 컴퓨터가 90/120초 후에 종료됩니다. ", font=my_font) + label.pack(pady=15) + + cancel_button = customtkinter.CTkButton(warning_window, text="취소", command=cancel_shutdown, font=my_font) + cancel_button.pack(pady=5) + + # 90초 후에 프로그램 종료 + warning_window.after_id = warning_window.after(90000, app.exit_program) + + def shutdown_computer(): + os.system("shutdown /s /t 120") + show_shutdown_warning() + + def auto_time_thread(stop_event): + def seconds_to_hms(seconds): + h = seconds // 3600 + m = (seconds % 3600) // 60 + s = seconds % 60 + if h != 0: + if (s%2) == 0: + return f"{h:02d}:{m:02d}" + else: + return f"{h:02d} {m:02d}" + else: + if (s%2) == 0: + return f"{m:02d}:{s:02d}" + else: + return f"{m:02d} {s:02d}" + + def update_label_for_time_finished(): + app.automation_button.configure(text="자동화 (종료됨)") + app.auto_time_left_flag = False + app.automation_button.deselect() + if self.windows_shutdown.get() == 1: + shutdown_computer() + else: + notification.notify( + title = '자동생성이 완료되었어요.', + message = '결과를 확인해 보세요.', + app_name = "NAIA", + app_icon = app.basedir+'/icoico.ico', + timeout = 5, # seconds + ) + + while not stop_event.is_set() and app.auto_time_left > 0: + time.sleep(1) + app.auto_time_left -= 1 + remaining_time = seconds_to_hms(app.auto_time_left) + app.after(0, lambda: app.automation_button.configure(text=f"자동화 ({remaining_time})")) + + if app.auto_time_left <= 0: + app.after(0, update_label_for_time_finished) + + if app.stop_event.is_set(): + app.after(0, lambda: app.automation_button.configure(text="자동화")) + app.auto_time_left_flag = False + + def auto_count_thread(stop_event): + def update_label_for_count_finished(): + app.automation_button.configure(text="자동화 (종료됨)") + app.auto_time_left_flag = False + app.automation_button.deselect() + if self.windows_shutdown.get() == 1: + shutdown_computer() + else: + notification.notify( + title = '자동생성이 완료되었어요.', + message = '결과를 확인해 보세요.', + app_name = "NAIA", + app_icon = app.basedir+'/icoico.ico', + timeout = 5, # seconds + ) + + while not stop_event.is_set(): + time.sleep(1) # 주기적으로 확인 + app.after(0, lambda: app.automation_button.configure(text=f"자동화 ({app.auto_count_left})")) + if app.auto_count_left <= 0: + app.after(0, update_label_for_count_finished) + break # 루프 탈출 + if app.stop_event.is_set(): + app.after(0, lambda: app.automation_button.configure(text="자동화")) + app.auto_count_left_flag = False + + # 스레드 시작 및 종료 함수 + def start_auto_thread(): + app.stop_event = threading.Event() + app.auto_thread = None + + if app.auto_time_left_flag: + app.auto_thread = threading.Thread(target=auto_time_thread, args=(app.stop_event,), daemon=True) + elif app.auto_count_left_flag: + app.auto_thread = threading.Thread(target=auto_count_thread, args=(app.stop_event,), daemon=True) + + if app.auto_thread is not None: + app.auto_thread.start() + + def stop_auto_thread(self): + if app.stop_event is not None: + app.stop_event.set() + + def on_stop(self): + stop_auto_thread(self) + + self.protocol("WM_DELETE_WINDOW", lambda: on_close(self)) + +class Character_search(customtkinter.CTkToplevel): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.title("캐릭터 검색 및 사전") + #self.resizable(width=False, height=False) + self.app = app + self.attributes('-topmost', True) + my_font = customtkinter.CTkFont('Pretendard', 13) + + character_book_dict = {} + favorite_characters = {} + try: + if os.path.exists('Character_book.json'): + with open('Character_book.json', 'r', encoding='utf-8') as f: + try: + character_book_dict = json.load(f) + self.dictionary_parity = True + except: + character_book_dict = {} + except FileNotFoundError: + character_book_dict = {} + try: + if os.path.exists('favorite_characters.json'): + with open('favorite_characters.json', 'r', encoding='utf-8') as f: + try: + favorite_characters = json.load(f) + except: + favorite_characters = {} + except FileNotFoundError: + favorite_characters = {} + + def lost_top_most(self): + self.attributes('-topmost', False) + + def on_close(self): + self.withdraw() + + frame_head = customtkinter.CTkFrame(self, width=750, height=40) + frame_head.grid(row =0, column =0, columnspan=5, padx=5, pady=5, sticky="nsew") + frame_left = customtkinter.CTkFrame(self, width=300, height=520) + frame_left.grid(row =1, column =0, padx=5, pady=5, sticky="nsew") + frame_right = customtkinter.CTkFrame(self, width=450, height=520) + frame_right.grid(row =1, column =2, padx=5, pady=5, sticky="nsew") + + search_word = customtkinter.StringVar() + search_entry = customtkinter.CTkEntry(frame_head, font=my_font, textvariable=search_word) + + character_prompt_frame = customtkinter.CTkFrame(frame_right) + character_prompt_frame.grid(row=0, column=0, padx=10, pady=5, sticky="nsew") + prompt_label = customtkinter.CTkLabel(character_prompt_frame, text="캐릭터 프롬프트: ", font=my_font) + prompt_label.grid(row=0, column=0, padx=10, pady=5, sticky="w") + + def generate_character(self): + prompt = character_prompt.get("0.0", "end-1c") + prompt_cos = cosplay_prompt.get("0.0", "end-1c") + concat_entry = generate_entry.get() + ", {{face focus, upper body}}" + keywords = [item.strip() for item in prompt.split(',')] + if len(keywords) >= 2: + keyword = keywords[1] + keywords[1] = '{{'+keyword+'}}' + keywords.insert(2, concat_entry) + else: + keyword = keywords[0] + keywords[0] = "{{" + keywords[0] + "}}" + request_prompt = ', '.join(keywords) + instant_image_generation_book(self, request_prompt, keyword, prompt, prompt_cos) + + character_prompt_generation = customtkinter.CTkButton(character_prompt_frame, font=my_font, text="즉시생성", fg_color="grey", hover_color="grey10", command=lambda: generate_character(self)) + character_prompt_generation.grid(row=0, column=1, padx=5, pady=5, sticky="w") + continuous_generation = customtkinter.IntVar() + continuous_generation_button = customtkinter.CTkCheckBox(character_prompt_frame, font=my_font, text="빈 도감 자동채우기", variable=continuous_generation) + continuous_generation_button.grid(row=0, column=2, padx=5, pady=5, sticky="w") + + character_prompt = customtkinter.CTkTextbox(frame_right, font=customtkinter.CTkFont('Pretendard', 15), width=450) + character_prompt.grid(row=1, column=0, padx=10, pady=5, sticky="w") + character_prompt.tag_config("yellow_text", foreground="yellow") + + cosplay_prompt_frame = customtkinter.CTkFrame(frame_right) + cosplay_prompt_frame.grid(row=2, column=0, padx=10, pady=5, sticky="nsew") + prompt_label = customtkinter.CTkLabel(cosplay_prompt_frame, text="캐릭터(Cosplay)):", font=my_font) + prompt_label.grid(row=0, column=0, padx=10, pady=5, sticky="w") + #cosplay_prompt_generation = customtkinter.CTkButton(cosplay_prompt_frame, font=my_font, text="즉시생성", fg_color="grey", hover_color="grey10", command=lambda: app.instant_image_generation_book()) + #cosplay_prompt_generation.grid(row=0, column=1, padx=5, pady=5, sticky="w") + cosplay_prompt = customtkinter.CTkTextbox(frame_right, font=customtkinter.CTkFont('Pretendard', 15), width=450) + cosplay_prompt.grid(row=3, column=0, padx=10, pady=5, sticky="w") + image_label = customtkinter.CTkLabel(frame_right, text="") + image_label.grid(row=0, rowspan=4, column=1, padx=10, pady=5, sticky="w") + white_image = Image.new('RGB', (384, 512), 'white') + white_photo = customtkinter.CTkImage(white_image, size=(384,512)) + image_label.configure(image=white_photo) + generate_label = customtkinter.CTkLabel(self, text="Portrait 생성시 적용할 선행 고정 프롬프트 : ", font=my_font) + generate_label.grid(row=2, column=0, padx=5, pady=5, sticky="w") + generate_entry = customtkinter.CTkEntry(self, font=my_font) + generate_entry.grid(row=2, column=1, columnspan=2, padx=5, pady=5, sticky="nsew") + + if character_book_dict != None and 'user_fix_prompt' in character_book_dict and character_book_dict['user_fix_prompt'] != None: + generate_entry.delete(0, "end") + generate_entry.insert(0, character_book_dict['user_fix_prompt']) + + def instant_image_generation_book(self, request_prompt, character_name, p1, p2): + if app.running_flag == False and app.automation_button.get() == 0: + character_prompt_generation.configure(state="disabled") + #cosplay_prompt_generation.configure(state="disabled") + app.running_flag == True + try: + scale_pre = float(scale_pre) + except: + scale_pre = 5.0 + app.cfg_scale_var.set("5.0") + rescale_pre = app.prompt_guidance_rescale_entry.get() + try: + rescale_pre = float(rescale_pre) + except: + rescale_pre = 0 + app.prompt_guidance_rescale_var.set("0") + uncond_pre = app.uncond_strength_entry.get() + try: + uncond_pre = float(uncond_pre) + uncond_pre = round(uncond_pre / 0.05) * 0.05 + if (uncond_pre > 1.5): + uncond_pre = 1.5 + app.uncond_strength_entry.delete(0, "end") + app.uncond_strength_entry.insert(0, "1.5") + except: + uncond_pre = 1.0 + app.uncond_strength_entry.delete(0, "end") + app.uncond_strength_entry.insert(0, "1.0") + gen_request = { + "width":896, + "height":1152, + "quality_toggle":app.auto_quality_toggle.get(), + "seed":random.randint(0,9999999999), + "sampler":app.sampler_button.get(), + "scale":scale_pre, + "sema":app.sema_button.get(), + "sema_dyn": app.dyn_button.get(), + "cfg_rescale": rescale_pre, + "uncond_scale":uncond_pre, + "prompt": request_prompt, + "negative":app.negative_prompt_input.get("0.0", "end-1c"), + "user_screen_size": app.get_max_size(), + "start_time": app.start_time, + "access_token": app.access_token, + "save_folder": app.output_file_path, + "png_rule": app.name_var.get(), + "type": "normal" + } + def run_generation(): + if gen_request["png_rule"] == "count": + app.generation_count += 1 + gen_request["count"] =app.generation_count + app.state_label.configure(text ="state : 도감 이미지 요청됨 ", text_color = "#FFFF97") + result_image, result_prompt, result_seed, info, filename = NAIA_generation.generate(gen_request) + app.running_flag == False + character_prompt_generation.configure(state="normal") + #cosplay_prompt_generation.configure(state="normal") + app.state_label.configure(text ="state : 도감 이미지 요청 반환됨", text_color = "#DCE4EE") + if info: + temp = info.get('Comment', '') + temp = temp[temp.find("prompt")+10:temp.find("skip_cfg_below_sigma")-3].replace('"','') + else: + temp = result_prompt + app.image_label_report.configure(state="normal") + app.image_label_report.delete("0.0", "end") + app.image_label_report.configure(text_color="#DCE4EE") + app.image_label_report.insert("0.0", temp) + app.image_label_report.configure(state="disabled") + if result_image: + image_bytes = io.BytesIO() + book_image = Image.open(filename).resize((384,512)) + book_image = book_image.convert('RGB') + book_image.save(image_bytes, format='JPEG', quality=90) + image_bytes = base64.b64encode(image_bytes.getvalue()).decode('utf-8') + character_book_dict[character_name] = [0, p1, p2, image_bytes] + character_book_dict['user_fix_prompt'] = generate_entry.get() + fav_button.configure(state="normal") + with open('Character_book.json', 'w', encoding='utf-8') as f: + json.dump(character_book_dict, f, ensure_ascii=False, indent=4) + image_stream = io.BytesIO(base64.b64decode(image_bytes)) + reloaded_image = Image.open(image_stream) + image_label.configure(image=customtkinter.CTkImage(reloaded_image, size=(384,512))) + if app.state() != 'zoomed': + instant_result_image = customtkinter.CTkImage(result_image, size=(620,620)) + else: + current_image = Image.open(filename) + original_width, original_height = current_image.size + max_size = app.winfo_screenheight()-100 + if original_width > max_size or original_height > max_size: + new_image = Image.new("RGB", (max_size, max_size), "black") + new_image.paste(current_image, ((max_size - original_width) // 2, (max_size - original_height) // 2)) + instant_result_image = customtkinter.CTkImage(new_image, size=(max_size, max_size)) + else: + instant_result_image = customtkinter.CTkImage(current_image, size=(max_size, max_size)) + app.image_label.configure(image=instant_result_image) + app.ext_set_image_to_queue(result_image, result_prompt, str(result_seed), filename) + if continuous_generation.get() == 1: + listbox.focus() + app.after(500, move_selection_down(self)) + else: + if continuous_generation.get() == 1: + app.after(500, generate_character(self)) + generation_thread = threading.Thread(target=run_generation, daemon=True) + generation_thread.start() + + def move_selection_down(self): + selected_indices = listbox.curselection() + if selected_indices: + current_index = selected_indices[0] + next_index = current_index + 1 + + # 리스트의 끝을 넘어가지 않도록 확인 + if next_index < listbox.size(): + listbox.selection_clear(current_index) + listbox.selection_set(next_index) + listbox.see(next_index) + else: + continuous_generation_button.deselect() + keyword_with_count = listbox.get(next_index).strip() + keyword = keyword_with_count.split(' - ')[0].strip() + if keyword not in character_book_dict and continuous_generation.get() == 1: + on_search() + app.after(500, generate_character(self)) + elif continuous_generation.get() == 1: + app.after(500, move_selection_down(self)) + + def on_key_release(event): + search_keyword = search_entry.get() + update_listbox(search_keyword) + + def on_search(): + # 검색 버튼 이벤트 처리 함수 + selected_indices = listbox.curselection() + if not selected_indices: # If there is no selection + print("No selection made.") + return + + # Assuming the first selected index (single selection mode) + selected_index = selected_indices[0] + # Get the keyword from the listbox, split by '-', and trim whitespace + keyword_with_count = listbox.get(selected_index).strip() + keyword = keyword_with_count.split(' - ')[0].strip() + + # Call the analyze function with the selected keyword + print(f"Analyzing keyword: {keyword}") + character, cosplay = analyze_keywords_in_data('csdataset.parquet', keyword) + character_prompt.delete("0.0", "end") + character_prompt.insert("0.0", character) + cosplay_prompt.delete("0.0", "end") + cosplay_prompt.insert("0.0", cosplay) + + def analyze_keywords_in_data(parquet_file, keyword): + # Parquet 파일 읽기 + df = pd.read_parquet(parquet_file) + + bag_of_tags = ['penis', 'character name', 'upper body','holding','full body', 'artist name', 'male focus', 'open mouth', 'mature male', 'muscular', 'muscular male', 'closed mouth','closed eyes', 'white background', 'solo', 'breasts', 'simple background', 'smile', 'looking at viewer', 'flat chest', 'small breasts', 'medium breasts', 'large breasts', 'huge breasts','aqua eyes', 'black eyes', 'blue eyes', 'brown eyes', 'green eyes', 'grey eyes', 'orange eyes', 'purple eyes', 'pink eyes', 'red eyes', 'white eyes', 'yellow eyes', 'amber eyes', 'heterochromia', 'multicolored eyes', 'aqua pupils', 'blue pupils', 'brown pupils', 'green pupils', 'grey pupils', 'orange pupils', 'pink pupils', 'purple pupils', 'red pupils', 'white pupils', 'yellow pupils', 'pointy ears', 'long pointy ears', 'aqua hair', 'black hair', 'blonde hair', 'blue hair', 'light blue hair', 'dark blue hair', 'brown hair', 'light brown hair', 'green hair', 'dark green hair', 'light green hair', 'grey hair', 'orange hair', 'pink hair', 'purple hair', 'light purple hair', 'red hair', 'white hair', 'multicolored hair', 'colored inner hair', 'colored tips', 'roots (hair)', 'gradient hair', 'print hair', 'rainbow hair', 'split-color hair', 'spotted hair', 'streaked hair', 'two-tone hair', 'very short hair', 'short hair', 'medium hair', 'long hair', 'very long hair', 'absurdly long hair', 'big hair', 'bald', 'bald girl', 'bob cut', 'inverted bob', 'bowl cut', 'buzz cut', 'chonmage', 'crew cut', 'flattop', 'okappa', 'pixie cut', 'undercut', 'flipped hair', 'wolf cut', 'cornrows', 'dreadlocks', 'hime cut', 'mullet', 'bow-shaped hair', 'braid', 'braided bangs', 'front braid', 'side braid', 'french braid', 'crown braid', 'single braid', 'multiple braids', 'twin braids', 'low twin braids', 'tri braids', 'quad braids', 'flower-shaped hair', 'hair bun', 'braided bun', 'single hair bun', 'double bun', 'cone hair bun', 'doughnut hair bun', 'heart hair bun', 'triple bun', 'cone hair bun', 'hair rings', 'single hair ring', 'half updo', 'one side up', 'two side up', 'low-braided long hair', 'low-tied long hair', 'mizura', 'multi-tied hair', 'nihongami', 'ponytail', 'folded ponytail', 'front ponytail', 'high ponytail', 'short ponytail', 'side ponytail', 'split ponytail', 'star-shaped hair', 'topknot', 'twintails', 'low twintails', 'short twintails', 'uneven twintails', 'tri tails', 'quad tails', 'quin tails', 'twisted hair', 'afro', 'huge afro', 'beehive hairdo', 'crested hair', 'pompadour', 'quiff', 'shouten pegasus mix mori', 'curly hair', 'drill hair', 'twin drills', 'tri drills', 'hair flaps', 'messy hair', 'pointy hair', 'ringlets', 'spiked hair', 'straight hair', 'wavy hair', 'bangs', 'arched bangs', 'asymmetrical bangs', 'bangs pinned back', 'blunt bangs', 'crossed bangs', 'diagonal bangs', 'dyed bangs', 'fanged bangs', 'hair over eyes', 'hair over one eye', 'long bangs', 'parted bangs', 'curtained hair', 'ribbon bangs', 'short bangs', 'swept bangs', 'hair between eyes', 'hair intakes', 'single hair intake', 'sidelocks', 'asymmetrical sidelocks', 'drill sidelocks', 'low-tied sidelocks', 'sidelocks tied back', 'single sidelock', 'ahoge', 'heart ahoge', 'huge ahoge', 'antenna hair', 'heart antenna hair', 'comb over', 'hair pulled back', 'hair slicked back', 'mohawk', 'oseledets', 'lone nape hair', 'hair bikini', 'hair censor', 'hair in own mouth', 'hair over breasts', 'hair over one breast', 'hair over crotch', 'hair over shoulder', 'hair scarf', 'alternate hairstyle', 'hair down', 'hair up', 'asymmetrical hair', 'sidecut', 'blunt ends', 'dark skin', 'dark-skinned female', 'pale skin', 'sun tatoo', 'black skin', 'blue skin', 'green skin', 'grey skin', 'orange skin', 'pink skin', 'purple skin', 'red skin', 'white skin', 'yellow skin', 'colored skin', 'multiple tails', 'demon tail', 'dragon tail', 'ghost tail', 'pikachu tail', 'snake head tail', 'fiery tail', 'bear tail', 'rabbit tail', 'cat tail', 'cow tail', 'deer tail', 'dog tail', 'ermine tail', 'fox tail', 'horse tail', 'leopard tail', 'lion tail', 'monkey tail', 'mouse tail', 'pig tail', 'sheep tail', 'squirrel tail', 'tiger tail', 'wolf tail', 'crocodilian tail', 'fish tail', 'scorpion tail', 'snake tail', 'tadpole tail'] + mini_bag_of_tags = ['penis', 'character name', 'upper body', 'full body', 'alternate costume','artist name', 'male focus', 'open mouth', 'mature male', 'muscular', 'muscular male', 'closed mouth','closed eyes','white background', 'solo', 'breasts', 'simple background', 'smile', 'looking at viewer'] + # 'character' 열에서 keyword에 해당하는 행만 필터링 + filtered_df = df[df['character'] == keyword] + filtered_df = filtered_df[~filtered_df['character'].str.contains(',', na=False)] + if filtered_df.empty: + filtered_df = df[df['character'].str.contains(keyword, na=False, regex=False)] + filtered_df = filtered_df[~(filtered_df['rating'] == 'e')] + #print(len(filtered_df)) + # keyword:count 형태로 데이터 집계할 딕셔너리 생성 + keyword_count_dict = {} + + # 각 행의 'general' 열 처리 + for general in filtered_df['general']: + # 문자열 분할 + tags = [tag.strip() for tag in general.split(',')] + + # 딕셔너리에 각 태그의 count 추가 + for tag in tags: + keyword_count_dict[tag] = keyword_count_dict.get(tag, 0) + 1 + + # 특정 키워드 제외 + exclude_keywords = bag_of_tags + keyword_count_dict1 = keyword_count_dict.copy() + keyword_count_dict1 = {k: v for k, v in keyword_count_dict1.items() if k not in mini_bag_of_tags} + keyword_count_dict = {k: v for k, v in keyword_count_dict.items() if k not in exclude_keywords} + + # 가장 높은 count 값 찾기 + highest_count = max(keyword_count_dict.values()) if keyword_count_dict else 0 + highest_count1 = max(keyword_count_dict1.values()) if keyword_count_dict1 else 0 + + if(len(filtered_df)) < 10: + weight = 0.6 + elif(len(filtered_df)) < 30: + weight = 0.5 + elif(len(filtered_df)) < 80: + weight = 0.45 + elif(len(filtered_df)) < 160: + weight = 0.35 + elif(len(filtered_df)) < 280: + weight = 0.3 + else: + weight = 0.2 + + result_list = [] + result_list1 = [] + # count가 highest_count*0.4 이상인 키워드 출력 + for k, v in keyword_count_dict.items(): + if '|' in k: continue + if v >= highest_count * weight: + result_list.append(k) + + for k, v in keyword_count_dict1.items(): + if '|' in k: continue + if v >= highest_count1 * weight: + result_list1.append(k) + + result_list1.insert(1, keyword) + result_list.insert(1, "alternative costume, ["+keyword+" (cosplay)]") + return ", ".join(result_list1),", ".join(result_list) + + def update_listbox(search_keyword): + # Clear the listbox + if listbox.size() > 0: + listbox.delete(0, "end") + + # List to hold all matching keywords and counts + all_matching_keywords = [] + + # Set to track added keywords to avoid duplicates + added_keywords = set() + + # Check if the search keyword is not empty and has at least 3 characters + if search_keyword and len(search_keyword) >= 3: + # Search in copyright_dict first + for key, keywords in copyright_dict_0103.copyright_dict.items(): + if search_keyword.lower() in key.lower(): + for keyword in keywords: + count = cd.character_dictionary.get(keyword, 0) + if count > 50 and keyword not in added_keywords: + all_matching_keywords.append((keyword, count)) + added_keywords.add(keyword) + + # Then search in character_dictionary + matching_keywords = [ + (k, v) for k, v in cd.character_dictionary.items() + if search_keyword.lower() in k.lower() and v > 50 and k not in added_keywords + ] + all_matching_keywords.extend(matching_keywords) + + else: + # Sort all keywords by count in descending order + all_matching_keywords = [ + (k, v) for k, v in cd.character_dictionary.items() if v > 50 and k not in added_keywords + ] + + # Now sort all matching keywords by count in descending order and insert into listbox + all_matching_keywords.sort(key=lambda item: item[1], reverse=True) + for keyword, count in all_matching_keywords: + listbox.insert("end", f"{keyword} - {count}") + + search_entry.grid(row =0, column =0, padx=5, pady=5, sticky="nsew") + search_entry.bind("", on_key_release) + search_label = customtkinter.CTkLabel(frame_head, text="◀ 검색 대상 copyright/character 입력 (English) | 성능 이슈로 Danbooru 50장 이상만 표시", font=my_font) + search_label.grid(row =0, column =1, padx=5, pady=5, sticky="w") + self.after(2500, lambda: self.attributes('-topmost', False)) + + def show_image(event): + highlight_tags = ["aqua","black","blonde","blue","brown","cyan","green","grey","orange","pink","purple","red","violet","white","yellow","mouth", "eyes", " cap", " ears", " girl", " ornament", " hat", "beret", " ear "] + + def insert_with_color(cs_text_input, current_lookup): + words = [word.strip() for word in current_lookup.split(',')] + for index, word in enumerate(words): + highlight = False + start_index = cs_text_input.index("end-1c") + if index == len(words) - 1: # 마지막 원소인 경우 + cs_text_input.insert("end", word) + else: + cs_text_input.insert("end", word + ", ") + end_index = cs_text_input.index("end-1c") + + for tag in highlight_tags: + if tag in word: + highlight = True + if word == "hat": highlight = True + # 조건 확인 + if word == keyword or word in tagbag.bag_of_tags[5:] or 'tail' in word or 'pupil' in word: + cs_text_input.tag_add(word, start_index, end_index) + cs_text_input.tag_config(word, foreground="#FFFF97") + elif highlight: + cs_text_input.tag_add(word, start_index, end_index) + cs_text_input.tag_config(word, foreground="#AED395") + selected_indices = listbox.curselection() + selected_index = selected_indices[0] + keyword_with_count = listbox.get(selected_index).strip() + keyword = keyword_with_count.split(' - ')[0].strip() + if keyword in character_book_dict: + try: + image_stream = io.BytesIO(base64.b64decode(character_book_dict[keyword][3])) + reloaded_image = Image.open(image_stream) + image_label.configure(image=customtkinter.CTkImage(reloaded_image, size=(384,512))) + character_prompt.delete("0.0", "end") + insert_with_color(character_prompt, character_book_dict[keyword][1]) + cosplay_prompt.delete("0.0", "end") + insert_with_color(cosplay_prompt, character_book_dict[keyword][2]) + try: + fav_rem_button.grid_forget() + fav_button.grid(row = 0, column=0, padx=5, pady=5, sticky="n") + except: + pass + fav_button.configure(state="normal") + if keyword in favorite_characters: + fav_button.grid_forget() + fav_rem_button.grid(row = 0, column=0, padx=5, pady=5, sticky="n") + except: + return + else: + if character_prompt.get("0.0","end") != None: + character_prompt.delete("0.0", "end") + if cosplay_prompt.get("0.0","end") != None: + cosplay_prompt.delete("0.0", "end") + fav_button.configure(state="disabled") + image_label.configure(image=white_photo) + + listbox = tk.Listbox(frame_left, width=32, height=30, font = font.Font(family='Pretendard', size=13), bg='#2B2B2B', fg='#F8F8F8', borderwidth=2, highlightbackground='lightgrey') + listbox.bind('<>', show_image) + listbox.grid(row = 0, column=0, padx=5, pady=5, sticky="nsew") + + search_frame = customtkinter.CTkFrame(self) + search_frame.grid(row = 1, column=1, padx=5, pady=140, sticky="w") + + def add_favorite(): + selected_indices = listbox.curselection() + selected_index = selected_indices[0] + keyword_with_count = listbox.get(selected_index).strip() + keyword = keyword_with_count.split(' - ')[0].strip() + words = [word.strip() for word in character_prompt.get("0.0","end").split(',')] + description = [] + for word in words: + if word == keyword or word in tagbag.bag_of_tags[5:] or 'tail' in word or 'pupil' in word: + description.append(word) + favorite_characters[keyword] = ', '.join(description) + with open('favorite_characters.json', 'w', encoding='utf-8') as f: + json.dump(favorite_characters, f, ensure_ascii=False, indent=4) + with open('wildcards/favorite_character.txt', 'w') as file: + for value in favorite_characters.values(): + file.write('100:' + value + '\n') + fav_button.grid_forget() + fav_rem_button.grid(row = 0, column=0, padx=5, pady=5, sticky="n") + character_prompt.delete("0.0", "end") + character_prompt.insert("0.0", f"favorite_character 와일드카드에 캐릭터 정보가 추가되었습니다 : {favorite_characters[keyword]}", "yellow_text") + fav_button.configure(state="disabled") + + def rem_favorite(): + selected_indices = listbox.curselection() + selected_index = selected_indices[0] + keyword_with_count = listbox.get(selected_index).strip() + keyword = keyword_with_count.split(' - ')[0].strip() + del favorite_characters[keyword] + with open('favorite_characters.json', 'w', encoding='utf-8') as f: + json.dump(favorite_characters, f, ensure_ascii=False, indent=4) + with open('wildcards/favorite_character.txt', 'w') as file: + for value in favorite_characters.values(): + file.write('100:' + value + '\n') + fav_rem_button.grid_forget() + fav_button.grid(row = 0, column=0, padx=5, pady=5, sticky="n") + + + fav_button = customtkinter.CTkButton(search_frame, font=my_font, text="Favorite 등록", state="disabled", command=add_favorite) + fav_button.grid(row = 0, column=0, padx=5, pady=5, sticky="n") + + fav_rem_button = customtkinter.CTkButton(search_frame, font=my_font, text="Favorite 제거", command=rem_favorite, fg_color="#FFFFFF", hover_color="grey", text_color="#FF362B") + + search_button = customtkinter.CTkButton(search_frame, font=my_font, text="조회", fg_color="grey", hover_color="grey10", command=on_search) + search_button.grid(row = 1, column=0, padx=5, pady=5, sticky="n") + + all_keywords = sorted( + ((k, v) for k, v in cd.character_dictionary.items() if v > 50), + key=lambda item: item[1], + reverse=True + ) + for keyword, count in all_keywords: + listbox.insert("end", f"{keyword} - {count}") + + self.protocol("WM_DELETE_WINDOW", lambda: on_close(self)) + + collection_button = customtkinter.CTkButton(frame_head, text="Copyright 도감", font=my_font, fg_color="grey", hover_color="grey10", command=lambda: open_collection(self)) + collection_button.grid(row =0, column =2, padx=50, pady=5, sticky="e") + + #add_custom_character_button = customtkinter.CTkButton(frame_head, text="Copyright 도감", font=my_font, fg_color="grey", hover_color="grey10", command=lambda: open_collection(self)) + #add_custom_character_button.grid(row =0, column =3, padx=50, pady=5, sticky="e") + + def open_collection(self): + self.attributes('-topmost', False) + collection_window = customtkinter.CTkToplevel() + collection_window.title("도감") + collection_window.attributes('-topmost', True) + collection_window.resizable(width=False, height=False) + + _height = app.winfo_screenheight() + print(_height) + if _height < 1152: + yview = 660 + ylist = 46 + else: + yview = 700 + ylist = 50 + + collection_frame_head = customtkinter.CTkFrame(collection_window, width=1220, height=40) + collection_frame_head.grid(row =0, column =0, columnspan=5, padx=5, pady=5, sticky="nsew") + + collection_label1 = customtkinter.CTkLabel(collection_frame_head, font=my_font, text="이름을 눌러 특징을 복사합니다.") + collection_label1.grid(row = 0, column=0, padx=5, pady=5, sticky="nsew") + + collection_frame_left = customtkinter.CTkFrame(collection_window, width=300, height=yview) + collection_frame_left.grid(row =1, column =0, padx=5, pady=5, sticky="nsew") + collection_frame_right = customtkinter.CTkScrollableFrame(collection_window, width=900, height=yview) + collection_frame_right.grid(row =1, column =2, padx=5, pady=5, sticky="nsew") + + show_window = [] + + def copy_character_info(key): + text = character_book_dict[key][1].split(',') + text_list = [keyword.strip() for keyword in text] + charactersitics = [] + charactersitics.append("{"+key+"}") + count = 0 + for texts in text_list: + if count < 4 and texts in tagbag.bag_of_tags[5:] and 'tail' not in texts and 'pupil' not in texts and '1girl' not in texts and '1boy' not in texts and '1other' not in texts: + charactersitics.append(texts) + count += 1 + texts = ', '.join(charactersitics) + pyperclip.copy(texts) + collection_label1.configure(text=f"선택하신 {texts}가 클립보드에 복사되었습니다.", text_color="#FFFF97") + + + + + def spread_image(event, show_window): + collection_frame_right._parent_canvas.yview_moveto(0) + selected_indices = collection_listbox.curselection() + selected_index = selected_indices[0] + collection_listbox.configure(state="disabled") + if show_window: + for i in show_window: + i.destroy() + keyword_with_count = collection_listbox.get(selected_index).strip() + keyword = ' '.join(keyword_with_count.split('%')[1].split()).strip() + try: + key_in_book = split_dict[keyword] + # 나머지 코드... + except KeyError: + print(f"KeyError: '{keyword}' not found in the split dictionary.") + count = 0 + for key in key_in_book: + _row = count//3 + _column = count%3 + if key in character_book_dict: + f = customtkinter.CTkFrame(collection_frame_right, width = 298, height=394) + f.grid(row=_row, column=_column, padx=5, pady=5, sticky="nsew") + name = key + if len(key) > 45: + name = key[:44] + b = customtkinter.CTkButton(f, font=my_font,text=name, state="disabled" if len(key) > 45 else "normal", fg_color="grey10", hover_color="grey10", text_color_disabled="white", command=lambda name=name: copy_character_info(name)) + b.grid(row=0, column=0, padx=5, pady=5, sticky="nsew") + image_stream = io.BytesIO(base64.b64decode(character_book_dict[key][3])) + reloaded_image = Image.open(image_stream) + i = customtkinter.CTkLabel(f, text="", image=customtkinter.CTkImage(reloaded_image, size=(288,384))) + i.grid(row=1, column=0, padx=5, pady=5, sticky="nsew") + show_window.append(f) + count += 1 + collection_listbox.configure(state="normal") + collection_listbox.selection_clear(selected_index) + + collection_listbox = tk.Listbox(collection_frame_left, width=32, height=ylist, font = font.Font(family='Pretendard', size=13), bg='#2B2B2B', fg='#F8F8F8', borderwidth=2, highlightbackground='lightgrey') + collection_listbox.bind('<>',lambda event: spread_image(event, show_window)) + collection_listbox.grid(row = 0, column=0, padx=5, pady=5, sticky="nsew") + + + + all_collections = sorted( + ((k, v) for k, v in copyright_dict_0103.copyright_dict.items()), + key=lambda item: len(item[1]), + reverse=True + ) + all_collections.insert(0, ('Favorites', list(favorite_characters.keys()))) + + split_dict = {} + for keyword, value in all_collections: + if keyword == 'Favorites': + key_in_book = list(favorite_characters.keys()) + cover = sum(key in character_book_dict for key in key_in_book) + coverage = f"{round((cover / len(key_in_book)) * 100, 1) if cover else 0}%" + else: + key_in_book = copyright_dict_0103.copyright_dict[keyword] + cover = sum(key in character_book_dict for key in key_in_book) + coverage = f"{round((cover / len(key_in_book)) * 100, 1) if cover else 0}%" + + # key_in_book의 길이가 90을 초과하는 경우 처리 + if len(key_in_book) > 96: + for i in range(0, len(key_in_book), 96): + part_key = f"{keyword} ({i//96 + 1})" + split_dict[part_key] = key_in_book[i:i+96] + cover_part = sum(key in character_book_dict for key in split_dict[part_key]) + coverage_part = f"{round((cover_part / len(split_dict[part_key])) * 100, 1) if cover_part else 0}%" + collection_listbox.insert("end", f"{coverage_part:<7s}{keyword} ({i//96 + 1})") + else: + split_dict[keyword] = key_in_book + collection_listbox.insert("end", f"{coverage:<7s}{keyword}") + + +class Preset_open(customtkinter.CTkToplevel): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.title("프롬프트 검색/설정 프리셋") + self.attributes('-topmost', True) + self.resizable(width=False, height=False) + + my_font = customtkinter.CTkFont('Pretendard', 13) + + presets ={} + items = 0 + preset_frame_left = customtkinter.CTkFrame(self, width=250, height=520) + preset_frame_left.grid(row =1, rowspan=10, column =0, padx=5, pady=5, sticky="nsew") + preset_frame_right = customtkinter.CTkFrame(self, width=700, height=520) + preset_frame_right.grid(row =1, rowspan=10, column =1, columnspan = 2, padx=5, pady=5, sticky="nsew") + preset_insert_right_frame = customtkinter.CTkFrame(self) + preset_insert_right_frame.grid(row =0, column =0, columnspan=2, padx=5, pady=5, sticky="nsew") + preset_insert_button = customtkinter.CTkButton(preset_insert_right_frame, text="프리셋 추가", font=my_font, command=lambda: add_preset(self, presets)) + preset_insert_button.grid(row =0, column =0, padx=5, pady=5, sticky="nsew") + preset_insert_name = customtkinter.CTkLabel(preset_insert_right_frame, text="프리셋 명칭 : ", font=my_font) + preset_insert_name.grid(row =0, column =1, padx=5, pady=5, sticky="w") + preset_insert_name_entry = customtkinter.CTkEntry(preset_insert_right_frame,width=200, font=my_font) + preset_insert_name_entry.grid(row =0, column =2, padx=5, pady=5, sticky="w") + preset_insert_description = customtkinter.CTkLabel(preset_insert_right_frame, text=" 설명 : ", font=my_font) + preset_insert_description.grid(row =0, column =3, padx=5, pady=5, sticky="w") + preset_insert_description_entry = customtkinter.CTkEntry(preset_insert_right_frame, width=420, font=my_font) + preset_insert_description_entry.grid(row =0, column =4, padx=5, pady=5, sticky="w") + preset_frame_bottom = customtkinter.CTkFrame(self) + preset_frame_bottom.grid(row =11, column =0, columnspan=2, padx=5, pady=5, sticky="nsew") + preset_label = customtkinter.CTkLabel(preset_frame_bottom, text="생성기 내 좌측 패널의 값들을 프리셋으로 저장할 수 있습니다.", font=my_font) + preset_label.grid(row =0, column=0,sticky="n" ) + + def _yield(selected_option): + ypreset = self.presets[selected_option] + yield_preset_description.configure(text=ypreset['description']) + yield_preset_search.configure(text=ypreset['search']) + yield_preset_exclude.configure(text=ypreset['exclude']) + yield_preset_prompt.configure(state="normal") + yield_preset_prompt.delete("0.0", "end") + yield_preset_prompt.insert("0.0", ypreset['prompt']) + yield_preset_prompt.configure(state="disabled") + _text = "" + if ypreset['explicit'] == 1: + _text += "Explicit " + if ypreset['nsfw'] == 1: + _text += "NSFW " + if ypreset['sensitive'] == 1: + _text += "Sensitive " + if ypreset['general'] == 1: + _text += "General" + yield_activated_ratings.configure(text = _text) + yield_prefix.configure(state="normal") + yield_prefix.delete("0.0", "end") + yield_prefix.insert("0.0", ypreset['fix']) + yield_prefix.configure(state="disabled") + yield_postfix.configure(state="normal") + yield_postfix.delete("0.0", "end") + yield_postfix.insert("0.0", ypreset['fix_after']) + yield_postfix.configure(state="disabled") + yield_negative.configure(state="normal") + yield_negative.delete("0.0", "end") + yield_negative.insert("0.0", ypreset['negative']) + yield_negative.configure(state="disabled") + yield_autohide.configure(state="normal") + yield_autohide.delete("0.0", "end") + yield_autohide.insert("0.0", ypreset['auto_hide']) + yield_autohide.configure(state="disabled") + _text = "" + if ypreset['rm_artist'] == 1: + _text += "작가명제거 " + if ypreset['rm_copyright'] == 1: + _text += "작품명제거 " + if ypreset['rm_character'] == 1: + _text += "캐릭터특징제거" + yield_rm.configure(text = _text) + + + + preset_list = CTkListbox(preset_frame_left, height=570, font=my_font, command= _yield) + preset_list.grid(row = 0, column=0, padx=5, pady=5, sticky="nsew") + + show_preset_description = customtkinter.CTkButton(preset_frame_right, text="Description", font=my_font, width=150, fg_color="grey10", text_color_disabled="white",state="disabled") + show_preset_description.grid(row = 0, column = 0, padx=5, pady=5, sticky="w") + yield_preset_description = customtkinter.CTkLabel(preset_frame_right, text="", font=my_font, width=550) + yield_preset_description.grid(row = 0, column = 1, padx=5, pady=5, sticky="w") + + def insert_search(): + if preset_list.get(): + app.search_label_entry.delete(0, "end") + app.search_label_entry.insert(0, yield_preset_search.cget("text")) + show_preset_search = customtkinter.CTkButton(preset_frame_right, text="Search Keyword", font=my_font, width=150, fg_color="grey10", hover_color="grey", command=insert_search) + show_preset_search.grid(row = 1, column = 0, padx=5, pady=5, sticky="w") + yield_preset_search = customtkinter.CTkLabel(preset_frame_right, text="", font=my_font, width=550) + yield_preset_search.grid(row = 1, column = 1, padx=5, pady=5, sticky="w") + def insert_exclude(): + if preset_list.get(): + app.exclude_label_entry.delete(0, "end") + app.exclude_label_entry.insert(0, yield_preset_exclude.cget("text")) + show_preset_exclude = customtkinter.CTkButton(preset_frame_right, text="Exclude Keyword", font=my_font, width=150, fg_color="grey10", hover_color="grey", command=insert_exclude) + show_preset_exclude.grid(row = 2, column = 0, padx=5, pady=5, sticky="w") + yield_preset_exclude = customtkinter.CTkLabel(preset_frame_right, text="", font=my_font, width=550) + yield_preset_exclude.grid(row = 2, column = 1, padx=5, pady=5, sticky="w") + def insert_prompt(): + if preset_list.get(): + app.text_input.delete("0.0","end") + app.text_input.insert("0.0", yield_preset_prompt.get("0.0", "end")) + show_preset_prompt = customtkinter.CTkButton(preset_frame_right, text="Prompt", font=my_font, width=150, height=100, fg_color="grey10", hover_color="grey", command=insert_prompt) + show_preset_prompt.grid(row = 3, column = 0, padx=5, pady=5, sticky="w") + yield_preset_prompt = customtkinter.CTkTextbox(preset_frame_right, font=my_font, state="disabled",width=550, height=100, fg_color='#2B2B2B') + yield_preset_prompt.grid(row = 3, column = 1, padx=5, pady=5, sticky="w") + def insert_ratings(): + if preset_list.get(): + app.rating_select_explicit.deselect() + app.rating_select_nsfw.deselect() + app.rating_select_sensitive.deselect() + app.rating_select_general.deselect() + _text = yield_activated_ratings.cget("text") + if "Explicit" in _text: + app.rating_select_explicit.select() + if "NSFW" in _text: + app.rating_select_nsfw.select() + if "Sensitive" in _text: + app.rating_select_sensitive.select() + if "General" in _text: + app.rating_select_general.select() + show_activated_ratings = customtkinter.CTkButton(preset_frame_right, text="Ratings", font=my_font, width=150, fg_color="grey10", hover_color="grey", command=insert_ratings) + show_activated_ratings.grid(row = 4, column = 0, padx=5, pady=5, sticky="w") + yield_activated_ratings = customtkinter.CTkLabel(preset_frame_right, text="", font=my_font, width=550) + yield_activated_ratings.grid(row = 4, column = 1, padx=5, pady=5, sticky="w") + + #선행고정 + def insert_prefix(): + if preset_list.get(): + app.fixed_prompt_input.delete("0.0","end") + app.fixed_prompt_input.insert("0.0", yield_prefix.get("0.0", "end")) + show_prefix = customtkinter.CTkButton(preset_frame_right, text="Pre-fix", font=my_font, width=150, height=50, fg_color="grey10", hover_color="grey", command=insert_prefix) + show_prefix.grid(row = 5, column = 0, padx=5, pady=5, sticky="w") + yield_prefix = customtkinter.CTkTextbox(preset_frame_right, font=my_font, state="disabled",width=550, height=50, fg_color='#2B2B2B') + yield_prefix.grid(row = 5, column = 1, padx=5, pady=5, sticky="w") + def insert_postfix(): + if preset_list.get(): + app.fixed_prompt_after_input.delete("0.0","end") + app.fixed_prompt_after_input.insert("0.0", yield_postfix.get("0.0", "end")) + show_postfix = customtkinter.CTkButton(preset_frame_right, text="Post-fix", font=my_font, width=150, height=50, fg_color="grey10", hover_color="grey", command=insert_postfix) + show_postfix.grid(row = 6, column = 0, padx=5, pady=5, sticky="w") + yield_postfix = customtkinter.CTkTextbox(preset_frame_right, font=my_font, state="disabled",width=550, height=50, fg_color='#2B2B2B') + yield_postfix.grid(row = 6, column = 1, padx=5, pady=5, sticky="w") + def insert_negative(): + if preset_list.get(): + app.negative_prompt_input.delete("0.0","end") + app.negative_prompt_input.insert("0.0", yield_negative.get("0.0", "end")) + show_negative = customtkinter.CTkButton(preset_frame_right, text="Negative prompt", font=my_font, width=150, height=70, fg_color="grey10", hover_color="grey", command=insert_negative) + show_negative.grid(row = 7, column = 0, padx=5, pady=5, sticky="w") + yield_negative = customtkinter.CTkTextbox(preset_frame_right, font=my_font, state="disabled",width=550, height=70, fg_color='#2B2B2B') + yield_negative.grid(row = 7, column = 1, padx=5, pady=5, sticky="w") + def insert_autohide(): + if preset_list.get(): + app.auto_hide_keyword_input.delete("0.0","end") + app.auto_hide_keyword_input.insert("0.0", yield_autohide.get("0.0", "end")) + show_autohide = customtkinter.CTkButton(preset_frame_right, text="Hidden keyword", font=my_font, width=150, height=70, fg_color="grey10", hover_color="grey", command=insert_autohide) + show_autohide.grid(row = 8, column = 0, padx=5, pady=5, sticky="w") + yield_autohide = customtkinter.CTkTextbox(preset_frame_right, font=my_font, state="disabled",width=550, height=70, fg_color='#2B2B2B') + yield_autohide.grid(row = 8, column = 1, padx=5, pady=5, sticky="w") + + + def insert_rm(): + if preset_list.get(): + app.rm_artist_name_button.deselect() + app.rm_copyright_name_button.deselect() + app.rm_characteristic_button.deselect() + _text = yield_rm.cget("text") + if "작가명제거" in _text: + app.rm_artist_name_button.select() + if "작품명제거" in _text: + app.rm_copyright_name_button.select() + if "캐릭터특징제거" in _text: + app.rm_characteristic_button.select() + show_rm = customtkinter.CTkButton(preset_frame_right, text="Options", font=my_font, width=150, fg_color="grey10", hover_color="grey", command=insert_rm) + show_rm.grid(row = 9, column = 0, padx=5, pady=5, sticky="w") + yield_rm = customtkinter.CTkLabel(preset_frame_right, text="", font=my_font, width=550) + yield_rm.grid(row = 9, column = 1, padx=5, pady=5, sticky="w") + def apply_all(): + if preset_list.get(): + insert_search() + insert_exclude() + insert_prompt() + insert_ratings() + insert_prefix() + insert_postfix() + insert_negative() + insert_autohide() + insert_rm() + apply_all = customtkinter.CTkButton(preset_frame_right, text="일괄설정", font=my_font, width=150, command=apply_all) + apply_all.grid(row = 10, column = 0, padx=5, pady=5, sticky="w") + def remove_preset(self): + presets = self.presets + preset_name = preset_list.get() + if preset_name in presets: + del presets[preset_name] + save_preset(presets) + self.presets = load_preset() + update_list(self, self.presets) + remove_preset_button = customtkinter.CTkButton(preset_frame_right, text="이 프리셋을 삭제", font=my_font, fg_color="grey10", hover_color="grey", command=lambda: remove_preset(self)) + remove_preset_button.grid(row = 10, column =1, padx=5, pady=5, sticky="nsew") + + + + def add_preset(self, presets): + preset_name = preset_insert_name_entry.get() + if len(preset_name.strip()) < 1: + preset_label.configure(text="프리셋 이름을 정확히 입력해주세요.", text_color="#FFFF97") + else: + preset = { + 'preset_name':preset_name, + 'description':preset_insert_description_entry.get(), + 'search':app.search_label_entry.get(), + 'exclude':app.exclude_label_entry.get(), + 'prompt':app.text_input.get("0.0", "end-1c"), + 'fix':app.fixed_prompt_input.get("0.0", "end-1c"), + 'fix_after':app.fixed_prompt_after_input.get("0.0", "end-1c"), + 'negative':app.negative_prompt_input.get("0.0", "end-1c"), + 'auto_hide':app.auto_hide_keyword_input.get("0.0", "end-1c"), + 'rm_artist':app.rm_artist_name_button.get(), + 'rm_character':app.rm_characteristic_button.get(), + 'rm_copyright':app.rm_copyright_name_button.get(), + 'explicit':app.rating_select_explicit.get(), + 'nsfw':app.rating_select_nsfw.get(), + 'sensitive':app.rating_select_sensitive.get(), + 'general':app.rating_select_general.get() + } + updated_preset = {preset_name: preset} + if self.presets: + updated_preset.update(self.presets) + else: + self.presets = updated_preset + save_preset(updated_preset) + self.presets = load_preset() + update_list(self, self.presets) + + def update_list(self, presets): + if preset_list.size() != 0: + preset_list.delete(0, "END") + if presets: + for i, names in enumerate(presets.keys()): + preset_list.insert(i, names) + self.presets = presets + + def save_preset(presets): + with open('NAIA_preset.json', 'w', encoding='utf-8') as f: + json.dump(presets, f, ensure_ascii=False, indent=4) + + def load_preset(): + try: + if os.path.exists('NAIA_preset.json'): + with open('NAIA_preset.json', 'r', encoding='utf-8') as f: + return json.load(f) + except FileNotFoundError: + return {} + + def on_close(self): + self.withdraw() + + presets = load_preset() + update_list(self, presets) + self.protocol("WM_DELETE_WINDOW", lambda: on_close(self)) + + + + +class App(customtkinter.CTk, TkinterDnD.DnDWrapper): + access_token = None + NAI_ID = None + data = Data(wlist, tagbag, arti_list, copyright_list_reformatted, cd, remove_result_e, remove_result_qe) + + def __init__(self): + super().__init__() + self.TkdndVersion = TkinterDnD._require(self) + + if getattr(sys, 'frozen', False): + basedir = sys._MEIPASS + else: + basedir = os.path.dirname(__file__) + self.basedir = basedir + ctypes.windll.gdi32.AddFontResourceW(os.path.abspath(os.path.join(basedir,"Pretendard-Bold.otf"))) + ctypes.windll.gdi32.AddFontResourceW(os.path.abspath(os.path.join(basedir,"Pretendard-Regular.otf"))) + my_font = customtkinter.CTkFont('Pretendard', 13) + large_font = customtkinter.CTkFont('Pretendard', 13) + v_large_font = customtkinter.CTkFont('Pretendard', 14, weight="normal") + + self.config = configparser.ConfigParser() + + #UI 사이즈 획득 + if os.path.exists('ui_config.ini'): + try: + self.config.read('ui_config.ini') + except: + self.config['SET_PROMPT_UI_SIZE'] = { + 'MAIN_PROMPT': '200', + 'PREFIX_PROMPT': '100', + 'POSTFIX_PROMPT': '60', + 'NEGATIVE_PROMPT': '100', + 'AUTOHIDE_KEYWORD': '100', + 'CONDITIONAL_PROMPT': '100', + 'CONDITIONAL_NEGATIVE': '100' + } + with open('ui_config.ini', 'w') as configfile: + self.config.write(configfile) + else: + self.config['SET_PROMPT_UI_SIZE'] = { + 'MAIN_PROMPT': '200', + 'PREFIX_PROMPT': '100', + 'POSTFIX_PROMPT': '60', + 'NEGATIVE_PROMPT': '100', + 'AUTOHIDE_KEYWORD': '100', + 'CONDITIONAL_PROMPT': '100', + 'CONDITIONAL_NEGATIVE': '100' + } + with open('ui_config.ini', 'w') as configfile: + self.config.write(configfile) + + #UI 사이즈 할당 + self.ui_size = {} + try: self.ui_size['prompt'] = int(self.config['SET_PROMPT_UI_SIZE']['MAIN_PROMPT']) + except: self.ui_size['prompt'] = 200 + try: self.ui_size['prefix'] = int(self.config['SET_PROMPT_UI_SIZE']['PREFIX_PROMPT']) + except: self.ui_size['prefix'] = 100 + try: self.ui_size['postfix'] = int(self.config['SET_PROMPT_UI_SIZE']['POSTFIX_PROMPT']) + except: self.ui_size['postfix'] = 60 + try: self.ui_size['negative'] = int(self.config['SET_PROMPT_UI_SIZE']['NEGATIVE_PROMPT']) + except: self.ui_size['negative'] = 100 + try: self.ui_size['autohide'] = int(self.config['SET_PROMPT_UI_SIZE']['AUTOHIDE_KEYWORD']) + except: self.ui_size['autohide'] = 100 + try: self.ui_size['cond_prompt'] = int(self.config['SET_PROMPT_UI_SIZE']['CONDITIONAL_PROMPT']) + except: self.ui_size['cond_prompt'] = 100 + try: self.ui_size['cond_negative'] = int(self.config['SET_PROMPT_UI_SIZE']['CONDITIONAL_NEGATIVE']) + except: self.ui_size['cond_negative'] = 100 + + #For Automation + self.auto_time_left = 0 + self.auto_time_left_flag = False + self.auto_count_left = 0 + self.auto_count_left_flag = False + self.delay_offset = 0 + self.auto_thread = None + self.stop_event = None + self.hold_wildcard = False + + #For Generation + self.current_prompt_rating = None + + #json file + self.dictionary_parity = False + + self.start_time_prime = datetime.now() + self.start_time = self.start_time_prime.strftime('%Y%m%d_%H%M') + self.xy_plot_count = 0 + self.import_image_negative = {} + + self.title("NAIA Beta v1.03") + self.grid_rowconfigure(0, weight=1) + self.columnconfigure(2, weight=1) + + windll.shcore.SetProcessDpiAwareness(1) + #디자인: 상용 프로그램의 스타일과 유사하도록 UI를 새롭게 개편한다. + self.left_frame = customtkinter.CTkScrollableFrame(self, width=510, height=960) + self.left_frame.grid(row=0, column=0, sticky="nsew") + self.right_frame = customtkinter.CTkScrollableFrame(self, width=768,height=960) + self.right_frame.grid(row=0, column=1, sticky="nsew") + self.hidden_frame = customtkinter.CTkFrame(self, width=250) + self.hidden_frame.grid(row=0, column=2, sticky="nsew") + self.image_label_report_sub = customtkinter.CTkTextbox(self.hidden_frame,width=250, height=500, font=large_font) + self.image_label_report_sub.grid(row=2, column=0, columnspan=2, pady=5, padx=5, sticky="nsew") + self.image_label_report_sub.configure(state="disabled") + self.state_label2 = customtkinter.CTkLabel(self.hidden_frame, text="state : idle", font=my_font) + self.state_label2.grid(row=8, column=0, columnspan=2, pady=5, padx=5, sticky="n") + self.hidden_frame.grid_forget() + + def sync_text(): + content = self.image_label_report.get("0.0", "end-1c").rstrip() + label = self.state_label.cget("text").rstrip() + label_sub = self.state_label2.cget("text").rstrip() + content_sub = self.image_label_report_sub.get("0.0", "end-1c").rstrip() + if content != content_sub: + self.image_label_report_sub.configure(state="normal") + self.image_label_report_sub.delete("0.0", "end") + self.image_label_report_sub.insert("0.0", content) + self.image_label_report_sub.configure(text_color=self.image_label_report.cget("text_color"),state="disabled") + if label != label_sub: + self.state_label2.configure(text=label, text_color=self.image_label_report.cget("text_color")) + self.after(500, sync_text) + + #검색 키워드 창 관리 프레임 + self.search_frame = customtkinter.CTkFrame(self.left_frame) + self.search_frame.grid(row=0, column=0, sticky="nsew") + + def open_AccountSetting(self): + if self.AccountSetting is None: + self.AccountSetting = AccountSetting(self) + else: + if self.AccountSetting.state() == 'withdrawn': + self.AccountSetting.deiconify() + else: + self.AccountSetting.NAI_ID_entry.focus() + + def NAI_token_remove(self): + app.access_token = None + app.NAI_ID = None + if self.running_flag or self.automation_button.get()==1 or self.image_generation_button.cget("state") == "disabled": + self.image_label_report.configure(state="normal") + self.image_label_report.delete("0.0", "end") + self.image_label_report.insert("0.0", " NAI 토큰의 제거가 가능한 상태가 아닙니다. 모든 작업/자동화 설정을 마치고 시도 해주세요.") + self.image_label_report.configure(text_color="#FFFF97") + self.image_label_report.configure(state="disabled") + else: + app.NAI_Account_Login.configure(state="disabled") + app.NAI_Token_Remove.configure(state="disabled") + app.NAI_Account_State.configure(text="NAI Logout", text_color="red") + app.image_generation_button.configure(state="disabled") + self.image_label_report.configure(state="normal") + self.image_label_report.delete("0.0", "end") + self.image_label_report.insert("0.0", " NAI 토큰 제거 완료(Logout). 프로그램을 정상적으로 종료 후 재실행해야 반영됩니다.") + self.image_label_report.configure(text_color="#FFFF97") + self.image_label_report.configure(state="disabled") + + + self.AccountSetting = None + #NAI 계정 관리 프레임 + self.NAI_Account_Frame = customtkinter.CTkFrame(self.search_frame, width=440, height=50, bg_color="transparent") + self.NAI_Account_Frame.grid(row=0, column=0, padx=8, pady=5,sticky="nsew") + self.NAI_Account_Frame.columnconfigure(0, weight=6) + self.NAI_Account_Frame.columnconfigure(1, weight=4) + self.NAI_Account_Frame.columnconfigure(2, weight=4) + + def search_ui_hide(): + if self.ui_hide_var.get() == 0: + self.NAI_Account_State.configure(text="검색 UI 펼치기") + self.search_label.grid_forget() + search_hyperlink.grid_forget() + self.search_label_entry.grid_forget() + self.exclude_label.grid_forget() + self.exclude_label_entry.grid_forget() + self.select_rating_frame.grid_forget() + else: + self.NAI_Account_State.configure(text="검색 UI 숨기기") + self.search_label.grid(row=1, column=0, padx=8, sticky="w") + search_hyperlink.grid(row=1, column=0, padx=8, sticky="e") + self.search_label_entry.grid(row=2, column=0, padx=8, sticky="w") + self.exclude_label.grid(row=3, column=0, padx=8, sticky="w") + self.exclude_label_entry.grid(row=4, column=0, padx=8, sticky="w") + self.select_rating_frame.grid(row=6, column=0, padx=8, pady=10, sticky="nsew") + + #NAI 계정 로그인 상태 + self.ui_hide_var = customtkinter.IntVar(value=1) + self.NAI_Account_State = customtkinter.CTkCheckBox(self.NAI_Account_Frame, text="NAI Login 필요 ", font=my_font, variable=self.ui_hide_var, command=search_ui_hide, border_color="#333333", fg_color="grey10", hover_color="#50164A") + #print(self.NAI_Account_State .cget("font")) + self.NAI_Account_State.grid(row=0, column=0, padx=20, sticky="w") + #NAI 계정 로그인 버튼 + self.NAI_Account_Login = customtkinter.CTkButton(self.NAI_Account_Frame, text="NAI 계정연결", font=my_font, command=lambda: open_AccountSetting(self)) + self.NAI_Account_Login.grid(row=0, column=1, padx=20, sticky="ew") + #NAI 토큰 해제 버튼 + self.NAI_Token_Remove = customtkinter.CTkButton(self.NAI_Account_Frame, text="NAI 토큰제거", state="disabled", fg_color="#848484", font=my_font, command=lambda: NAI_token_remove(self)) + self.NAI_Token_Remove.grid(row=0, column=2,padx=20, sticky="ew") + + #검색,제외 키워드 창 + self.search_label = customtkinter.CTkLabel(self.search_frame, text="검색 키워드 입력 : keyword, *keyword, {keyword1|keyword2}", font=my_font) + self.search_label.grid(row=1, column=0, padx=8, sticky="w") + search_hyperlink = customtkinter.CTkLabel(self.search_frame, text="가이드 열기 (arca.live) ", font=customtkinter.CTkFont('Pretendard', 13),text_color="lightblue", cursor="hand2") + search_hyperlink.grid(row=1, column=0, sticky="e") + search_hyperlink.bind("", lambda e: webbrowser.open_new("https://arca.live/b/aiart/96006690")) + self.search_label_entry = customtkinter.CTkEntry(self.search_frame, width=490) + self.search_label_entry.grid(row=2, column=0, padx=8, sticky="w") + self.exclude_label = customtkinter.CTkLabel(self.search_frame, text="제외 키워드 입력 : keyword, ~keyword", font=my_font) + self.exclude_label.grid(row=3, column=0, padx=8, sticky="w") + self.exclude_label_entry = customtkinter.CTkEntry(self.search_frame, width=490) + self.exclude_label_entry.grid(row=4, column=0, padx=8, sticky="w") + + #검색 수위 관리 창 + self.select_rating_frame = customtkinter.CTkFrame(self.search_frame, width=490) + self.select_rating_frame.grid(row=6, column=0, padx=8, pady=10, sticky="nsew") + + #검색 수위 체크박스 + self.rating_select_var_explicit = customtkinter.IntVar(value=1) + self.rating_select_var_nsfw = customtkinter.IntVar(value=1) + self.rating_select_var_sensitive = customtkinter.IntVar(value=1) + self.rating_select_var_general = customtkinter.IntVar(value=1) + self.rating_select_explicit = customtkinter.CTkCheckBox(self.select_rating_frame, width=85, text="Explicit", variable= self.rating_select_var_explicit, font=my_font) + self.rating_select_explicit.grid(row=0, column=0, sticky="ew") + self.rating_select_nsfw = customtkinter.CTkCheckBox(self.select_rating_frame, width=80,text="NSFW", variable= self.rating_select_var_nsfw, font=my_font) + self.rating_select_nsfw.grid(row=0, column=1, sticky="ew") + self.rating_select_sensitive = customtkinter.CTkCheckBox(self.select_rating_frame,width=90, text="Sensitive", variable= self.rating_select_var_sensitive, font=my_font) + self.rating_select_sensitive.grid(row=0, column=2, sticky="ew") + self.rating_select_general = customtkinter.CTkCheckBox(self.select_rating_frame,width=85, text="General", variable= self.rating_select_var_general, font=my_font) + self.rating_select_general.grid(row=0, column=3, sticky="e") + + self.cached_rows = None + self.searching_flag = False + self.search_thread = None + self.freezed_cached_rows = None + + def fav_check(id): + return id in self.fav_id_list + + def null_prompt_attention(): + self.text_input.delete("0.0", "end") + self.text_input.insert("0.0", "현재 조건에 맞는 프롬프트가 없습니다.") + + def prompt_search(): + if not self.control_pressed: + self.searching_flag = True + self.search_button.configure(text="검색 (작업중)", state="disabled") + search_thread = threading.Thread(target=perform_search, daemon=True) + self.search_thread = search_thread + search_thread.start() + else: + depth_search() + + def depth_search(): + depth_search_window = customtkinter.CTkToplevel() + depth_search_window.title("심층검색") + depth_search_window.attributes('-topmost', True) + depth_search_window.resizable(width=False, height=False) + self.freezed_cached_rows = self.cached_rows.copy() + + def perform_depth_search(): + self.search_button.configure(text="심층검색중", state="disabled") + self.searching_flag = True + df = self.cached_rows + df = NAIA_search.search(df, search_label_entry.get(), exclude_label_entry.get()) + + if df is None: + null_prompt_attention() # 문자열 비었다고 경고 + else: + self.cached_rows = df + df.reset_index(drop=True, inplace=True) + update_labels(df) + self.searching_flag = False + depth_search_execute.configure(text="남은 프롬프트 행에서 재검색 : "+str(len(self.cached_rows))) + prompt_rollback.configure(text="이전 프롬프트행 복원 : "+str(len(self.freezed_cached_rows))) + self.search_button.configure(text="검색", state="normal") + prompt_rollback.configure(state="normal") + + def rollback(): + self.cached_rows = self.freezed_cached_rows.copy() + update_labels(self.cached_rows) + depth_search_execute.configure(text="남은 프롬프트 행에서 재검색 : "+str(len(self.cached_rows))) + prompt_rollback.configure(state="disabled") + + def depth_search_close(): + self.freezed_cached_rows = None + depth_search_window.destroy() + + search_label = customtkinter.CTkLabel(depth_search_window, text="검색 키워드 입력 : keyword, *keyword, {keyword1|keyword2}", font=my_font) + search_label.grid(row=1, column=0, columnspan=2, padx=8, pady=5, sticky="w") + search_label_entry = customtkinter.CTkEntry(depth_search_window, width=490) + search_label_entry.grid(row=2, column=0, columnspan=2, padx=8, pady=5, sticky="w") + exclude_label = customtkinter.CTkLabel(depth_search_window, text="제외 키워드 입력 : keyword, ~keyword", font=my_font) + exclude_label.grid(row=3, column=0, columnspan=2, padx=8, pady=5, sticky="w") + exclude_label_entry = customtkinter.CTkEntry(depth_search_window, width=490) + exclude_label_entry.grid(row=4, column=0, columnspan=2, padx=8, pady=5, sticky="w") + depth_search_execute = customtkinter.CTkButton(depth_search_window, text="남은 프롬프트 행에서 재검색 : "+str(len(self.cached_rows)), font=my_font, command=perform_depth_search) + depth_search_execute.grid(row=5, column=0, padx=5,pady=5, sticky="n") + prompt_rollback = customtkinter.CTkButton(depth_search_window, text="이전 프롬프트행 복원 : "+str(len(self.freezed_cached_rows)), font=my_font, fg_color="#7030A0", hover_color="#481F67", command=rollback, state="disabled") + prompt_rollback.grid(row=5, column=1, padx=5,pady=5, sticky="n") + depth_search_window.protocol("WM_DELETE_WINDOW", depth_search_close) + + def perform_search(): + df = pd.read_parquet(os.path.join(basedir, "tags.parquet"), engine="pyarrow") + df = NAIA_search.search(df, self.search_label_entry.get(), self.exclude_label_entry.get(), self.rating_select_explicit.get(), self.rating_select_nsfw.get(), self.rating_select_sensitive.get(), self.rating_select_general.get()) + + if df is None: + null_prompt_attention() # 문자열 비었다고 경고 + else: + self.cached_rows = df + df.reset_index(drop=True, inplace=True) + update_labels(df) + self.searching_flag = False + self.search_button.configure(text="검색", state="normal") + + def update_labels(df): + self.searched_prompt_label.configure(text="검색 프롬프트 행 : " + str(len(self.cached_rows))) + self.cached_prompt_label.configure(text="남은 프롬프트 행 : " + str(len(self.cached_rows))) + print(df.info(memory_usage="deep")) + + #검색 버튼 + self.search_button = customtkinter.CTkButton(self.select_rating_frame, text="검색", font=my_font, command=prompt_search, width=130) + self.search_button.grid(row=0, column=4,padx=5, sticky="w") + + #검색 결과 관리 창 + self.searched_prompt_frame = customtkinter.CTkFrame(self.search_frame) + self.searched_prompt_frame.grid(row=7, column=0, padx=8, pady=5, sticky="nsew") + self.searched_prompt_frame.columnconfigure(0, weight= 1) + self.searched_prompt_frame.columnconfigure(1, weight= 1) + self.searched_prompt_frame.columnconfigure(2, weight= 1) + self.searched_prompt_label = customtkinter.CTkLabel(self.searched_prompt_frame, text="검색 프롬프트 행 : 0", font=my_font) + self.searched_prompt_label.grid(row=0, column=0, sticky="ew") + self.cached_prompt_label = customtkinter.CTkLabel(self.searched_prompt_frame, text="남은 프롬프트 행 : 0", font=my_font) + self.cached_prompt_label.grid(row=0, column=1, sticky="ew") + fdf = None + + self.Preset_open = None + def open_Preset(self): + if self.Preset_open is None: + self.Preset_open = Preset_open(self) + else: + if self.Preset_open.state() == 'withdrawn': + self.Preset_open.deiconify() + else: + self.Preset_open.focus() + + def open_favorite(): + favorite_window = customtkinter.CTkToplevel() + favorite_window.title("선호 프롬프트") + favorite_window.attributes('-topmost', True) + favorite_window.resizable(width=False, height=False) + self.freezed_cached_rows = self.cached_rows.copy() + + global fdf + fdf = pd.read_parquet("favorite_prompt.parquet", engine="pyarrow") + odf = fdf.copy() + max_page = [1 + len(fdf) // 5] + + def perform_favorite_search(): + global fdf + fdf = NAIA_search.search(odf, search_label_entry.get(), exclude_label_entry.get()) + + if type(fdf) == type(None): + assign_row.configure(text="검색 결과 없음") + return + + assign_row.configure(text="랜덤 프롬프트에 삽입 "+str(len(fdf))) + prompt_rollback.configure(text="이전 프롬프트행 복원 : "+str(len(self.freezed_cached_rows))) + self.search_button.configure(text="검색", state="normal") + max_page[0] = 1 + len(fdf) // 5 + current_page[0] = 0 + page.configure(text=f" 1 / {max_page[0]} ") + prev_button.configure(state="disabled") + + + if max_page[0] == 1: + next_button.configure(state="disabled") + else: + next_button.configure(state="normal") + update_ui_with_dataframe(current_page, favorite_window, headers_width, my_font) + + def import_prompt(): + global fdf + if fdf is None: + null_prompt_attention() # 문자열 비었다고 경고 + else: + self.cached_rows = fdf.copy() + fdf.reset_index(drop=True, inplace=True) + update_labels(fdf) + prompt_rollback.configure(state="normal") + + def rollback(): + global fdf + self.cached_rows = self.freezed_cached_rows.copy() + update_labels(self.cached_rows) + assign_row.configure(text="랜덤 프롬프트에 삽입 "+str(len(fdf))) + prompt_rollback.configure(state="disabled") + + def favorite_search_close(): + self.freezed_cached_rows = None + favorite_window.destroy() + + search_frame = customtkinter.CTkFrame(favorite_window, width=990) + search_frame.grid(row=0, column=0, padx=5,pady=5, sticky="nsew") + search_label = customtkinter.CTkLabel(search_frame, text="검색 키워드 입력 : keyword, *keyword, {keyword1|keyword2}", font=my_font) + search_label.grid(row=0, column=0, columnspan=2, padx=8, pady=5, sticky="nsew") + search_label_entry = customtkinter.CTkEntry(search_frame, width=485) + search_label_entry.grid(row=1, column=0, columnspan=2, padx=8, pady=5, sticky="nsew") + exclude_label = customtkinter.CTkLabel(search_frame, text="제외 키워드 입력 : keyword, ~keyword", font=my_font) + exclude_label.grid(row=0, column=2, columnspan=2, padx=8, pady=5, sticky="nsew") + exclude_label_entry = customtkinter.CTkEntry(search_frame, width=485) + exclude_label_entry.grid(row=1, column=2, columnspan=2, padx=8, pady=5, sticky="nsew") + + depth_search_execute = customtkinter.CTkButton(search_frame, text="선호 프롬프트내 검색", font=my_font, command=perform_favorite_search) + depth_search_execute.grid(row=2, column=0, padx=5,pady=5, sticky="n") + + assign_row = customtkinter.CTkButton(search_frame, text="랜덤 프롬프트에 삽입 "+str(len(fdf)), font=my_font, fg_color="#CDCDCD", text_color="black", hover_color="#848484", command=import_prompt) + assign_row.grid(row=2, column=1, padx=5,pady=5, sticky="n") + + prompt_rollback = customtkinter.CTkButton(search_frame, text="이전 프롬프트행 복원 : "+str(len(self.freezed_cached_rows)), font=my_font, fg_color="#7030A0", hover_color="#481F67", command=rollback, state="disabled") + prompt_rollback.grid(row=2, column=2, padx=5,pady=5, sticky="n") + + page_frame = customtkinter.CTkFrame(search_frame) + page_frame.grid(row=2, column=3, padx=5,pady=5 ,sticky="nsew") + + def prev_pressed(): + current_page[0] -= 1 + if current_page[0] <= 0: + prev_button.configure(state="disabled") + if current_page[0] < max_page[0]: + next_button.configure(state="normal") + page.configure( text=f"{1+current_page[0]} / {max_page[0]} ") + update_ui_with_dataframe(current_page, favorite_window, headers_width, my_font) + + def next_pressed(): + current_page[0] += 1 + if current_page[0] >= max_page[0]-1: + next_button.configure(state="disabled") + prev_button.configure(state="normal") + page.configure( text=f"{1+current_page[0]} / {max_page[0]} ") + update_ui_with_dataframe(current_page, favorite_window, headers_width, my_font) + + + page = customtkinter.CTkLabel(page_frame, text=f" 1 / {max_page[0]} ", font=my_font, width=70) + page.grid(row=0, column=1, padx=5, sticky="nsew") + prev_button = customtkinter.CTkButton(page_frame, text=" ◀ Prev ", font=my_font, width=70, state="disabled", command=prev_pressed) + prev_button.grid(row=0, column=0, padx=5 ,sticky="nsew") + next_button = customtkinter.CTkButton(page_frame, text=" Next ▶ ", font=my_font, width=70, command=next_pressed) + next_button.grid(row=0, column=2, padx=5, sticky="nsew") + if max_page[0] == 1: + next_button.configure(state="disabled") + + + + favorite_window.protocol("WM_DELETE_WINDOW", favorite_search_close) + + headers = ['↙', 'character', 'copyright', 'artist', 'general prompts', 'image', 'delete'] + headers_width = [60, 120, 120, 120, 360, 120, 60] + header_frame = customtkinter.CTkFrame(favorite_window) + header_frame.grid(row=6, column=0, padx=5,pady=5, sticky="nsew") + empty_image = customtkinter.CTkImage(Image.new('RGB', size=(192,192), color="#2B2B2B"), size=(120, 120)) + + for i, header in enumerate(headers): + label = customtkinter.CTkLabel(header_frame, text=header, width=headers_width[i], font=my_font) + label.grid(row=0, column=i, sticky="nsew", padx=5) + + ui_elements = [] # UI 요소 저장용 이중 리스트 + + for i in range(5): # DataFrame 길이를 고려 + row_elements = [] + if i < len(fdf): + row = fdf.iloc[i] + row_frame = customtkinter.CTkFrame(favorite_window) + row_frame.grid(row=i+7, column=0, sticky="nsew", padx=5, pady=5) + + for j, header in enumerate(['↙', 'character', 'copyright', 'artist', 'general', 'image', 'delete']): + if header == '↙': + button = customtkinter.CTkButton( + row_frame, width=headers_width[j], font=my_font, + text="↙", height=28, state="disabled" + ) + button.grid(row=0, padx=5, column=j, sticky="nsew") + element = button + elif header == 'delete': + delete_button = customtkinter.CTkButton( + row_frame, width=headers_width[j], font=my_font, text="✖", + height=28, fg_color="grey", hover_color="grey10", state="disabled" + ) + delete_button.grid(row=0, padx=5, column=j, sticky="nsew") + element = delete_button + elif header == 'image': + label = customtkinter.CTkLabel(row_frame, width=headers_width[j], font=my_font, text=" ", height=120) + label.grid(row=0, padx=5, column=j, sticky="nsew") + element = label + else: + text = customtkinter.CTkTextbox(row_frame, width=headers_width[j], font=my_font, height=120) + text.grid(row=0, padx=5, column=j, sticky="nsew") + element = text + row_elements.append(element) + + ui_elements.append(row_elements) # 각 행의 UI 요소를 추가 + + # 이미지 삭제 함수 + def delete_image(image_path): + try: + os.remove(image_path) + except OSError as e: + print(f"Error deleting file {image_path}: {e.strerror}") + + # DataFrame 행 삭제 및 업데이트 함수 + def delete_row(fdf, idx, image_path, basedir): + # DataFrame에서 특정 id 값을 가진 행을 'deleted'로 표시 + fdf.loc[fdf['id'] == idx, 'deleted'] = 'deleted' + + # DataFrame에서 특정 행 삭제 + fdf.drop(fdf[fdf['id'] == idx].index, inplace=True) + + # 변경된 DataFrame을 parquet 파일로 저장 + fdf.to_parquet("favorite_prompt.parquet", engine='pyarrow') + + # 이미지 파일 삭제 + delete_image(image_path) + + update_ui_with_dataframe(current_page, favorite_window, headers_width, my_font) + + # 삭제 버튼 클릭 시 실행될 함수를 생성하는 함수 + def make_delete_command(fdf, idx, image_path, basedir): + def command(): + delete_row(fdf, idx, image_path, basedir) + return command + + def make_select_command(dataframe, row_id): + """ + 특정 DataFrame 행의 'id' 값을 출력하는 커맨드를 생성하는 함수. + 이 함수는 람다 함수를 사용하여 버튼에 할당될 커맨드를 반환합니다. + """ + def command(): + popped_row = dataframe.loc[dataframe['id'] == row_id] + if not popped_row.empty: + popped_row_series = popped_row.iloc[0] # 첫 번째 행을 Series로 변환 + # 얻은 Series에 random_function을 적용합니다. + random_function(popped_row_series) + return command + + def update_ui_with_dataframe(current_page, favorite_window, headers_width, my_font): + global fdf + base_image_path = "favorite_prompts" + rows_per_page = 5 + start_index = current_page[0] * rows_per_page # current_page[0]에서 수정됨, current_page가 이미 정수라고 가정 + end_index = min(start_index + rows_per_page, len(fdf)) + + for i in range(5): # 최대 5개의 행을 처리 + # 현재 페이지에 표시할 데이터가 있는 경우 + if i < end_index - start_index: + row = fdf.iloc[start_index + i] + for j, header in enumerate(['↙', 'character', 'copyright', 'artist', 'general', 'image', 'delete']): + element = ui_elements[i][j] # 기존 UI 요소에 접근 + if header == '↙': + # '↙' 버튼의 커맨드 업데이트 + element.configure(state="normal", command=make_select_command(fdf, row['id'])) + elif header == 'delete': + # 'delete' 버튼의 커맨드 업데이트 + image_path = f"{base_image_path}/{row['id']}.jpg" + element.configure(state="normal", command=make_delete_command(fdf, row['id'], image_path, basedir)) + elif header == 'image': + try: + image_path = f"{base_image_path}/{row['id']}.jpg" + if os.path.exists(image_path): + _image = customtkinter.CTkImage(Image.open(image_path), size=(120, 120)) + element.configure(image=_image) + else: + raise FileNotFoundError + except FileNotFoundError: + element.configure(text="Image Not Found") + else: + # 텍스트박스의 텍스트 업데이트 + text_value = row[header] if pd.notna(row[header]) else "" + element.delete("0.0", "end") + element.insert("0.0", text_value) + else: + # 현재 페이지에 표시할 데이터가 없는 경우, 비어 있는 행의 내용을 초기화 + for j, element in enumerate(ui_elements[i]): + if isinstance(element, customtkinter.CTkLabel): + element.configure(text=" ", image=empty_image) + element.image = empty_image + elif isinstance(element, customtkinter.CTkTextbox): + element.delete("0.0", "end") + elif isinstance(element, customtkinter.CTkButton): + element.configure(state="disabled") + + + + # 현재 페이지를 0으로 설정 (0부터 시작) + current_page = [0] + + # UI 업데이트 함수 호출 + update_ui_with_dataframe(current_page, favorite_window, headers_width, my_font) + favorite_window.after(1500, lambda: favorite_window.attributes('-topmost', False)) + + + + self.prompt_preset_button = customtkinter.CTkButton(self.searched_prompt_frame, width=80, text="프리셋", fg_color="#848484", font=my_font, command= lambda: open_Preset(self)) + self.prompt_preset_button.grid(row=0, column=2, sticky="w") + + self.fav_button = customtkinter.CTkButton(self.searched_prompt_frame, width=40, text=" ★ ", font=my_font, fg_color="#7030A0", hover_color="#481F67", command= open_favorite) + self.fav_button.grid(row=0, column=2, padx=15, sticky="e") + + try: + temp_df = pd.read_parquet("favorite_prompt.parquet", engine="pyarrow") + self.fav_id_list = [] + if 'id' in temp_df.columns: + self.fav_id_list.extend(temp_df['id'].tolist()) + del(temp_df) + except: + self.fav_id_list = [] + #여기서 오류남 이따 고치셈 + self.fav_button.configure(state="disabled") + + def highlight_text(event=None): + if '#' in self.text_input.get("1.0", tk.END): + # 기존의 하이라이트를 모두 제거 + self.text_input.tag_remove("highlight", "1.0", tk.END) + + # '#'으로 시작하여 ','나 '\n', 또는 텍스트 끝까지 이어지는 모든 부분을 찾음 + for match in re.finditer(r'#.*?([,\n]|$)', self.text_input.get("1.0", tk.END)): + start = "1.0 + {}c".format(match.start()) + end = "1.0 + {}c".format(match.end()) + self.text_input.tag_add("highlight", start, end) + + def on_enter_pressed(event): + if self.control_pressed: + NAIA_generate(self) + return "break" + else: + highlight_text() + + def on_tab_pressed(event): + if self.toggle_prompt_fix_button.get() == 0: + time.sleep(0.2) + random_function() + + #텍스트 입력 + self.text_input_frame = customtkinter.CTkFrame(self.searched_prompt_frame) + self.text_input_frame.grid(row=1, column=0, columnspan=3, sticky="nsew") + self.text_input_label = customtkinter.CTkLabel(self.text_input_frame, text=" ----------------------------- 프롬프트 ----------------------------- ", font=large_font) + self.text_input_label.grid(row = 0, sticky="n" ) + self.text_input = customtkinter.CTkTextbox(self.text_input_frame, width=490, height=self.ui_size['prompt'], font=v_large_font, undo=True) + self.text_input.grid(row=1, column=0, pady=5, sticky="nsew") + self.text_input.bind("", on_enter_pressed) + self.text_input.bind("", on_tab_pressed) + self.text_input.bind("", highlight_text) + self.text_input.tag_config("highlight", foreground="#FFFF97") + self.current_popped_row = None + + def random_function(edf = None): + if(self.random_function_pressed) == False: + self.random_function_pressed = True + self.random_function_button.configure(state="disabled") + if type(edf) == type(None): + if self.toggle_prompt_fix.get()==0 and type(self.cached_rows) == type(None): + self.text_input.delete("0.0", "end") + self.text_input.insert("0.0", "먼저 키워드 검색을 해 주세요.") + self.random_function_button.configure(state="normal") + self.random_function_pressed = False + return + if self.toggle_prompt_fix.get()==1 and type(self.cached_rows) == type(None): + self.random_function_pressed = False + return + elif self.toggle_prompt_fix.get()==0 and self.cached_rows.empty: + if (self.automation_button.get() == 0): + self.text_input.delete("0.0", "end") + self.text_input.insert("0.0", "먼저 키워드 검색을 해 주세요.") + self.random_function_button.configure(state="normal") + self.random_function_pressed = False + return + elif type(self.freezed_cached_rows) != type(None): + self.text_input.delete("0.0", "end") + self.text_input.insert("0.0", "기존 프롬프트를 복원합니다.") + self.cached_rows = self.freezed_cached_rows.copy() + time.sleep(1) + self.random_function_button.configure(state="normal") + self.random_function_pressed = False + app.after(0, random_function) + return + else: + self.text_input.delete("0.0", "end") + self.text_input.insert("0.0", "재검색을 수행합니다.") + prompt_search() + while(self.cached_rows.empty): + time.sleep(1) + self.random_function_button.configure(state="normal") + self.random_function_pressed = False + app.after(0, random_function) + return + if self.toggle_prompt_fix.get(): + self.random_function_pressed = False + return + else: + if self.random_artist_button.get() == 1: + try: + if self.random_artist_prefix.get() == "artist:": + random_artist_name = "artist:"+ random.choice(self.random_artist) + elif self.random_artist_prefix.get() == "(artist)": + random_artist_name = random.choice(self.random_artist)+" (artist)" + else: + random_artist_name = random.choice(self.random_artist) + except: + random_artist_name = "" + magic_word = { + "random_artist":True, + "random_artist_name": random_artist_name + } + else: + magic_word = { + "random_artist":False + } + if self.cond_prompt_button_var.get() == 1: + magic_word["cond"] = self.conditional_prompt_input.get("0.0", "end-1c") if len(self.conditional_prompt_input.get("0.0", "end-1c")) > 4 else None + if type(edf) == type(None): + random_index = np.random.choice(self.cached_rows.index) + popped_row = self.cached_rows.loc[random_index] + self.current_popped_row = popped_row.copy() + self.cached_rows.drop(random_index, inplace=True) + self.cached_prompt_label.configure(text = "남은 프롬프트 행 : "+str(len(self.cached_rows))) + else: + popped_row = edf.copy() + self.current_popped_row = popped_row.copy() + #조건 추가 부분 + if self.rm_character_name_var.get() == 1: rmc = ", *(remove character name)" + else: rmc="" + if(self.toggle_wildcard_preopen.get()==1): + if(self.wildcard_preopen_repeat_current >= self.wildcard_preopen_repeat): + self.wildcard_preopen_repeat_current = 0 + #와일드카드 개봉 구현 + temp_fixed = self.fixed_prompt_input.get("0.0", "end") + temp_fixed = [item.strip() for item in temp_fixed.split(',')] + global_temp = [] + if '<' in self.fixed_prompt_input.get("0.0", "end"): + wildcard_present = True + itc = 0 + while (wildcard_present and itc < 10): + wildcard_present = False + #### 단계 1 : 인스턴트 와일드카드 처리 ### + for i, keyword in enumerate(temp_fixed): + if keyword.startswith('<') and keyword.endswith('>'): + wildcard_present = True + vbar_check = keyword[1:-1] + if '|' in vbar_check: + choices = vbar_check.split('|') # '|'를 기준으로 split + choice_dic = {} + for choice in choices: + match = re.match(r'(\d*\.?\d+):(.+)', choice) + if match: + value, keyword = float(match.group(1)), match.group(2).strip() + else: + value, keyword = 1, choice.strip() + choice_dic[keyword] = value + keywords = list(choice_dic.keys()) + weights = list(choice_dic.values()) + selected_instant_wildcard = random.choices(keywords, weights=weights, k=1)[0] + temp_fixed[i] = selected_instant_wildcard + #### 단계 2 : 글로벌 와일드카드 처리 ### + for i, keyword in enumerate(temp_fixed): + if "<" in keyword and ">" in keyword: + wildcard_present = True + input_str = keyword.strip('<>').strip() + if("__" in input_str): + adjectives = re.findall(r'__(.*?)__', input_str) + last_keyword = re.split(r'__.*?__', input_str)[-1] + adjective_string = "" + for adjective in adjectives: + adjective_string += (self.get_wildcard(adjective) + " ") + temp_fixed[i] = adjective_string + self.get_wildcard(last_keyword) + else: + out_wildcard = self.get_wildcard(input_str) + if ',' in out_wildcard: + split_wc = [item.strip() for item in out_wildcard.split(',')] + temp = [] + for _key in split_wc[1:]: + if "{" not in _key and "}" not in _key and "[" not in _key and "]" not in _key: + temp.append(_key) + if temp: + for _key in temp: + split_wc.remove(_key) + global_temp.append(_key) + if len(split_wc) == 2 and '{' not in split_wc[1] and '[' not in split_wc[1] and (split_wc[1].endswith('}') or split_wc[1].endswith(']')): + wc_post = split_wc[1].replace(']','').replace('}','') + global_temp.append(wc_post) + split_wc[0] += split_wc[1].replace(wc_post, '') + split_wc.pop() + out_wildcard = ', '.join(split_wc) + temp_fixed[i] = out_wildcard + itc += 1 + if not wildcard_present: + break + temp_fixed_result = ', '.join(temp_fixed) + if global_temp: temp_fixed_result += ', '+', '.join(global_temp) + self.previous_fix_prompt = temp_fixed_result + else: + temp_fixed_result = self.previous_fix_prompt + self.wildcard_preopen_repeat_current += 1 + if isinstance(popped_row, pd.DataFrame): popped_row = popped_row.iloc[0] + #기회될 때 이부분 dictionary로 싹 정리 + prompt, self.current_prompt_rating = NAIA_random_function_core.RFP(popped_row, temp_fixed_result, self.fixed_prompt_after_input.get("0.0", "end")+rmc, self.auto_hide_keyword_input.get("0.0", "end") + ,self.rm_artist_name_button.get(), self.rm_copyright_name_button.get(),self.rm_characteristic_button.get(), self.rm_location_button.get(), self.rm_color_button.get(), self.rm_not_nsfw_button.get(), self.data, magic_word) + else: + if isinstance(popped_row, pd.DataFrame): popped_row = popped_row.iloc[0] + prompt, self.current_prompt_rating = NAIA_random_function_core.RFP(popped_row, self.fixed_prompt_input.get("0.0", "end"), self.fixed_prompt_after_input.get("0.0", "end")+rmc, self.auto_hide_keyword_input.get("0.0", "end") + ,self.rm_artist_name_button.get(), self.rm_copyright_name_button.get(),self.rm_characteristic_button.get(), self.rm_location_button.get(), self.rm_color_button.get(), self.rm_not_nsfw_button.get(), self.data, magic_word) + self.text_input.delete("0.0", "end") + self.text_input.insert("0.0", prompt) + app.after(100, lambda: self.random_function_button.configure(state="normal")) + self.random_function_pressed = False + app.after(100, highlight_text) + + self.random_function_pressed = False + self.running_flag = False + self.error_count = 0 + self.turbo_fail_count = 0 + self.anlas_request = False + self.image_generation_repeat_prompt = "" + + def NAIA_generate(self): + if self.seed_fix_button.get() == 0: + self.entry_seed_value.set(random.randint(0,9999999999)) + if self.random_resolution_button.get() == 1: + resolutions = ["1024 x 1024", "960 x 1088", "896 x 1152", "832 x 1216", "1088 x 960", "1152 x 896", "1216 x 832"] + random_resolution = random.choice(resolutions) + self.resolution_button.set(random_resolution) + if self.cached_rows is None and self.toggle_prompt_fix_button.get() == 0 and self.automation_button.get() == 1: + self.state_label.configure(text ="state : 자동화 전 프롬프트 검색을 수행하는 중 입니다", text_color = "#FFFF97") + prompt_search() + if self.turbo_button.get() == 1: + origin = self.text_input.get("0.0", "end-1c") + pretest = [keyword.strip() for keyword in origin.split(',')] + if ('*(split nsfw)' in pretest or '*(split nsfw)-rand' in pretest): + pass + elif (('sex' not in pretest and 'group sex' not in pretest) or ('1girl' not in pretest and 'girls' not in origin[:20]) or ('1boy' not in pretest and 'boys' not in origin[:20])): + self.image_label_report.configure(state="normal") + self.image_label_report.delete("0.0", "end") + self.image_label_report.insert("0.0", " Explicit Turbo 기능은 프롬프트 내에 1girl/girls, 1boy/boys, sex/group sex가 존재할 때 사용 가능합니다.") + self.turbo_fail_count += 1 + if self.turbo_fail_count >= 20: + self.turbo_button.deselect() + self.image_label_report.configure(text_color="#FFFF97") + self.image_label_report.configure(state="disabled") + if self.automation_button.get() == 1 and self.toggle_prompt_fix_button.get() == 0: + random_function() + time.sleep(0.5) + if self.searching_flag == True: + self.search_thread.join() + return self.event_generate(GENERATE_EVENT, when="tail") + elif self.automation_button.get() == 1 and self.toggle_prompt_fix_button.get() == 1: + self.automation_button.deselect() + return + else: + return + self.turbo_fail_count = 0 + + if not self.running_flag: + self.image_generation_button.configure(state="disabled") + self.running_flag = True + NAI_width, NAI_height = self.resolution_button.get().split('x') + NAI_width = NAI_width.strip() + NAI_height = NAI_height.strip() + NAI_width = str((int(NAI_width) // 64) * 64) + NAI_height = str((int(NAI_height) // 64) * 64) + if int(NAI_height) * int(NAI_width) > 1048576: + self.image_label_report.configure(state="normal") + self.image_label_report.delete("0.0", "end") + self.image_label_report.insert("0.0", " Anlas가 소모되는 해상도 요청입니다.") + self.image_label_report.configure(text_color="#FFFF97") + self.image_label_report.configure(state="disabled") + self.anlas_request = True + scale_pre = self.cfg_scale_entry.get() + try: + scale_pre = float(scale_pre) + except: + scale_pre = 5.0 + self.cfg_scale_var.set("5.0") + rescale_pre = self.prompt_guidance_rescale_entry.get() + try: + rescale_pre = float(rescale_pre) + except: + rescale_pre = 0 + self.prompt_guidance_rescale_var.set("0") + uncond_pre = self.uncond_strength_entry.get() + try: + uncond_pre = float(uncond_pre) + uncond_pre = round(uncond_pre / 0.05) * 0.05 + if (uncond_pre > 1.5): + uncond_pre = 1.5 + self.uncond_strength_entry.delete(0, "end") + self.uncond_strength_entry.insert(0, "1.5") + except: + uncond_pre = 1.0 + self.uncond_strength_entry.delete(0, "end") + self.uncond_strength_entry.insert(0, "1.0") + #### 여기서부터 와일드카드 처리 구문 #### + if self.image_generation_repeat_flag == False: + before_wildcard = self.text_input.get("0.0", "end-1c") + before_wildcard = [item.strip() for item in before_wildcard.split(',')] + if '<' in self.text_input.get("0.0", "end-1c"): + wildcard_present = True + itc = 0 + while (wildcard_present and itc < 10): + wildcard_present = False + #### 단계 1 : 인스턴트 와일드카드 처리 ### + for i, keyword in enumerate(before_wildcard): + if keyword.startswith('<') and keyword.endswith('>'): + wildcard_present = True + vbar_check = keyword[1:-1] + if '|' in vbar_check: + choices = vbar_check.split('|') # '|'를 기준으로 split + choice_dic = {} + for choice in choices: + match = re.match(r'(\d*\.?\d+):(.+)', choice) + if match: + value, keyword = float(match.group(1)), match.group(2).strip() + else: + value, keyword = 1, choice.strip() + choice_dic[keyword] = value + keywords = list(choice_dic.keys()) + weights = list(choice_dic.values()) + selected_instant_wildcard = random.choices(keywords, weights=weights, k=1)[0] + before_wildcard[i] = selected_instant_wildcard + #### 단계 2 : 글로벌 와일드카드 처리 ### + for i, keyword in enumerate(before_wildcard): + if "<" in keyword and ">" in keyword: + wildcard_present = True + input_str = keyword.strip('<>').strip() + if("__" in input_str): + adjectives = re.findall(r'__(.*?)__', input_str) + last_keyword = re.split(r'__.*?__', input_str)[-1] + adjective_string = "" + for adjective in adjectives: + adjective_string += (self.get_wildcard(adjective) + " ") + before_wildcard[i] = adjective_string + self.get_wildcard(last_keyword) + else: + before_wildcard[i] = self.get_wildcard(input_str) + itc += 1 + if not wildcard_present: + break + else: + after_wildcard = self.text_input.get("0.0", "end-1c") + after_wildcard = ', '.join(before_wildcard) + if self.image_generation_repeat > 1 and self.hold_wildcard: + if self.image_generation_repeat_flag == False: + self.image_generation_repeat_flag = True + self.image_generation_repeat_prompt = after_wildcard + self.image_generation_repeat_current = 1 + else: + after_wildcard = self.image_generation_repeat_prompt + self.image_generation_repeat_current += 1 + elif self.image_generation_repeat > 1 and not self.hold_wildcard: + self.image_generation_repeat_current += 1 + ############################## + gen_request = { + "width":NAI_width, + "height":NAI_height, + "quality_toggle":self.auto_quality_toggle.get(), + "seed":self.seed_entry.get(), + "sampler":self.sampler_button.get(), + "scale":scale_pre, + "uncond_scale":uncond_pre, + "sema":self.sema_button.get(), + "sema_dyn": self.dyn_button.get(), + "cfg_rescale": rescale_pre, + "prompt": after_wildcard, + "negative":self.negative_prompt_input.get("0.0", "end-1c"), + "user_screen_size": self.get_max_size(), + "start_time": self.start_time, + "access_token": self.access_token, + "save_folder": self.output_file_path, + "png_rule": self.name_var.get(), + "type": "normal", + "rating":self.current_prompt_rating + } + if self.cond_negative_button_var.get() == 1: + gen_request["cond_negative"] = self.conditional_negative_input.get("0.0", "end-1c") + + if self.turbo_button.get() == 1 and "*(split nsfw)"in gen_request["prompt"]: + request_list = [] + for i in range(self.image_generation_repeat): + if i != 0: + cvalue = random.randint(0,9999999999) + gen_request["seed"] = cvalue + pre_prompt = gen_request["prompt"] + pre_prompt = [keyword.strip() for keyword in pre_prompt.split(',')] + fix_words = self.fixed_prompt_input.get("0.0", "end-1c")+", "+self.fixed_prompt_after_input.get("0.0", "end-1c") + fix_words = [keyword.strip() for keyword in fix_words.split(',')] + if "*(split nsfw)-rand" in pre_prompt: + pre_prompt.remove("*(split nsfw)-rand") + random_seed = True + else: + pre_prompt.remove("*(split nsfw)") + random_seed = False + nsfw_word = [] + colors = ['black','white','blond','silver','gray','yellow','blue','purple','red','pink','brown','orange','green','aqua','gradient'] + patterns = ['polka dot', 'stripe', 'camouflage', 'plaid', 'filter', 'leotard', 'multicolored', 'strawberry', 'print', 'tone', 'trimmed', 'satin'] + qe_word = app.data.get_qe_word() + ['cross-section', 'x-ray'] + for keyword in pre_prompt: #전처리 + if 'mouth' in keyword or 'eyes' in keyword: + if keyword not in fix_words: + pre_prompt.remove(keyword) + for keyword in pre_prompt: + if '{' not in keyword and '[' not in keyword and keyword in qe_word and all(color not in keyword for color in colors) and all(pattern not in keyword for pattern in patterns): + nsfw_word.append(keyword) + if len(nsfw_word) != 0: + basket = [] + for keyword in nsfw_word: + if 'cross-section' in keyword or 'x-ray' in keyword: + if keyword not in basket: basket.append(keyword) + for keyword in nsfw_word: + if 'internal cumshot' in keyword or 'ejaculation' in keyword: + if keyword not in basket: basket.append(keyword) + for keyword in nsfw_word: + if 'cum' in keyword or 'ahegao' in keyword: + if keyword not in basket: basket.append(keyword) + for keyword in nsfw_word: + if 'after ' in keyword: + if keyword not in basket: basket.append(keyword) + for keyword in basket: + if keyword in nsfw_word: + nsfw_word.remove(keyword) + nsfw_word += basket + words = [] + if len(nsfw_word) > 13: + max_val = len(nsfw_word) // 4 + if len(nsfw_word) % 4 > 0: max_val += 1 + for i in range(max_val): + current_word = [] + if i < max_val - 1: + words.append(nsfw_word[i*4]) + words.append(nsfw_word[i*4+1]) + words.append(nsfw_word[i*4+2]) + words.append(nsfw_word[i*4+3]) + current_word.append(nsfw_word[i*4]) + current_word.append(nsfw_word[i*4+1]) + current_word.append(nsfw_word[i*4+2]) + current_word.append(nsfw_word[i*4+3]) + else: + if len(nsfw_word) % 4 == 0: + words.append(nsfw_word[i*4]) + words.append(nsfw_word[i*4+1]) + words.append(nsfw_word[i*4+2]) + words.append(nsfw_word[i*4+3]) + current_word.append(nsfw_word[i*4]) + current_word.append(nsfw_word[i*4+1]) + current_word.append(nsfw_word[i*4+2]) + current_word.append(nsfw_word[i*4+3]) + elif len(nsfw_word) % 4 == 3: + words.append(nsfw_word[i*4]) + words.append(nsfw_word[i*4+1]) + words.append(nsfw_word[i*4+2]) + current_word.append(nsfw_word[i*4]) + current_word.append(nsfw_word[i*4+1]) + current_word.append(nsfw_word[i*4+2]) + elif len(nsfw_word) % 4 == 2: + words.append(nsfw_word[i*4]) + words.append(nsfw_word[i*4+1]) + current_word.append(nsfw_word[i*4]) + current_word.append(nsfw_word[i*4+1]) + else: + words.append(nsfw_word[i*4]) + current_word.append(nsfw_word[i*4]) + gen_request_sub = copy.deepcopy(gen_request) + temp_prompt = []+pre_prompt + for keyword in nsfw_word: + if keyword not in words: + if random_seed == False: + temp_prompt[temp_prompt.index(keyword)] = " "*len(keyword) + else: + temp_prompt.remove(keyword) + elif keyword in current_word: + temp_prompt[temp_prompt.index(keyword)] = "{"+keyword+"}" + gen_request_sub["prompt"] = ', '.join(temp_prompt) + if random_seed: + gen_request_sub["seed" ] = random.randint(0,9999999999) + request_list.append(copy.deepcopy(gen_request_sub)) + elif len(nsfw_word) > 8: + max_val = len(nsfw_word) // 3 + if len(nsfw_word) % 3 > 0: max_val += 1 + for i in range(max_val): + current_word = [] + if i < max_val - 1: + words.append(nsfw_word[i*3]) + words.append(nsfw_word[i*3+1]) + words.append(nsfw_word[i*3+2]) + current_word.append(nsfw_word[i*3]) + current_word.append(nsfw_word[i*3+1]) + current_word.append(nsfw_word[i*3+2]) + else: + if len(nsfw_word) % 3 == 0: + words.append(nsfw_word[i*3]) + words.append(nsfw_word[i*3+1]) + words.append(nsfw_word[i*3+2]) + current_word.append(nsfw_word[i*3]) + current_word.append(nsfw_word[i*3+1]) + current_word.append(nsfw_word[i*3+2]) + elif len(nsfw_word) % 3 == 2: + words.append(nsfw_word[i*3]) + words.append(nsfw_word[i*3+1]) + current_word.append(nsfw_word[i*3]) + current_word.append(nsfw_word[i*3+1]) + else: + words.append(nsfw_word[i*3]) + current_word.append(nsfw_word[i*3]) + gen_request_sub = copy.deepcopy(gen_request) + temp_prompt = []+pre_prompt + for keyword in nsfw_word: + if keyword not in words: + if random_seed == False: + temp_prompt[temp_prompt.index(keyword)] = " "*len(keyword) + else: + temp_prompt.remove(keyword) + elif keyword in current_word: + temp_prompt[temp_prompt.index(keyword)] = "{"+keyword+"}" + gen_request_sub["prompt"] = ', '.join(temp_prompt) + if random_seed: + gen_request_sub["seed" ] = random.randint(0,9999999999) + request_list.append(copy.deepcopy(gen_request_sub)) + elif len(nsfw_word) > 3: + max_val = len(nsfw_word) // 2 + len(nsfw_word) % 2 + for i in range(max_val): + current_word = [] + if i < max_val - 1: + words.append(nsfw_word[i*2]) + words.append(nsfw_word[i*2+1]) + current_word.append(nsfw_word[i*2]) + current_word.append(nsfw_word[i*2+1]) + else: + if len(nsfw_word) % 2 == 0: + words.append(nsfw_word[i*2]) + words.append(nsfw_word[i*2+1]) + current_word.append(nsfw_word[i*2]) + current_word.append(nsfw_word[i*2+1]) + else: + words.append(nsfw_word[i*2]) + current_word.append(nsfw_word[i*2]) + gen_request_sub = copy.deepcopy(gen_request) + temp_prompt = []+pre_prompt + for keyword in nsfw_word: + if keyword not in words: + if random_seed == False: + temp_prompt[temp_prompt.index(keyword)] = " "*len(keyword) + else: + temp_prompt.remove(keyword) + elif keyword in current_word: + temp_prompt[temp_prompt.index(keyword)] = "{"+keyword+"}" + gen_request_sub["prompt"] = ', '.join(temp_prompt) + if random_seed: + gen_request_sub["seed" ] = random.randint(0,9999999999) + request_list.append(copy.deepcopy(gen_request_sub)) + else: + for word in nsfw_word: + words.append(word) + gen_request_sub = copy.deepcopy(gen_request) + temp_prompt = []+pre_prompt + for keyword in nsfw_word: + if keyword not in words: + if random_seed == False: + temp_prompt[temp_prompt.index(keyword)] = " "*len(keyword) + else: + temp_prompt.remove(keyword) + elif keyword == word: + temp_prompt[temp_prompt.index(keyword)] = "{"+word+"}" + gen_request_sub["prompt"] = ', '.join(temp_prompt) + if random_seed: + gen_request_sub["seed" ] = random.randint(0,9999999999) + request_list.append(copy.deepcopy(gen_request_sub)) + else: + request_list.append(gen_request) + elif self.turbo_button.get() == 1: + request_list = [] + for i in range(self.image_generation_repeat): + if i != 0: + cvalue = random.randint(0,9999999999) + gen_request["seed"] = cvalue + try: + treq_0, treq_1, treq_2, treq_3 = NAIA_generation.make_turbo_prompt(gen_request) + request_list.append(gen_request.copy()) + request_list.append(treq_0) + request_list.append(treq_1) + request_list.append(treq_2) + request_list.append(treq_3) + except: + request_list.append(gen_request) + else: + pre_prompt = gen_request["prompt"] + pre_prompt = [keyword.strip() for keyword in pre_prompt.split(',')] + if "*(split nsfw)-rand" in pre_prompt: + pre_prompt.remove("*(split nsfw)-rand") + gen_request["prompt"] = ', '.join(pre_prompt) + + def run_generation(): + if gen_request["png_rule"] == "count": + self.generation_count += 1 + gen_request["count"] =self.generation_count + if app.auto_count_left_flag == True: + app.auto_count_left -= 1 + if self.image_generation_repeat_flag == True: + gen_request["repeat"] = self.image_generation_repeat_current -1 + gen_request["repeat_max"] = self.image_generation_repeat + app.image_generation_button.configure(border_width = 2) + pass_bit = False + retry_count = 0 + while(not pass_bit): + self.state_label.configure(text ="state : NAI 이미지 생성 대기중 ... ", text_color = "#FFFF97") + result_image, result_prompt, result_seed, info, filename = NAIA_generation.generate(gen_request) + self.state_label.configure(text ="state : idle", text_color = "#DCE4EE") + app.image_generation_button.configure(border_width = 0) + if self.anlas_request: + self.anlas_request = False + self.get_anlas() + if info: + self.error_count = 0 + temp = info.get('Comment', '') + temp = temp[temp.find("prompt")+10:temp.find("skip_cfg_below_sigma")-3].replace('"','') + pass_bit = True + else: + self.error_count += 1 + retry_count += 1 + if self.error_count >= 10: + self.automation_button.deselect() + temp = result_prompt + if self.automation_button.get() == 1: + instant_wait = random.uniform(3.5, 5.5) + self.delay_offset + if instant_wait < 0 : instant_wait = 0.5 + time.sleep(instant_wait) + else: + time.sleep(1) + if self.automation_button.get() == 0 or retry_count >= 5: + break + self.running_flag = False + self.image_label_report.configure(state="normal") + self.image_label_report.delete("0.0", "end") + self.image_label_report.configure(text_color="#DCE4EE") + self.image_label_report.insert("0.0", temp) + self.image_label_report.configure(state="disabled") + if result_image: + if app.state() != 'zoomed': + instant_result_image = customtkinter.CTkImage(result_image, size=(620,620)) + else: + current_image = Image.open(filename) + original_width, original_height = current_image.size + max_size = app.winfo_screenheight()-100 + if original_width > max_size or original_height > max_size: + new_image = Image.new("RGB", (max_size, max_size), "black") + new_image.paste(current_image, ((max_size - original_width) // 2, (max_size - original_height) // 2)) + instant_result_image = customtkinter.CTkImage(new_image, size=(max_size, max_size)) + else: + instant_result_image = customtkinter.CTkImage(current_image, size=(max_size, max_size)) + self.image_label.configure(image=instant_result_image) + set_image_to_queue(result_image, result_prompt, result_seed, filename) + #자동화 체크 + if self.automation_button.get() == 1: + instant_wait = random.uniform(3.5, 5.5) + self.delay_offset + if instant_wait < 0 : instant_wait = 0.5 + if self.image_generation_repeat > 1 and self.hold_wildcard == False and (self.image_generation_repeat > self.image_generation_repeat_current): + pass + elif self.image_generation_repeat_flag == False or (self.image_generation_repeat <= self.image_generation_repeat_current): + self.image_generation_repeat_current = 0 + self.image_generation_repeat_flag = False + if self.toggle_prompt_fix_button.get() == 0: + random_function() + #반복생성 처리구문, 트래킹 필요 + while(instant_wait > 0): + self.image_generation_button.configure(text=f"NAI 이미지 생성 ({round(instant_wait)})") + if instant_wait >= 1: + time.sleep(1) + else: + time.sleep(instant_wait) + instant_wait -= 1 + self.image_generation_button.configure(text="NAI 이미지 생성") + if self.automation_button.get() == 1: + self.event_generate(GENERATE_EVENT, when="tail") + else: + self.image_generation_button.configure(state="normal") + else: + self.image_generation_repeat_current = 0 + self.image_generation_repeat_flag = False + self.image_generation_button.configure(state="normal") + def run_generation_turbo(gen_request): + if gen_request["png_rule"] == "count": + self.generation_count += 1 + gen_request["count"] =self.generation_count + if app.auto_count_left_flag == True: + app.auto_count_left -= 1 + app.image_generation_button.configure(border_width = 2) + pass_bit = False + retry_count = 0 + while(not pass_bit): + self.state_label.configure(text ="state : NAI ���미지 생성 대기중 (EXP.turbo) ... ", text_color = "#FFFF97") + result_image, result_prompt, result_seed, info, filename = NAIA_generation.generate(gen_request) + self.state_label.configure(text ="state : idle", text_color = "#DCE4EE") + app.image_generation_button.configure(border_width = 0) + if info: + self.error_count = 0 + temp = info.get('Comment', '') + temp = temp[temp.find("prompt")+10:temp.find("skip_cfg_below_sigma")-3].replace('"','') + pass_bit = True + else: + self.error_count += 1 + retry_count += 1 + if self.error_count >= 10: + self.automation_button.deselect() + self.turbo_button.deselect() + temp = result_prompt + if self.automation_button.get() == 1: + instant_wait = random.uniform(4.5, 8.5) + self.delay_offset + if instant_wait < 0 : instant_wait = 0.5 + time.sleep(instant_wait) + else: + time.sleep(1) + if self.turbo_button.get() == 0 or retry_count >= 5: + break + self.running_flag = False + self.image_label_report.configure(state="normal") + self.image_label_report.delete("0.0", "end") + self.image_label_report.configure(text_color="#DCE4EE") + self.image_label_report.insert("0.0", temp) + self.image_label_report.configure(state="disabled") + if result_image: + if app.state() != 'zoomed': + instant_result_image = customtkinter.CTkImage(result_image, size=(620,620)) + else: + current_image = Image.open(filename) + original_width, original_height = current_image.size + max_size = app.winfo_screenheight()-100 + if original_width > max_size or original_height > max_size: + new_image = Image.new("RGB", (max_size, max_size), "black") + new_image.paste(current_image, ((max_size - original_width) // 2, (max_size - original_height) // 2)) + instant_result_image = customtkinter.CTkImage(new_image, size=(max_size, max_size)) + else: + instant_result_image = customtkinter.CTkImage(current_image, size=(max_size, max_size)) + self.image_label.configure(image=instant_result_image) + set_image_to_queue(result_image, result_prompt, str(result_seed), filename) + instant_wait = random.uniform(4.5, 8.5) + self.delay_offset + if instant_wait < 0 : instant_wait = 0.5 + while(instant_wait > 0): + self.image_generation_button.configure(text=f"NAI 이미지 생성 ({round(instant_wait)})") + if instant_wait >= 1: + time.sleep(1) + else: + time.sleep(instant_wait) + instant_wait -= 1 + self.image_generation_button.configure(text="NAI 이미지 생성") + + def turbo_process_request(): + turbo_count = 0 + while(request_list): + gen_request = request_list.pop(0) #length=5 + generation_thread = threading.Thread(target=run_generation_turbo, args=(gen_request,), daemon=True) + generation_thread.start() + generation_thread.join() + turbo_count += 1 + if app.turbo_button.get() == 0: + app.state_label.configure(text =f"state : 사용자에 의한 터보요청 중단 ({turbo_count})", text_color = "#FFFF97") + break + if self.automation_button.get() == 1: + if self.toggle_prompt_fix_button.get() == 0: + random_function() + self.event_generate(GENERATE_EVENT, when="tail") + else: + self.image_generation_button.configure(state="normal") + + if self.turbo_button.get() == 0: + generation_thread = threading.Thread(target=run_generation, daemon=True) + generation_thread.start() + else: + turbo_process_request_thread = threading.Thread(target=turbo_process_request, daemon=True) + turbo_process_request_thread.start() + + def open_AutomationSetting(self): + if self.Automation_setting is None: + self.Automation_setting = Automation_setting(self) + else: + if self.Automation_setting.state() == 'withdrawn': + self.Automation_setting.deiconify() # 숨겨진 윈도우를 다시 화면에 표시 + else: + self.Automation_setting.focus() + + def show_advanced_settings(self): + if self.Advanced_setting is None: + self.Advanced_setting = Advanced_setting(self) + else: + if self.Advanced_setting.state() == 'withdrawn': + self.Advanced_setting.deiconify() + else: + self.Advanced_setting.focus() + + def open_Character_search(self): + if self.Character_search is None: + if not os.path.exists("csdataset.parquet"): + make_parquet(self) + self.Character_search = Character_search(self) + else: + if self.Character_search.state() == 'withdrawn': + self.Character_search.deiconify() + else: + self.Character_search.focus() + + def make_parquet(self): + filtered_dfs = [] + df = pd.read_parquet(os.path.join(basedir, "tags.parquet"),engine="pyarrow") + filtered_df = df[df['general'].str.contains(' solo,', na=False) & ~df['general'].str.contains('monochrome', na=False)] + filtered_dfs.append(filtered_df) + final_df = pd.concat(filtered_dfs, ignore_index=True) + final_df.to_parquet("csdataset.parquet") + + + + self.Advanced_setting = None + self.Automation_setting = None + self.Character_search = None + #이미지 생성 프레임 + self.image_generation_frame = customtkinter.CTkFrame(self.text_input_frame) + self.image_generation_frame.grid(row=2, column=0, sticky="nsew") + self.image_generation_frame.columnconfigure(0, weight= 1) + self.image_generation_frame.columnconfigure(1, weight= 1) + self.image_generation_frame.columnconfigure(2, weight= 1) + self.random_function_button = customtkinter.CTkButton(self.image_generation_frame, text="랜덤/다음 프롬프트", font=my_font, command=random_function) + self.random_function_button.grid(row=0, column=0, pady=5, sticky="nsew") + self.automation_setting_button = customtkinter.CTkButton(self.image_generation_frame, text="자동화 설정", fg_color="#CDCDCD", text_color="black", hover_color="#848484", font=my_font, command=lambda: open_AutomationSetting(self)) + self.automation_setting_button.grid(row=0, column=1, padx=15, sticky="ew") + self.image_generation_button = customtkinter.CTkButton(self.image_generation_frame, text="NAI 이미지 생성", fg_color="#ED7D31", hover_color="#CC5D12", font=my_font, state="disabled", command=lambda: NAIA_generate(self), text_color_disabled="black") + self.image_generation_button.grid(row=0, column=2, pady=5, sticky="nsew") + + def hold_prompt(): + if self.toggle_prompt_fix_button.get() == 1: + if self.unlock_hold_prompt_var.get() == 0: + self.fixed_prompt_input.configure(state="disabled", text_color="#A2B8D2") + self.fixed_prompt_after_input.configure(state="disabled", text_color="#A2B8D2") + self.random_function_button.configure(state="disabled") + self.rm_not_nsfw_button.deselect() + self.rm_not_nsfw_button.configure(state="disabled") + self.random_artist_button.deselect() + self.random_artist_button.configure(state="disabled") + self.unlock_hold_prompt.configure(state="normal") + else: + self.fixed_prompt_input.configure(state="normal", text_color="#DCE4EE") + self.fixed_prompt_after_input.configure(state="normal", text_color="#DCE4EE") + self.random_function_button.configure(state="normal") + self.rm_not_nsfw_button.configure(state="normal") + self.random_artist_button.configure(state="normal") + + def explicit_user_attention(): + if self.turbo_button.get() == 1: + self.image_label_report.configure(state="normal") + self.image_label_report.delete("0.0","end") + self.image_label_report.insert("0.0", " Explicit turbo 생성 도중 중단 희망시 체크박스를 해제 하시기 바랍니다.") + self.image_label_report.configure(text_color="#FFFF97") + self.image_label_report.configure(state="disabled") + else: + self.image_label_report.configure(text_color="#DCE4EE") + + self.toggle_prompt_fix = customtkinter.IntVar() + self.toggle_prompt_fix_button = customtkinter.CTkCheckBox(self.image_generation_frame, text="프롬프트 고정", variable=self.toggle_prompt_fix, font=my_font, command=hold_prompt) + self.toggle_prompt_fix_button.grid(row=1, column=0, sticky="ew", padx=15, pady=5) + self.automation = customtkinter.IntVar() + self.automation_button = customtkinter.CTkCheckBox(self.image_generation_frame, text="자동화", variable=self.automation, font=my_font) + self.automation_button.grid(row=1, column=1, sticky="ew", padx=15) + self.turbo = customtkinter.IntVar() + self.turbo_button = customtkinter.CTkCheckBox(self.image_generation_frame, text="연속 이미지 생성(Explicit)", variable=self.turbo, font=my_font, command=explicit_user_attention) + self.turbo_button.grid(row=1, column=2, sticky="ew") + + def fixed_prompt_label_command(): + if self.fixed_prompt_label_var.get() == 0: + self.fixed_prompt_input.grid_forget() + else: + self.fixed_prompt_input.grid(row=4, column=0, pady=5, sticky="nsew") + + def highlight_text_fixed(event=None): + if '#' in self.fixed_prompt_input.get("1.0", tk.END): + # 기존의 하이라이트를 모두 제거 + self.fixed_prompt_input.tag_remove("highlight", "1.0", tk.END) + + # '#'으로 시작하여 ','나 '\n', 또는 텍스트 끝까지 이어지는 모든 부분을 찾음 + for match in re.finditer(r'#.*?([,\n]|$)', self.fixed_prompt_input.get("1.0", tk.END)): + start = "1.0 + {}c".format(match.start()) + end = "1.0 + {}c".format(match.end()) + self.fixed_prompt_input.tag_add("highlight", start, end) + + self.fixed_prompt_label_var = customtkinter.IntVar(value=1) + self.fixed_prompt_label = customtkinter.CTkCheckBox(self.text_input_frame, text=" ----------------------------- 선행 고정 프롬프트 ----------------------------- ", fg_color="grey10", hover_color="#50164A", font=large_font, variable=self.fixed_prompt_label_var, command=fixed_prompt_label_command, border_color="#333333") + self.fixed_prompt_label.grid(row = 3, column=0, sticky="w" ) + self.fixed_prompt_input = customtkinter.CTkTextbox(self.text_input_frame, width=490, height=self.ui_size['prefix'], font=v_large_font, undo=True) + self.fixed_prompt_input.grid(row=4, column=0, pady=5, sticky="nsew") + self.fixed_prompt_input.bind("", highlight_text_fixed) + self.fixed_prompt_input.tag_config("highlight", foreground="#FFFF97") + + #작가명 제거 및 기타 기능 + self.check_box_frame = customtkinter.CTkFrame(self.text_input_frame, width=490) + self.check_box_frame.grid(row=5, column=0, pady=5, columnspan=3, sticky="nsew") + self.rm_artist_name_var = customtkinter.IntVar() + self.rm_artist_name_button = customtkinter.CTkCheckBox(self.check_box_frame, text="작가명 제거", variable=self.rm_artist_name_var, font=my_font) + self.rm_artist_name_button.grid(row=0, column=0, pady=5, padx=10, sticky="nsew") + self.rm_copyright_name_var = customtkinter.IntVar() + self.rm_copyright_name_button = customtkinter.CTkCheckBox(self.check_box_frame, text="작품명 제거", variable=self.rm_copyright_name_var, font=my_font) + self.rm_copyright_name_button.grid(row=0, column=1, pady=5, sticky="nsew") + + + def rm_characteristic(): + if self.rm_characteristic_var.get() == 1: + self.rm_character_name_button.deselect() + + self.rm_characteristic_var = customtkinter.IntVar() + self.rm_characteristic_button = customtkinter.CTkCheckBox(self.check_box_frame, text="캐릭터 특징 제거", variable=self.rm_characteristic_var, font=my_font, command=rm_characteristic) + self.rm_characteristic_button.grid(row=0, column=2, padx=10, pady=5, sticky="nsew") + + + def open_extend_frame(): + if self.rm_extend_var.get() == 1: + self.rm_extend_frame.grid(row=1, column=0, columnspan=4, pady=5, sticky="nsew") + else: + self.rm_extend_frame.grid_forget() + + self.rm_extend_frame = customtkinter.CTkFrame(self.check_box_frame, fg_color="#333333") + + self.rm_extend_var = customtkinter.IntVar() + self.rm_extend_button = customtkinter.CTkCheckBox(self.check_box_frame, text="제거 확장(+)", variable=self.rm_extend_var, font=my_font, command=open_extend_frame) + self.rm_extend_button.grid(row=0, column=3, padx=5, pady=5, sticky="nsew") + + self.rm_not_nsfw_var = customtkinter.IntVar() + self.rm_not_nsfw_button = customtkinter.CTkCheckBox(self.rm_extend_frame, text="NSFW 태그를 제외하고 전부 제거", variable=self.rm_not_nsfw_var, font=my_font) + + self.rm_color_var = customtkinter.IntVar() + self.rm_color_button = customtkinter.CTkCheckBox(self.rm_extend_frame, text="의류 색상 제거", variable=self.rm_color_var, font=my_font) + + self.rm_location_var = customtkinter.IntVar() + self.rm_location_button = customtkinter.CTkCheckBox(self.rm_extend_frame, text="장소/배경색 제거", variable=self.rm_location_var, font=my_font) + + def yield_rm_character_name(): + if self.rm_character_name_var.get() == 1: + self.rm_characteristic_button.deselect() + + self.rm_character_name_var = customtkinter.IntVar() + self.rm_character_name_button = customtkinter.CTkCheckBox(self.rm_extend_frame, text="캐릭터 이름만 제거", variable=self.rm_character_name_var, font=my_font, command=yield_rm_character_name) + + self.rm_color_button.grid(row=0, column=0, pady=5, padx=10, sticky="nsew") + self.rm_location_button.grid(row=0, column=1, pady=5, sticky="nsew") + self.rm_not_nsfw_button.grid(row=0, column=2, pady=5, padx=5, sticky="w") + self.rm_character_name_button.grid(row=1, column=0, pady=5, padx=10, sticky="nsew") + + + + def fixed_prompt_after_label_command(): + if self.fixed_prompt_after_label_var.get() == 0: + self.fixed_prompt_after_input.grid_forget() + else: + self.fixed_prompt_after_input.grid(row=7, column=0, pady=5, sticky="nsew") + + def highlight_text_after(event=None): + if '#' in self.fixed_prompt_after_input.get("1.0", tk.END): + # 기존의 하이라이트를 모두 제거 + self.fixed_prompt_after_input.tag_remove("highlight", "1.0", tk.END) + + # '#'으로 시작하여 ','나 '\n', 또는 텍스트 끝까지 이어지는 모든 부분을 찾음 + for match in re.finditer(r'#.*?([,\n]|$)', self.fixed_prompt_after_input.get("1.0", tk.END)): + start = "1.0 + {}c".format(match.start()) + end = "1.0 + {}c".format(match.end()) + self.fixed_prompt_after_input.tag_add("highlight", start, end) + + self.fixed_prompt_after_label_var = customtkinter.IntVar(value=1) + self.fixed_prompt_after_label = customtkinter.CTkCheckBox(self.text_input_frame, text=" ----------------------------- 후행 고정 프롬프트 ----------------------------- ", fg_color="grey10", hover_color="#50164A", font=large_font, variable=self.fixed_prompt_after_label_var, command=fixed_prompt_after_label_command, border_color="#333333") + self.fixed_prompt_after_label.grid(row = 6, column=0, sticky="w" ) + self.fixed_prompt_after_input = customtkinter.CTkTextbox(self.text_input_frame, width=490, height=self.ui_size['postfix'], font=v_large_font, undo=True) + self.fixed_prompt_after_input.grid(row=7, column=0, pady=5, sticky="nsew") + self.negative_prompt_label_frame = customtkinter.CTkFrame(self.text_input_frame, fg_color="#2B2B2B") + self.negative_prompt_label_frame.grid(row = 8, column=0, sticky="nsew" ) + self.fixed_prompt_after_input.bind("", highlight_text_after) + self.fixed_prompt_after_input.tag_config("highlight", foreground="#FFFF97") + + def negative_prompt_label_command(): + if self.negative_prompt_label_var.get() == 0: + self.negative_prompt_input.grid_forget() + else: + self.negative_prompt_input.grid(row=9, column=0, pady=5, sticky="nsew") + + def highlight_text_negative(event=None): + if '#' in self.negative_prompt_input.get("1.0", tk.END): + # 기존의 하이라이트를 모두 제거 + self.negative_prompt_input.tag_remove("highlight", "1.0", tk.END) + + # '#'으로 시작하여 ','나 '\n', 또는 텍스트 끝까지 이어지는 모든 부분을 찾음 + for match in re.finditer(r'#.*?([,\n]|$)', self.negative_prompt_input.get("1.0", tk.END)): + start = "1.0 + {}c".format(match.start()) + end = "1.0 + {}c".format(match.end()) + self.negative_prompt_input.tag_add("highlight", start, end) + + self.negative_prompt_label_var = customtkinter.IntVar(value=1) + self.negative_prompt_label = customtkinter.CTkCheckBox(self.negative_prompt_label_frame, text=" ---------------------------- 네거티브 프롬프트 ------------- Strength : ", fg_color="grey10", hover_color="#50164A", font=large_font, variable=self.negative_prompt_label_var, command=negative_prompt_label_command,border_color="#333333" ) + self.negative_prompt_label.grid(row = 0, column=0, sticky="w" ) + self.uncond_strength_entry = customtkinter.CTkEntry(self.negative_prompt_label_frame, font=my_font, width=50) + self.uncond_strength_entry.grid(row = 0, column=1, sticky="e" ) + self.uncond_strength_entry.insert(0, "1.00") + self.negative_prompt_input = customtkinter.CTkTextbox(self.text_input_frame, width=490, height=self.ui_size['negative'], font=v_large_font, undo=True) + self.negative_prompt_input.insert("0.0", "lowres, jpeg artifacts, worst quality, watermark, blurry, very displeasing") + self.negative_prompt_input.grid(row=9, column=0, pady=5, sticky="nsew") + self.negative_prompt_input.bind("", highlight_text_negative) + self.negative_prompt_input.tag_config("highlight", foreground="#FFFF97") + + def auto_hide_label_command(): + if self.auto_hide_keyword_input_var.get() == 0: + self.auto_hide_keyword_input.grid_forget() + else: + self.auto_hide_keyword_input.grid(row=9, column=0, pady=5, sticky="nsew") + + def highlight_text_autohide(event=None): + if '#' in self.auto_hide_keyword_input.get("1.0", tk.END): + # 기존의 하이라이트를 모두 제거 + self.auto_hide_keyword_input.tag_remove("highlight", "1.0", tk.END) + + # '#'으로 시작하여 ','나 '\n', 또는 텍스트 끝까지 이어지는 모든 부분을 찾음 + for match in re.finditer(r'#.*?([,\n]|$)', self.auto_hide_keyword_input.get("1.0", tk.END)): + start = "1.0 + {}c".format(match.start()) + end = "1.0 + {}c".format(match.end()) + self.auto_hide_keyword_input.tag_add("highlight", start, end) + + self.auto_hide_keyword_input_var = customtkinter.IntVar(value=1) + + self.auto_hide_keyword_input_var = customtkinter.IntVar(value=1) + self.auto_hide_label = customtkinter.CTkCheckBox(self.text_input_frame, text=" ----------------------------- 자동숨김 키워드 -----------------------------", fg_color="grey10", hover_color="#50164A", font=my_font, variable=self.auto_hide_keyword_input_var, command=auto_hide_label_command,border_color="#333333" ) + self.auto_hide_label.grid(row = 10, column=0, sticky="w" ) + self.auto_hide_keyword_input = customtkinter.CTkTextbox(self.text_input_frame, width=490, height=self.ui_size['autohide'], font=v_large_font, undo=True) + self.auto_hide_keyword_input.grid(row=11, column=0, pady=5, sticky="nsew") + self.auto_hide_keyword_input.bind("", highlight_text_autohide) + self.auto_hide_keyword_input.tag_config("highlight", foreground="#FFFF97") + + def bottom_ui_hide_command(): + if self.bottom_ui_hide_var.get() == 0: + self.bottom_ui_toggle.configure(text="하단 기능 펼치기") + self.unlock_hold_prompt.grid_forget() + self.toggle_wildcard_preopen.grid_forget() + self.conditional_frame.grid_forget() + self.conditional_prompt_label.grid_forget() + self.conditional_prompt_input.grid_forget() + self.conditional_negative_label.grid_forget() + self.conditional_negative_input.grid_forget() + else: + self.bottom_ui_toggle.configure(text="하단 기능 숨기기") + self.unlock_hold_prompt.grid(row = 13, column = 0, pady=5, sticky="w") + self.toggle_wildcard_preopen.grid(row = 14, column = 0, pady=5, sticky="w") + self.conditional_frame.grid(row = 15, column=0, sticky="w", pady=5) + self.conditional_prompt_label.grid(row = 16, column=0, sticky="n" ) + self.conditional_prompt_input.grid(row=17, column=0, pady=5, sticky="nsew") + self.conditional_negative_label.grid(row = 18, column=0, sticky="n" ) + self.conditional_negative_input.grid(row=19, column=0, pady=5, sticky="nsew") + + self.bottom_ui_hide_var = customtkinter.IntVar(value=1) + self.bottom_ui_toggle = customtkinter.CTkCheckBox(self.text_input_frame, text="하단 기능 숨기기", fg_color="grey10", hover_color="#50164A", font=my_font, variable=self.bottom_ui_hide_var, command=bottom_ui_hide_command,border_color="#333333" ) + self.bottom_ui_toggle.grid(row = 12, column = 0, pady=5, sticky="w") + self.auto_quality_toggle_var = customtkinter.IntVar() + self.auto_quality_toggle =customtkinter.CTkCheckBox(self.text_input_frame, text="Auto Quality Tag 활성화 (UC Preset은 제외)", font=my_font, variable=self.auto_quality_toggle_var) + self.auto_quality_toggle.grid(row = 12, column = 0, pady=5, sticky="e") + self.auto_quality_toggle.select() + self.unlock_hold_prompt_var = customtkinter.IntVar() + + def unlock_hold(): + if self.unlock_hold_prompt_var.get() == 1: + self.fixed_prompt_input.configure(state="normal", text_color="#DCE4EE") + self.fixed_prompt_after_input.configure(state="normal", text_color="#DCE4EE") + elif self.unlock_hold_prompt_var.get() == 0 and self.toggle_prompt_fix_button.get() == 1: + self.fixed_prompt_input.configure(state="disabled", text_color="#A2B8D2") + self.fixed_prompt_after_input.configure(state="disabled", text_color="#A2B8D2") + + def reset_wildcard_preopen(): + if self.toggle_wildcard_preopen_var.get() == 1: + self.wildcard_preopen_repeat_current = self.wildcard_preopen_repeat + + self.unlock_hold_prompt = customtkinter.CTkCheckBox(self.text_input_frame, text="프롬프트 고정: 선행/후행/랜덤 ���롬프트 잠금 강제해제 ", font=my_font, variable=self.unlock_hold_prompt_var, state="disabled", command=unlock_hold) + self.unlock_hold_prompt.grid(row = 13, column = 0, pady=5, sticky="w") + self.unlock_hold_prompt.deselect() + self.toggle_wildcard_preopen_var = customtkinter.IntVar() + self.toggle_wildcard_preopen = customtkinter.CTkCheckBox(self.text_input_frame, text="선행프롬의 와일드카드를 랜덤 프롬프트에서 먼저 오픈", font=my_font, variable=self.toggle_wildcard_preopen_var, command=reset_wildcard_preopen) + self.toggle_wildcard_preopen.deselect() + self.toggle_wildcard_preopen.grid(row = 14, column = 0, pady=5, sticky="w") + + self.conditional_frame = customtkinter.CTkFrame(self.text_input_frame, fg_color="#2B2B2B") + self.conditional_frame.grid(row = 15, column=0, sticky="w", pady=5) + + self.cond_prompt_button_var = customtkinter.IntVar() + self.cond_prompt_button = customtkinter.CTkCheckBox(self.conditional_frame,text="조건부 프롬프트 활성화", variable=self.cond_prompt_button_var,font=my_font ) + self.cond_prompt_button.grid(row = 0, column = 0, sticky="w") + + self.cond_negative_button_var = customtkinter.IntVar() + self.cond_negative_button = customtkinter.CTkCheckBox(self.conditional_frame,text="조건부 네거티브 활성화", variable=self.cond_negative_button_var, font=my_font ) + self.cond_negative_button.grid(row = 0, column = 1, sticky="w", padx=5) + + def highlight_text_cond(event=None): + if '#' in self.conditional_prompt_input.get("1.0", tk.END): + # 기존의 하이라이트를 모두 제거 + self.conditional_prompt_input.tag_remove("highlight", "1.0", tk.END) + + # '#'으로 시작하여 ','나 '\n', 또는 텍스트 끝까지 이어지는 모든 부분을 찾음 + for match in re.finditer(r'#.*?([,\n]|$)', self.conditional_prompt_input.get("1.0", tk.END)): + start = "1.0 + {}c".format(match.start()) + end = "1.0 + {}c".format(match.end()) + self.conditional_prompt_input.tag_add("highlight", start, end) + + def highlight_text_cond_neg(event=None): + if '#' in self.conditional_negative_input.get("1.0", tk.END): + # 기존의 하이라이트를 모두 제거 + self.conditional_negative_input.tag_remove("highlight", "1.0", tk.END) + + # '#'으로 시작하여 ','나 '\n', 또는 텍스트 끝까지 이어지는 모든 부분을 찾음 + for match in re.finditer(r'#.*?([,\n]|$)', self.conditional_negative_input.get("1.0", tk.END)): + start = "1.0 + {}c".format(match.start()) + end = "1.0 + {}c".format(match.end()) + self.conditional_negative_input.tag_add("highlight", start, end) + + self.conditional_prompt_label = customtkinter.CTkLabel(self.text_input_frame, text=" ------------------------ 조건부 프롬프트 (랜덤시 적용) ------------------------", font=my_font) + self.conditional_prompt_label.grid(row = 16, column=0, sticky="w" ) + self.conditional_prompt_input = customtkinter.CTkTextbox(self.text_input_frame, width=490, height=self.ui_size['cond_prompt'], font=v_large_font, undo=True) + self.conditional_prompt_input.grid(row=17, column=0, pady=5, sticky="nsew") + self.conditional_prompt_input.bind("", highlight_text_cond) + self.conditional_prompt_input.tag_config("highlight", foreground="#FFFF97") + + self.conditional_negative_label = customtkinter.CTkLabel(self.text_input_frame, text=" ------------------------ 조건부 네거티브 (생성시 적용) ------------------------", font=my_font) + self.conditional_negative_label.grid(row = 18, column=0, sticky="w" ) + self.conditional_negative_input = customtkinter.CTkTextbox(self.text_input_frame, width=490, height=self.ui_size['cond_negative'], font=v_large_font, undo=True) + self.conditional_negative_input.grid(row=19, column=0, pady=5, sticky="nsew") + self.conditional_negative_input.bind("", highlight_text_cond_neg) + self.conditional_negative_input.tag_config("highlight", foreground="#FFFF97") + + #이미지 히스토리 + self.image_history_frame = customtkinter.CTkFrame(self.right_frame) + self.image_history_frame.grid(row=0, column=0, columnspan=2, rowspan=15, padx=5, pady=5, sticky="n") + + def show_context_menu(event): + if len(app.image_queue) != 0: + self.image_label_context_menu.tk_popup(event.x_root, event.y_root) + + def copy_file(event=None): + focused_widget = self.focus_get() + if focused_widget == self: + file_path = self.image_queue[self.current_window][3] + command = f"powershell -command \"Get-Item '{file_path}' | Set-Clipboard\"" + subprocess.run(command, shell=True) + self.control_pressed = False + + def copy_file_instant(event=None): + file_path = self.image_queue[self.current_window][3] + command = f"powershell -command \"Get-Item '{file_path}' | Set-Clipboard\"" + subprocess.run(command, shell=True) + + #이미지 레이블 + self.image_label = customtkinter.CTkLabel(self.image_history_frame, text="") + self.image_label.grid(row=1, column=0, rowspan=14, padx=10, pady=5, sticky="w") + self.image_label.bind("", show_context_menu) + + self.image_label_context_menu = tk.Menu(self, tearoff=0, font=v_large_font) + self.image_label_context_menu.add_command(label="이미지를 클립보드에 복사", command=copy_file_instant) + + + self.image_label_report = customtkinter.CTkTextbox(self.right_frame, width=740, height=100, font=large_font) + #image_label_report.insert("0.0", "1girl, {{minato aqua, ahoge, blue hair, braid, colored inner hair}}, artist:healthyman, [[[[[[[[[[artist:mikozin]]]]]]]]]], [[[artist:lakilolom]]], [[[[artist:crumbles]]]], [[artist:tianliang_duohe_fangdongye]], breasts, dutch angle, gloves, groin, gun, handgun, looking at viewer, nipples, no panties, one eye closed, skirt, smile, solo, topless, weapon, commentary request, highres, nsfw, great quality, aesthetic, absurdres, retouched, smooth lines, excellent color, suitable texture (SEED:22222222222), This text is sample, not an output of the actual generated result.") + self.image_label_under_frame = customtkinter.CTkFrame(self.image_history_frame) + self.image_label_under_frame.grid(row=0, column=0, padx=10, pady=5, sticky="ew") + self.open_save_folder = customtkinter.CTkButton(self.image_label_under_frame, text="폴더 열기", font=my_font, fg_color="transparent", width=60,command=lambda: open_file_explorer(self)) + self.open_save_folder.grid(row=0, column=0,padx=5, pady=5, sticky="nsew") + self.window_label = customtkinter.CTkLabel(self.image_label_under_frame, text="0 / 0", font=my_font, width=60) + self.window_label.grid(row=0, column=4,padx=5, pady=5, sticky="nsew") + self.my_anlas = customtkinter.IntVar(value=0) + self.anlas_label = customtkinter.CTkLabel(self.image_label_under_frame, text="Anlas : 0", font=my_font) + self.anlas_label.grid(row=0, column=5,padx=5, pady=5, sticky="nsew") + self.request_upper_size_button = customtkinter.CTkButton(self.image_label_under_frame, text="img2img / inpaint",fg_color="transparent", hover_color="grey10", text_color="#FFFF97", font=my_font,state="disabled", command=lambda: img2img(self), width=110) + self.request_upper_size_button.grid(row=0, column=6,padx=5, pady=5, sticky="nsew") + #self.canvas_mode = customtkinter.CTkButton(self.image_label_under_frame, text="Inpaint", font=my_font, text_color="#0E0F21",fg_color="#F5F3C2", hover_color="#F2F2F2",width=50) + #self.canvas_mode.grid(row=0, column=9,padx=5, pady=5, sticky="nsew") + self.open_in_file_explorer = customtkinter.CTkButton(self.image_label_under_frame, text="파일 위치 열기", font=my_font, fg_color="transparent", width=80,command=lambda: open_in_file_explorer(self)) + self.open_in_file_explorer.grid(row=0, column=7,padx=5, pady=5, sticky="nsew") + self.open_save_folder.grid(row=0, column=0,padx=5, pady=5, sticky="nsew") + self.image_label_report.grid(row=16, column=0, padx=10, pady=5, sticky="ew") + self.image_label_report.configure(state="disabled") + white_image = Image.new('RGB', (768, 768), 'white') + #white_image =Image.open("sample_image.jpg") + white_photo = customtkinter.CTkImage(white_image, size=(620,620)) + self.image_label.configure(image=white_photo) + self.image_label.image = white_photo + + self.image_queue = [] + self.window = 0 + + #image history 버튼 + self.image_history_sub_frame = customtkinter.CTkFrame(self.image_history_frame, width=100, height=550) + self.image_history_sub_frame.grid(row=1, rowspan=12, column=1, sticky="n") + + self.output_file_path = f"output_NAI\\{self.start_time}\\txt2img" + self.import_image_count = 0 + + def open_file_explorer(self): + if not os.path.exists(self.output_file_path): + os.makedirs(self.output_file_path) + os.startfile(self.output_file_path) + + def open_in_file_explorer(self): + filename = self.image_queue[self.current_window][3] + explorer_command = f'explorer /select,"{os.path.abspath(filename)}"' + subprocess.run(explorer_command) + + def up_button_pressed(): + if self.image_history_button_down.cget("state") == "disabled": + self.image_history_button_down.configure(state="normal") + if self.control_pressed: + self.window = len(self.image_queue) + else: + self.window+=1 + update() + show_text = str(self.window)+" / "+ str(len(self.image_queue)) + self.window_label.configure(text=show_text) + if self.window >= len(self.image_queue): + self.image_history_button_up.configure(state="disabled") + self.control_pressed = False + image_history_0_yield() + + def down_button_pressed(): + if self.image_history_button_up.cget("state") == "disabled": + self.image_history_button_up.configure(state="normal") + if self.control_pressed: + if self.window >= 105: + self.window -= 100 + else: + self.window = 5 + else: + self.window-=1 + update() + show_text = str(self.window)+" / "+ str(len(self.image_queue)) + self.window_label.configure(text=show_text) + if self.window <= 5: + self.image_history_button_down.configure(state="disabled") + self.control_pressed = False + image_history_4_yield() + + highlight_tags = ["aqua","black","blonde","blue","brown","cyan","green","grey","orange","pink","purple","red","violet","white","yellow","mouth", "eyes", " cap", " ears", " girl", " ornament"," hat", "beret", " ear "] + + def insert_with_color(cs_text_input, current_lookup): + words = [word.strip() for word in current_lookup.split(',')] + for index, word in enumerate(words): + highlight = False + start_index = cs_text_input.index("end-1c") + if index == len(words) - 1: # 마지막 원소인 경우 + cs_text_input.insert("end", word) + else: + cs_text_input.insert("end", word + ", ") + end_index = cs_text_input.index("end-1c") + + for tag in highlight_tags: + if tag in word: + highlight = True + if word == "hat": highlight = True + # 조건 확인 + if word in cd.character_dictionary or word in tagbag.bag_of_tags[5:] or 'tail' in word or 'pupil' in word: + cs_text_input.tag_add(word, start_index, end_index) + cs_text_input.tag_config(word, foreground="#FFFF97") + elif highlight: + cs_text_input.tag_add(word, start_index, end_index) + cs_text_input.tag_config(word, foreground="#AED395") + + self.current_window = None + def set_image_to_queue(imagen, prompt, seed, filename): + if type(self.current_popped_row) != type(None) and not self.current_popped_row.empty: + self.image_queue.append([imagen, prompt, seed, filename, self.current_popped_row.copy()]) + else: + self.image_queue.append([imagen, prompt, seed, filename]) + #self.history_export.configure(text=f"export ({len(self.image_queue)})") + self.request_upper_size_button.configure(state="normal") + self.current_window = self.window + if self.window == len(self.image_queue)-1: + self.window+=1 + update() + show_text = str(self.window)+" / "+ str(len(self.image_queue)) + + self.window_label.configure(text=show_text) + + def image_history_0_yield(event=None): + if len(self.image_queue) < 1: + return + if self.control_pressed: + if self.toggle_prompt_fix_button.get() == 0: + self.text_input.delete("0.0", "end") + insert_with_color(self.text_input, self.image_queue[self.window-1][1]) + else: + self.image_label_report.configure(state="normal") + self.image_label_report.delete("0.0", "end") + self.image_label_report.insert("0.0", " 프롬프트 고정 상태에서는 사용 할 수 없습니다.") + self.image_label_report.configure(text_color="#FFFF97") + self.image_label_report.configure(state="disabled") + else: + self.current_window = self.window-1 + if app.state() != 'zoomed': + self.image_label.configure(image=customtkinter.CTkImage(self.image_queue[self.window-1][0], size=(620,620))) + else: + current_image = Image.open(self.image_queue[self.window-1][3]) + original_width, original_height = current_image.size + max_size = app.winfo_screenheight()-100 + if original_width > max_size or original_height > max_size: + new_image = Image.new("RGB", (max_size, max_size), "black") + new_image.paste(current_image, ((max_size - original_width) // 2, (max_size - original_height) // 2)) + instant_result_image = customtkinter.CTkImage(new_image, size=(max_size, max_size)) + else: + instant_result_image = customtkinter.CTkImage(current_image, size=(max_size, max_size)) + self.image_label.configure(image=instant_result_image) + self.image_label_report.configure(state="normal") + self.image_label_report.delete("0.0", "end") + self.image_label_report.configure(text_color="#DCE4EE") + self.image_label_report.insert("0.0", self.image_queue[self.window-1][1] + " seed : " + str(self.image_queue[self.window-1][2])) + self.image_label_report.configure(state="disabled") + if len(self.image_queue[self.window-1]) >= 5 and 'id' in self.image_queue[self.window-1][4].index: + if fav_check(self.image_queue[self.window-1][4]['id']): + self.history_export.configure(text="+ FAV (★)", state="disabled") + else: + self.history_export.configure(text="+ FAV (☆)", state="normal") + else: + self.history_export.configure(state="disabled") + + def image_history_1_yield(event=None): + if len(self.image_queue) < 2: + return + if self.control_pressed: + if self.toggle_prompt_fix_button.get() == 0: + self.text_input.delete("0.0", "end") + insert_with_color(self.text_input, self.image_queue[self.window-2][1]) + else: + self.image_label_report.configure(state="normal") + self.image_label_report.delete("0.0", "end") + self.image_label_report.insert("0.0", " 프롬프트 고정 상태에서는 사용 할 수 없습니다.") + self.image_label_report.configure(text_color="#FFFF97") + self.image_label_report.configure(state="disabled") + else: + self.current_window = self.window-2 + if app.state() != 'zoomed': + self.image_label.configure(image=customtkinter.CTkImage(self.image_queue[self.window-2][0], size=(620,620))) + else: + current_image = Image.open(self.image_queue[self.window-2][3]) + original_width, original_height = current_image.size + max_size = app.winfo_screenheight()-100 + if original_width > max_size or original_height > max_size: + new_image = Image.new("RGB", (max_size, max_size), "black") + new_image.paste(current_image, ((max_size - original_width) // 2, (max_size - original_height) // 2)) + instant_result_image = customtkinter.CTkImage(new_image, size=(max_size, max_size)) + else: + instant_result_image = customtkinter.CTkImage(current_image, size=(max_size, max_size)) + self.image_label.configure(image=instant_result_image) + self.image_label_report.configure(state="normal") + self.image_label_report.delete("0.0", "end") + self.image_label_report.configure(text_color="#DCE4EE") + self.image_label_report.insert("0.0", self.image_queue[self.window-2][1] + " seed : " + str(self.image_queue[self.window-2][2])) + self.image_label_report.configure(state="disabled") + if len(self.image_queue[self.window-2]) >= 5 and 'id' in self.image_queue[self.window-2][4].index: + if fav_check(self.image_queue[self.window-2][4]['id']): + self.history_export.configure(text="+ FAV (★)", state="disabled") + else: + self.history_export.configure(text="+ FAV (☆)", state="normal") + else: + self.history_export.configure(state="disabled") + def image_history_2_yield(event=None): + if len(self.image_queue) < 3: + return + if self.control_pressed: + if self.toggle_prompt_fix_button.get() == 0: + self.text_input.delete("0.0", "end") + insert_with_color(self.text_input, self.image_queue[self.window-3][1]) + else: + self.image_label_report.configure(state="normal") + self.image_label_report.delete("0.0", "end") + self.image_label_report.insert("0.0", " 프롬프트 고정 상태에서는 사용 할 수 없습니다.") + self.image_label_report.configure(text_color="#FFFF97") + self.image_label_report.configure(state="disabled") + else: + self.current_window = self.window-3 + if app.state() != 'zoomed': + self.image_label.configure(image=customtkinter.CTkImage(self.image_queue[self.window-3][0], size=(620,620))) + else: + current_image = Image.open(self.image_queue[self.window-3][3]) + original_width, original_height = current_image.size + max_size = app.winfo_screenheight()-100 + if original_width > max_size or original_height > max_size: + new_image = Image.new("RGB", (max_size, max_size), "black") + new_image.paste(current_image, ((max_size - original_width) // 2, (max_size - original_height) // 2)) + instant_result_image = customtkinter.CTkImage(new_image, size=(max_size, max_size)) + else: + instant_result_image = customtkinter.CTkImage(current_image, size=(max_size, max_size)) + self.image_label.configure(image=instant_result_image) + self.image_label_report.configure(state="normal") + self.image_label_report.delete("0.0", "end") + self.image_label_report.configure(text_color="#DCE4EE") + self.image_label_report.insert("0.0", self.image_queue[self.window-3][1] + " seed : " + str(self.image_queue[self.window-3][2])) + self.image_label_report.configure(state="disabled") + if len(self.image_queue[self.window-3]) >= 5 and 'id' in self.image_queue[self.window-3][4].index: + if fav_check(self.image_queue[self.window-3][4]['id']): + self.history_export.configure(text="+ FAV (★)", state="disabled") + else: + self.history_export.configure(text="+ FAV (☆)", state="normal") + else: + self.history_export.configure(state="disabled") + def image_history_3_yield(event=None): + if len(self.image_queue) < 4: + return + if self.control_pressed: + if self.toggle_prompt_fix_button.get() == 0: + self.text_input.delete("0.0", "end") + insert_with_color(self.text_input, self.image_queue[self.window-4][1]) + else: + self.image_label_report.configure(state="normal") + self.image_label_report.delete("0.0", "end") + self.image_label_report.insert("0.0", " 프롬프트 고정 상태에서는 사용 할 수 없습니다.") + self.image_label_report.configure(text_color="#FFFF97") + self.image_label_report.configure(state="disabled") + else: + self.current_window = self.window-4 + if app.state() != 'zoomed': + self.image_label.configure(image=customtkinter.CTkImage(self.image_queue[self.window-4][0], size=(620,620))) + else: + current_image = Image.open(self.image_queue[self.window-4][3]) + original_width, original_height = current_image.size + max_size = app.winfo_screenheight()-100 + if original_width > max_size or original_height > max_size: + new_image = Image.new("RGB", (max_size, max_size), "black") + new_image.paste(current_image, ((max_size - original_width) // 2, (max_size - original_height) // 2)) + instant_result_image = customtkinter.CTkImage(new_image, size=(max_size, max_size)) + else: + instant_result_image = customtkinter.CTkImage(current_image, size=(max_size, max_size)) + self.image_label.configure(image=instant_result_image) + self.image_label_report.configure(state="normal") + self.image_label_report.delete("0.0", "end") + self.image_label_report.configure(text_color="#DCE4EE") + self.image_label_report.insert("0.0", self.image_queue[self.window-4][1] + " seed : " + str(self.image_queue[self.window-4][2])) + self.image_label_report.configure(state="disabled") + if len(self.image_queue[self.window-4]) >= 5 and 'id' in self.image_queue[self.window-4][4].index: + if fav_check(self.image_queue[self.window-4][4]['id']): + self.history_export.configure(text="+ FAV (★)", state="disabled") + else: + self.history_export.configure(text="+ FAV (☆)", state="normal") + else: + self.history_export.configure(state="disabled") + def image_history_4_yield(event=None): + if len(self.image_queue) < 5: + return + if self.control_pressed: + if self.toggle_prompt_fix_button.get() == 0: + self.text_input.delete("0.0", "end") + insert_with_color(self.text_input, self.image_queue[self.window-5][1]) + else: + self.image_label_report.configure(state="normal") + self.image_label_report.delete("0.0", "end") + self.image_label_report.insert("0.0", " 프롬프트 고정 상태에서는 사용 할 수 없습니다.") + self.image_label_report.configure(text_color="#FFFF97") + self.image_label_report.configure(state="disabled") + else: + self.current_window = self.window-5 + if app.state() != 'zoomed': + self.image_label.configure(image=customtkinter.CTkImage(self.image_queue[self.window-5][0], size=(620,620))) + else: + current_image = Image.open(self.image_queue[self.window-5][3]) + original_width, original_height = current_image.size + max_size = app.winfo_screenheight()-100 + if original_width > max_size or original_height > max_size: + new_image = Image.new("RGB", (max_size, max_size), "black") + new_image.paste(current_image, ((max_size - original_width) // 2, (max_size - original_height) // 2)) + instant_result_image = customtkinter.CTkImage(new_image, size=(max_size, max_size)) + else: + instant_result_image = customtkinter.CTkImage(current_image, size=(max_size, max_size)) + self.image_label.configure(image=instant_result_image) + self.image_label_report.configure(state="normal") + self.image_label_report.delete("0.0", "end") + self.image_label_report.configure(text_color="#DCE4EE") + self.image_label_report.insert("0.0", self.image_queue[self.window-5][1] + " seed : " + str(self.image_queue[self.window-5][2])) + self.image_label_report.configure(state="disabled") + if len(self.image_queue[self.window-5]) >= 5 and 'id' in self.image_queue[self.window-5][4].index: + if fav_check(self.image_queue[self.window-5][4]['id']): + self.history_export.configure(text="+ FAV (★)", state="disabled") + else: + self.history_export.configure(text="+ FAV (☆)", state="normal") + else: + self.history_export.configure(state="disabled") + + def update(): + if len(self.image_queue) <= 5: + if len(self.image_queue) >= 1: + self.image_history_0.configure(self.image_history_sub_frame, image=customtkinter.CTkImage(self.image_queue[self.window-1][0], size=(100,100))) + if len(self.image_queue[self.window-1]) >= 5: + button_item_0 = self.image_queue[self.window-1][4].copy() + self.image_history_0.bind("", lambda event: on_right_click(event, button_item_0)) + if len(self.image_queue) >= 2: + self.image_history_0.configure(self.image_history_sub_frame, image=customtkinter.CTkImage(self.image_queue[self.window-1][0], size=(100,100))) + if len(self.image_queue[self.window-1]) >= 5: + button_item_0 = self.image_queue[self.window-1][4].copy() + self.image_history_0.unbind("") + self.image_history_0.bind("", lambda event: on_right_click(event, button_item_0)) + self.image_history_1.configure(self.image_history_sub_frame, image=customtkinter.CTkImage(self.image_queue[self.window-2][0], size=(100,100))) + if len(self.image_queue[self.window-2]) >= 5: + button_item_1 = self.image_queue[self.window-2][4].copy() + self.image_history_1.bind("", lambda event: on_right_click(event, button_item_1)) + if len(self.image_queue) >= 3: + self.image_history_0.configure(self.image_history_sub_frame, image=customtkinter.CTkImage(self.image_queue[self.window-1][0], size=(100,100))) + if len(self.image_queue[self.window-1]) >= 5: + button_item_0 = self.image_queue[self.window-1][4].copy() + self.image_history_0.unbind("") + self.image_history_0.bind("", lambda event: on_right_click(event, button_item_0)) + self.image_history_1.configure(self.image_history_sub_frame, image=customtkinter.CTkImage(self.image_queue[self.window-2][0], size=(100,100))) + if len(self.image_queue[self.window-2]) >= 5: + button_item_1 = self.image_queue[self.window-2][4].copy() + self.image_history_1.unbind("") + self.image_history_1.bind("", lambda event: on_right_click(event, button_item_1)) + self.image_history_2.configure(self.image_history_sub_frame, image=customtkinter.CTkImage(self.image_queue[self.window-3][0], size=(100,100))) + if len(self.image_queue[self.window-3]) >= 5: + button_item_2 = self.image_queue[self.window-3][4].copy() + self.image_history_2.bind("", lambda event: on_right_click(event, button_item_2)) + if len(self.image_queue) >= 4: + self.image_history_0.configure(self.image_history_sub_frame, image=customtkinter.CTkImage(self.image_queue[self.window-1][0], size=(100,100))) + if len(self.image_queue[self.window-1]) >= 5: + button_item_0 = self.image_queue[self.window-1][4].copy() + self.image_history_0.unbind("") + self.image_history_0.bind("", lambda event: on_right_click(event, button_item_0)) + self.image_history_1.configure(self.image_history_sub_frame, image=customtkinter.CTkImage(self.image_queue[self.window-2][0], size=(100,100))) + if len(self.image_queue[self.window-2]) >=5: + button_item_1 = self.image_queue[self.window-2][4].copy() + self.image_history_1.unbind("") + self.image_history_1.bind("", lambda event: on_right_click(event, button_item_1)) + self.image_history_2.configure(self.image_history_sub_frame, image=customtkinter.CTkImage(self.image_queue[self.window-3][0], size=(100,100))) + if len(self.image_queue[self.window-3]) >= 5: + button_item_2 = self.image_queue[self.window-3][4].copy() + self.image_history_2.unbind("") + self.image_history_2.bind("", lambda event: on_right_click(event, button_item_2)) + self.image_history_3.configure(self.image_history_sub_frame, image=customtkinter.CTkImage(self.image_queue[self.window-4][0], size=(100,100))) + if len(self.image_queue[self.window-4]) >= 5: + button_item_3 = self.image_queue[self.window-4][4].copy() + self.image_history_3.bind("", lambda event: on_right_click(event, button_item_3)) + if len(self.image_queue) == 5: + self.image_history_0.configure(self.image_history_sub_frame, image=customtkinter.CTkImage(self.image_queue[self.window-1][0], size=(100,100))) + if len(self.image_queue[self.window-1]) >= 5: + button_item_0 = self.image_queue[self.window-1][4].copy() + self.image_history_0.unbind("") + self.image_history_0.bind("", lambda event: on_right_click(event, button_item_0)) + self.image_history_1.configure(self.image_history_sub_frame, image=customtkinter.CTkImage(self.image_queue[self.window-2][0], size=(100,100))) + if len(self.image_queue[self.window-2]) >= 5: + button_item_1 = self.image_queue[self.window-2][4].copy() + self.image_history_1.unbind("") + self.image_history_1.bind("", lambda event: on_right_click(event, button_item_1)) + self.image_history_2.configure(self.image_history_sub_frame, image=customtkinter.CTkImage(self.image_queue[self.window-3][0], size=(100,100))) + if len(self.image_queue[self.window-3]) >= 5: + button_item_2 = self.image_queue[self.window-3][4].copy() + self.image_history_2.unbind("") + self.image_history_2.bind("", lambda event: on_right_click(event, button_item_2)) + self.image_history_3.configure(self.image_history_sub_frame, image=customtkinter.CTkImage(self.image_queue[self.window-4][0], size=(100,100))) + if len(self.image_queue[self.window-4]) >= 5: + button_item_3 = self.image_queue[self.window-4][4].copy() + self.image_history_3.unbind("") + self.image_history_3.bind("", lambda event: on_right_click(event, button_item_3)) + self.image_history_4.configure(self.image_history_sub_frame, image=customtkinter.CTkImage(self.image_queue[self.window-5][0], size=(100,100))) + if len(self.image_queue[self.window-5]) >= 5: + button_item_4 = self.image_queue[self.window-5][4].copy() + self.image_history_4.bind("", lambda event: on_right_click(event, button_item_4)) + else: + self.image_history_0.configure(self.image_history_sub_frame, image=customtkinter.CTkImage(self.image_queue[self.window-1][0], size=(100,100))) + if len(self.image_queue[self.window-1]) >= 5: + button_item_0 = self.image_queue[self.window-1][4].copy() + self.image_history_0.unbind("") + self.image_history_0.bind("", lambda event: on_right_click(event, button_item_0)) + self.image_history_1.configure(self.image_history_sub_frame, image=customtkinter.CTkImage(self.image_queue[self.window-2][0], size=(100,100))) + if len(self.image_queue[self.window-2]) >= 5: + button_item_1 = self.image_queue[self.window-2][4].copy() + self.image_history_1.unbind("") + self.image_history_1.bind("", lambda event: on_right_click(event, button_item_1)) + self.image_history_2.configure(self.image_history_sub_frame, image=customtkinter.CTkImage(self.image_queue[self.window-3][0], size=(100,100))) + if len(self.image_queue[self.window-3]) >= 5: + button_item_2 = self.image_queue[self.window-3][4].copy() + self.image_history_2.unbind("") + self.image_history_2.bind("", lambda event: on_right_click(event, button_item_2)) + self.image_history_3.configure(self.image_history_sub_frame, image=customtkinter.CTkImage(self.image_queue[self.window-4][0], size=(100,100))) + if len(self.image_queue[self.window-4]) >= 5: + button_item_3 = self.image_queue[self.window-4][4].copy() + self.image_history_3.unbind("") + self.image_history_3.bind("", lambda event: on_right_click(event, button_item_3)) + self.image_history_4.configure(self.image_history_sub_frame, image=customtkinter.CTkImage(self.image_queue[self.window-5][0], size=(100,100))) + if len(self.image_queue[self.window-5]) >= 5: + button_item_4 = self.image_queue[self.window-5][4].copy() + self.image_history_4.unbind("") + self.image_history_4.bind("", lambda event: on_right_click(event, button_item_4)) + if len(self.image_queue) == 6: + self.image_history_button_down.configure(state="normal") + + def some_command(item): + print(item) + random_function(item) + + def on_right_click(event, button_item): + context_menu.delete(0, tk.END) # 기존 메뉴 항목을 모두 삭제 + context_menu.add_command(label="Requeue", command=lambda: some_command(button_item)) # 새 커맨드 추가 + context_menu.tk_popup(event.x_root, event.y_root) + + context_menu = tk.Menu(self, tearoff=0, font=v_large_font, fg="black") + + black_image = Image.new('RGB', (100, 100), '#2B2B2B') + self.image_history_0 = customtkinter.CTkButton(self.image_history_sub_frame, width=100, height=100, corner_radius=8,text="", fg_color="transparent", image=customtkinter.CTkImage(black_image, size=(100,100)), command=image_history_0_yield) + self.image_history_0.grid(row=0, column=1, padx=5, pady=1, sticky="n") + + self.image_history_1 = customtkinter.CTkButton(self.image_history_sub_frame, width=100, height=100, corner_radius=8,text="", fg_color="transparent", image=customtkinter.CTkImage(black_image, size=(100,100)), command=image_history_1_yield) + self.image_history_1.grid(row=1, column=1, padx=5, pady=1, sticky="n") + self.image_history_2 = customtkinter.CTkButton(self.image_history_sub_frame, width=100, height=100, corner_radius=8,text="", fg_color="transparent",image=customtkinter.CTkImage(black_image, size=(100,100)), command=image_history_2_yield) + self.image_history_2.grid(row=2, column=1, padx=5, pady=1, sticky="n") + self.image_history_3 = customtkinter.CTkButton(self.image_history_sub_frame, width=100, height=100, corner_radius=8,text="", fg_color="transparent", image=customtkinter.CTkImage(black_image, size=(100,100)), command=image_history_3_yield) + self.image_history_3.grid(row=3,column=1, padx=5, pady=1, sticky="n") + self.image_history_4 = customtkinter.CTkButton(self.image_history_sub_frame, width=100, height=100, corner_radius=8,text="", fg_color="transparent", image=customtkinter.CTkImage(black_image, size=(100,100)), command=image_history_4_yield) + self.image_history_4.grid(row=4, column=1, padx=5, pady=1, sticky="n") + self.image_history_button_up = customtkinter.CTkButton(self.image_history_frame, text="▲", width=100, font=my_font, state="disabled", command=up_button_pressed) + self.image_history_button_up.grid(row=0, column=1, pady=10, padx=5, sticky="n") + self.image_history_button_down = customtkinter.CTkButton(self.image_history_frame, text="▼", width=100, font=my_font, state="disabled", command=down_button_pressed) + self.image_history_button_down.grid(row=13, column=1, pady=5, padx=5, sticky="n") + + def save_to_excel(): + #contributor : highnoon1 + #function : 히스토리 엑셀로 저장 (무제한 보기는 성능 이슈로 제외) + workbook = Workbook() + sheet = workbook.active + + alignment = Alignment(wrapText=True) + sheet['A1'].alignment = alignment + sheet['A1'] = 'Prompt' + sheet['B1'] = 'Seed' + sheet['C1'] = 'Image' + + image_objects = [] # BytesIO 객체를 추적하기 위한 리스트 + + # history 데이터를 반복하며 Excel에 저장 + for index, (pil_image, prompt, seed, *_rest) in enumerate(self.image_queue): + + row_num = index + 1 # 행 번호 (Excel 행은 1부터 시작) + + # 프롬프트와 시드 삽입 + prompt_cell = sheet.cell(row=row_num, column=1, value=prompt) + sheet.cell(row=row_num, column=2, value=seed) + prompt_cell.alignment = Alignment(wrap_text=True) + + # PIL 이미지를 BytesIO 객체에 PNG 형식으로 저장 + pil_image = self.resize_image_to_fit(pil_image,192) + output = io.BytesIO() + pil_image.save(output, format='PNG') + output.seek(0) + image_objects.append(output) # 리스트에 추가 + + # Openpyxl 이미지 생성 및 시트에 추가 + img = OpenpyxlImage(output) + img.anchor = f'C{row_num}' # 이미지를 C열에 고정 + sheet.add_image(img) + + # 이미지 크기에 따라 행 높이 및 열 너비 조정 + scale_factor = 0.75 + sheet.row_dimensions[row_num].height = pil_image.height * scale_factor + sheet.column_dimensions['C'].width = pil_image.width * scale_factor / 7 + + # A열과 B열 크기 조정 + sheet.column_dimensions['A'].width = 50 + sheet.column_dimensions['B'].width = 15 + + # 워크북 저장 + workbook.save(f"history_with_images_{self.start_time}.xlsx") + + # 모든 BytesIO 객체 닫기 + for img_obj in image_objects: + img_obj.close() + if os.path.exists(f"history_with_images_{self.start_time}.xlsx"): + os.startfile(f"history_with_images_{self.start_time}.xlsx") + + print("엑셀 파일에 저장됨") + + def history_export(): + if self.control_pressed: + save_to_excel() + self.history_export.configure(text="+ FAV (☆)", fg_color="#F5F3C2", hover_color="#A69F40", text_color="black") + if self.image_queue and len(self.image_queue[self.current_window]) >= 5: + self.history_export.configure(state="normal") + else: + self.history_export.configure(state="disabled") + else: + """ + Description : @history_export 평상시에는 FAV 기능으로 동작 + """ + new_data = self.image_queue[self.current_window][4] + validation_prompt = self.image_queue[self.current_window][1] + prompt = new_data['general'] + validation_prompt = [key.strip() for key in validation_prompt.split(',')] + prompt = [key.strip() for key in prompt.split(',')] + autohide = self.auto_hide_keyword_input.get("0.0", "end") + autohide = [key.strip() for key in autohide.split(',')] + app.data.bag_of_tags + prompt = [key for key in prompt if key not in autohide] + not_validated = [key for key in prompt if key not in validation_prompt] + if len(not_validated) >= 3: + self.image_label_report.configure(state="normal") + self.image_label_report.delete("0.0", "end") + self.image_label_report.insert("0.0", f" 랜덤하게 생성된 프롬프트가 아니거나, {len(not_validated)}개의 제거된 태그가 검출되어 Favorite 등록에 실패하였습니다. 해당 기능은 랜덤 생성 프롬프트를 DataFrame 규격에 맞춰 복사하는 기능이기 때문에 수정된 프롬프트의 저장은 지원하지 않습니다.") + self.image_label_report.configure(text_color="#FFFF97") + self.image_label_report.configure(state="disabled") + return + try: + favorite_prompts = pd.read_parquet('favorite_prompt.parquet') + except FileNotFoundError: + dtype_dict = { + 'id': 'int64', + 'copyright': 'object', + 'character': 'object', + 'artist': 'object', + 'general': 'object', + 'meta': 'object', + 'rating': 'object', + 'score': 'object', + 'created_at': 'object', + } + favorite_prompts = pd.DataFrame(columns=dtype_dict.keys()) + self.fav_button.configure(state="normal") + try: + _temp = new_data['id'] + except: + self.image_label_report.configure(state="normal") + self.image_label_report.delete("0.0", "end") + self.image_label_report.insert("0.0", " 0215 버전부터 tags의 구성이 변경되었습니다. favorite 기능을 사용하기 위해 검색을 다시 수행 해 주세요.") + self.image_label_report.configure(text_color="#FFFF97") + self.image_label_report.configure(state="disabled") + return + new_data_id = new_data['id'] + new_data = pd.DataFrame([new_data]) + success = False + if 'id' not in favorite_prompts.columns: + favorite_prompts = pd.concat([favorite_prompts, new_data], ignore_index=True) + success = True + elif not favorite_prompts['id'].isin([new_data_id]).any(): + favorite_prompts = pd.concat([favorite_prompts, new_data], ignore_index=True) + success = True + else: + self.image_label_report.configure(state="normal") + self.image_label_report.delete("0.0", "end") + self.image_label_report.insert("0.0", " 이미 favorite에 등록되어있는 프롬프트입니다.") + self.image_label_report.configure(text_color="#FFFF97") + self.image_label_report.configure(state="disabled") + if success: + pil_image = self.image_queue[self.current_window][0] + folder_path = 'favorite_prompts' + if not os.path.exists(folder_path): + os.makedirs(folder_path) + favorite_prompts.to_parquet('favorite_prompt.parquet') + image_path = os.path.join(folder_path, f"{_temp}.jpg") + pil_image.save(image_path, 'JPEG') + self.fav_id_list.append(_temp) + self.history_export.configure(text="FAV (★)", fg_color="#F5F3C2", hover_color="#A69F40", text_color="black", state="disabled") + + self.history_export = customtkinter.CTkButton(self.image_history_frame, text="+ FAV (☆)", width=100, fg_color="#F5F3C2", hover_color="#A69F40", text_color="black", font=my_font, command=history_export, state="disabled") + self.history_export.grid(row=14, column=1, pady=10, padx=5, sticky="n") + + #랜덤작가 및 와일드카드 설정 + self.extended_right_frame = customtkinter.CTkFrame(self.right_frame) + self.extended_right_frame.grid(row=17, column=0, columnspan=2, padx=5, sticky="w") + self.random_artist_var = customtkinter.IntVar() + self.random_artist_button = customtkinter.CTkCheckBox(self.extended_right_frame, text="랜덤작가 추가", variable=self.random_artist_var, font=my_font, state="disabled") + self.random_artist_button.grid(row=0, column=0,pady=5, padx=10, sticky="nsew") + self.random_artist_manage_button = customtkinter.CTkButton(self.extended_right_frame, text="랜덤작가 관리", font=my_font, command=self.random_artist_window) + self.random_artist_manage_button.grid(row=0, column=1,pady=5, padx=5, sticky="nsew") + self.recommended_prompt_button = customtkinter.CTkButton(self.extended_right_frame, text="추천 프롬프트", font=my_font, command= self.open_prompt_window) + self.recommended_prompt_button.grid(row=0, column=3,pady=5, padx=5, sticky="nsew") + self.wildcard_manager_button = customtkinter.CTkButton(self.extended_right_frame, text="와일드카드 관리", font=my_font, command=self.open_wildcard_window) + self.wildcard_manager_button.grid(row=0, column=2,pady=5, padx=5, sticky="nsew") + self.character_search_button = customtkinter.CTkButton(self.extended_right_frame, text="캐릭터 검색", fg_color="#7030A0", hover_color="#481F67", font=my_font, command= lambda: open_Character_search(self)) + self.character_search_button.grid(row=0, column=4,pady=5, padx=5, sticky="nsew") + self.seed_fix_var = customtkinter.IntVar() + self.seed_fix_button = customtkinter.CTkCheckBox(self.extended_right_frame, text="시드고정 ", variable=self.seed_fix_var, font=my_font) + self.seed_fix_button.grid(row=1, column=0,pady=5, padx=10, sticky="nsew") + self.entry_seed_value = customtkinter.IntVar() + self.seed_entry = customtkinter.CTkEntry(self.extended_right_frame, textvariable=self.entry_seed_value) + self.seed_entry.grid(row=1, column=1,pady=5, padx=5, sticky="nsew") + self.seed_entry.insert(0, random.randint(0,9999999999)) + self.cfg_scale_label = customtkinter.CTkLabel(self.extended_right_frame, text="CFG Scale : ", font=my_font) + self.cfg_scale_label.grid(row=1, column=2,pady=5, padx=5, sticky="w") + self.cfg_scale_var = customtkinter.StringVar(value="5.0") + self.cfg_scale_entry = customtkinter.CTkEntry(self.extended_right_frame, width=65, textvariable=self.cfg_scale_var) + self.cfg_scale_entry.grid(row=1, column=2,pady=5, padx=5, sticky="e") + self.prompt_guidance_rescale_var = customtkinter.StringVar(value="0") + self.prompt_guidance_rescale_label = customtkinter.CTkLabel(self.extended_right_frame, text="Prompt Guidance Rescale :", font=my_font) + self.prompt_guidance_rescale_label.grid(row=1, column=3, pady=5, padx=5, sticky="w") + self.prompt_guidance_rescale_entry = customtkinter.CTkEntry(self.extended_right_frame, width=65) + self.prompt_guidance_rescale_entry.grid(row=1, column=4,pady=5, padx=10, sticky="w") + self.extra_setting_button = customtkinter.CTkButton(self.extended_right_frame, width=55, text="기타설정", font=my_font, fg_color="grey", hover_color="grey5", command=lambda: show_advanced_settings(self)) + self.extra_setting_button.grid(row=1, column=4,pady=5, padx=5, sticky="e") + self.random_resolution_var = customtkinter.IntVar() + self.random_resolution_button = customtkinter.CTkCheckBox(self.extended_right_frame, text="랜덤 해상도 ", variable=self.random_resolution_var, font=my_font) + self.random_resolution_button.grid(row=2, column=0,pady=5, padx=10, sticky="nsew") + self.resolution_var = customtkinter.StringVar(value="1024 x 1024") + self.resolution_button = customtkinter.CTkComboBox(self.extended_right_frame, width=160,values=["1024 x 1024", "960 x 1088", "896 x 1152", "832 x 1216", "1088 x 960", "1152 x 896", "1216 x 832"], variable=self.resolution_var, font=my_font) + self.resolution_button.grid(row=2, column=1, padx=5, sticky="w") + self.sampler_label = customtkinter.CTkLabel(self.extended_right_frame, text="Sampler :", font=my_font) + self.sampler_label.grid(row=2, column=2,pady=5, padx=15, sticky="w") + self.sampler_var = customtkinter.StringVar(value="k_euler_ancestral") + self.sampler_button = customtkinter.CTkComboBox(self.extended_right_frame, width=210,values=["k_euler", "k_euler_ancestral", "k_dpmpp_2s_ancestral", "k_dpmpp_sde", "k_dpmpp_2m"], variable=self.sampler_var, font=my_font) + self.sampler_button.grid(row=2, column=2, columnspan=2, pady=5, padx=15, sticky="e") + self.show_fullscreen_btn = customtkinter.CTkButton(self.extended_right_frame, text="전체화면(ESC닫기)", font=my_font, command=self.show_fullscreen_image) + self.show_fullscreen_btn.grid(row=2, column=4,pady=5, padx=5, sticky="nsew") + self.sema_button_var = customtkinter.IntVar() + + def sema_pressed(): + if self.sema_button_var.get() == 0: + self.dyn_button.deselect() + + self.sema_button = customtkinter.CTkCheckBox(self.extended_right_frame, text="SMEA", variable=self.sema_button_var,font=my_font, command=sema_pressed) + self.sema_button.grid(row=3, column=0, pady=5, padx=10, sticky="w") + self.dyn_button_var = customtkinter.IntVar() + + def sema_dyn_pressed(): + if self.dyn_button_var.get() == 1: + self.sema_button.select() + + self.dyn_button = customtkinter.CTkCheckBox(self.extended_right_frame, text="SMEA+DYN", variable=self.dyn_button_var, font=my_font, command=sema_dyn_pressed) + self.dyn_button.grid(row=3, column=1, pady=5, padx=10, sticky="w") + self.state_label = customtkinter.CTkLabel(self.extended_right_frame, text="state : idle", font=my_font) + self.state_label.grid(row=3, column=2, columnspan=2, pady=5, padx=10, sticky="n") + self.instant_row_button = customtkinter.CTkButton(self.extended_right_frame, text="인스턴트 이벤트", font=my_font, fg_color="grey10", command=self.open_instant_event) + self.instant_row_button.grid(row=3, column=4,pady=5, padx=5, sticky="nsew") + GENERATE_EVENT = "<>" + sync_text() + self.bind(GENERATE_EVENT, lambda x=None: NAIA_generate(self)) + self.output_file_path_personal = False + self.png_name_rule = "time" + self.name_var = customtkinter.StringVar(value=self.png_name_rule) + self.window_resize_last_access = None + self.last_window_size_conf = None + self.generation_count = 0 + self.random_artist_list = None + self.random_artist = [] + self.random_artist_prefix = customtkinter.StringVar(value="none") + + def on_window_resize(event): + if datetime.now() == app.last_window_size_conf: + return + time_difference = datetime.now() - app.start_time_prime + if (time_difference.total_seconds() < 3): + return + if app.last_window_size is None: + app.last_window_size = (app.winfo_width(), app.winfo_height()) + # 윈도우가 최대화되었는지 확인 + current_size = (app.winfo_width(), app.winfo_height()) + if current_size != app.last_window_size: + app.last_window_size = current_size + if app.state() == 'zoomed': + print(app.winfo_width()) + self.right_frame.configure(width=1200) + self.image_label.grid_forget() + self.image_label_under_frame.grid_forget() + self.image_history_sub_frame.grid_forget() + self.image_history_button_down.grid_forget() + self.image_history_sub_frame.grid(row=0, pady=45, rowspan=12, column=1, sticky="n") + self.image_history_button_down.grid(row=9, column=1, pady=5, padx=5, sticky="n") + self.image_label.grid(row=0, column=0,rowspan=14, padx=10, pady=5, sticky="w") + self.hidden_frame.grid(row=0, column=2,rowspan=14, sticky="nsew") + self.history_export.grid(row=10, column=1, pady=1, padx=5, sticky="n") + self.random_artist_button = customtkinter.CTkCheckBox(self.hidden_frame, text="랜덤작가 추가", variable=self.random_artist_var, font=my_font, state="disabled") + self.random_artist_button.grid(row=0, column=0,pady=5, padx=5, sticky="nsew") + self.seed_fix_button = customtkinter.CTkCheckBox(self.hidden_frame, text="시드고정 ", variable=self.seed_fix_var, font=my_font) + self.seed_fix_button.grid(row=1, column=0,pady=5, padx=5, sticky="nsew") + self.seed_entry = customtkinter.CTkEntry(self.hidden_frame, textvariable=self.entry_seed_value) + self.seed_entry.grid(row=1, column=1,pady=5, padx=5, sticky="nsew") + self.random_artist_manage_button = customtkinter.CTkButton(self.hidden_frame, text="랜덤작가 관리", font=my_font, command=self.random_artist_window) + self.random_artist_manage_button.grid(row=0, column=1,pady=5, padx=5, sticky="nsew") + self.random_resolution_button2 = customtkinter.CTkCheckBox(self.hidden_frame, text="랜덤 해상도", variable=self.random_resolution_var, font=my_font) + self.random_resolution_button2.grid(row=3, column=0,pady=5, padx=5, sticky="nsew") + self.resolution_button2 = customtkinter.CTkComboBox(self.hidden_frame, width=160,values=["1024 x 1024", "960 x 1088", "896 x 1152", "832 x 1216", "1088 x 960", "1152 x 896", "1216 x 832"], variable=self.resolution_var, font=my_font) + self.resolution_button2.grid(row=3, column=1, padx=5, sticky="w") + self.sampler_label2 = customtkinter.CTkLabel(self.hidden_frame, text="Sampler :", font=my_font) + self.sampler_label2.grid(row=4, column=0,pady=5, padx=5, sticky="w") + self.sampler_button2 = customtkinter.CTkComboBox(self.hidden_frame, width=160,values=["k_euler", "k_euler_ancestral", "k_dpmpp_2s_ancestral", "k_dpmpp_sde", "k_dpmpp_2m"], variable=self.sampler_var, font=my_font) + self.sampler_button2.grid(row=4, column=1, columnspan=2, pady=5, padx=5, sticky="e") + self.cfg_scale_label2 = customtkinter.CTkLabel(self.hidden_frame, text="CFG Scale : ", font=my_font) + self.cfg_scale_label2.grid(row=5, column=0,pady=5, padx=5, sticky="w") + self.cfg_scale_entry2 = customtkinter.CTkEntry(self.hidden_frame, width=65, textvariable=self.cfg_scale_var) + self.cfg_scale_entry2.grid(row=5, column=1,pady=5, padx=5, sticky="w") + self.prompt_guidance_rescale_label2 = customtkinter.CTkLabel(self.hidden_frame, text="P.Guide Rescale :", font=my_font) + self.prompt_guidance_rescale_label2.grid(row=6, column=0, pady=5, padx=5, sticky="w") + self.prompt_guidance_rescale_entry2 = customtkinter.CTkEntry(self.hidden_frame, width=65, textvariable=self.prompt_guidance_rescale_var) + self.prompt_guidance_rescale_entry2.grid(row=6, column=1,pady=5, padx=5, sticky="w") + self.sema_button2 = customtkinter.CTkCheckBox(self.hidden_frame, text="SEMA", variable=self.sema_button_var,font=my_font) + self.sema_button2.grid(row=7, column=0, pady=5, padx=5, sticky="w") + self.dyn_button2 = customtkinter.CTkCheckBox(self.hidden_frame, text="SEMA+DYN", variable=self.dyn_button_var, font=my_font) + self.dyn_button2.grid(row=7, column=1, pady=5, padx=5, sticky="w") + self.open_save_folder2 = customtkinter.CTkButton(self.hidden_frame, text="폴더 열기", font=my_font, fg_color="transparent", command=lambda: open_file_explorer(self), width=80) + self.open_save_folder2.grid(row=5, column=1,padx=5, pady=5, sticky="e") + self.extra_setting_button2 = customtkinter.CTkButton(self.hidden_frame, width=80, text="기타설정", font=my_font, fg_color="grey", hover_color="grey5", command=lambda: show_advanced_settings(self)) + self.extra_setting_button2.grid(row=6, column=1,pady=5, padx=5, sticky="e") + self.recommended_prompt_button2 = customtkinter.CTkButton(self.hidden_frame, text="추천 프롬프트", font=my_font, command= self.open_prompt_window) + self.recommended_prompt_button2.grid(row=9, column=0, columnspan=2, pady=5, padx=5, sticky="nsew") + self.wildcard_manager_button2 = customtkinter.CTkButton(self.hidden_frame, text="와일드카드 관리", font=my_font, command=self.open_wildcard_window) + self.wildcard_manager_button2.grid(row=10, column=0,columnspan=2, pady=5, padx=5, sticky="nsew") + self.character_search_button2 = customtkinter.CTkButton(self.hidden_frame, text="캐릭터 검색", fg_color="#7030A0", hover_color="#481F67", font=my_font, command= lambda: open_Character_search(self)) + self.character_search_button2.grid(row=11, column=0,columnspan=2, pady=5, padx=5, sticky="nsew") + self.window_label.grid_forget() + self.window_label = customtkinter.CTkLabel(self.hidden_frame, text=str(self.window)+" / "+ str(len(self.image_queue)), font=my_font) + self.window_label.grid(row=12, column=0,padx=5, pady=5, sticky="w") + self.request_upper_size_button2 = customtkinter.CTkButton(self.hidden_frame, text="img2img / inpaint",fg_color="transparent", hover_color="grey10", text_color="#FFFF97", font=my_font, command=lambda: img2img(self), width=110) + self.request_upper_size_button2.grid(row=12, column=1,padx=5, pady=5, sticky="nsew") + + if self.image_queue: + current_image = Image.open(self.image_queue[self.window-1][3]) + original_width, original_height = current_image.size + max_size = app.winfo_screenheight()-100 + if original_width > max_size or original_height > max_size: + new_image = Image.new("RGB", (max_size, max_size), "black") + new_image.paste(current_image, ((max_size - original_width) // 2, (max_size - original_height) // 2)) + instant_result_image = customtkinter.CTkImage(new_image, size=(max_size, max_size)) + else: + instant_result_image = customtkinter.CTkImage(current_image, size=(max_size, max_size)) + self.image_label.configure(image=instant_result_image) + else: + self.image_label.configure(image=customtkinter.CTkImage(white_image, size=(app.winfo_screenheight()-100, app.winfo_screenheight()-100))) + else: + self.right_frame.configure(width=768) + self.image_label_report.grid_forget() + self.image_label.grid_forget() + self.image_label_under_frame.grid_forget() + self.image_history_sub_frame.grid_forget() + self.image_history_button_up.grid_forget() + self.image_history_button_down.grid_forget() + self.image_label.grid(row=1, column=0, rowspan=14, padx=10, pady=5, sticky="w") + self.image_label_under_frame.grid(row=0, column=0, padx=10, pady=5, sticky="ew") + self.image_history_sub_frame.grid(row=1, rowspan=12, column=1, sticky="n") + self.image_label_report.grid(row=16, column=0, padx=10, pady=5, sticky="ew") + self.image_history_button_up.grid(row=0, column=1, pady=10, padx=5, sticky="n") + self.image_history_button_down.grid(row=13, column=1, pady=5, padx=5, sticky="n") + self.history_export.grid(row=14, column=1, pady=10, padx=5, sticky="n") + self.hidden_frame.grid_forget() + self.window_label.grid_forget() + self.window_label = customtkinter.CTkLabel(self.image_label_under_frame, text=str(self.window)+" / "+ str(len(self.image_queue)), font=my_font) + self.window_label.grid(row=0, column=4,padx=5, pady=5, sticky="w") + + + if self.image_queue: + self.image_label.configure(image=customtkinter.CTkImage(self.image_queue[self.window-1][0], size=(620, 620))) + else: + self.image_label.configure(image=customtkinter.CTkImage(white_image, size=(620, 620))) + app.last_window_size_conf = datetime.now() + self.bind("", on_window_resize) + + def on_ctrl_press(event): + self.control_pressed = True + self.search_button.configure(text="심층 검색") + self.image_history_button_up.configure(text="▲ (Top)") + self.image_history_button_down.configure(text="▼ (100)") + self.request_upper_size_button.configure(text="img2img (INST)", text_color="#FD8853") + self.history_export.configure(text=f"export ({len(self.image_queue)})", fg_color="#1F6AA5", hover_color="#144870", text_color="#DCE4EE") + if app.state() == 'zoomed': + self.request_upper_size_button2.configure(text="img2img (INST)", text_color="#FD8853") + + def on_ctrl_release(event): + self.control_pressed = False + self.search_button.configure(text="검색") + self.image_history_button_up.configure(text="▲") + self.image_history_button_down.configure(text="▼") + self.request_upper_size_button.configure(text="img2img / inpaint", text_color="#FFFF97") + self.history_export.configure(text="+ FAV (☆)", fg_color="#F5F3C2", hover_color="#A69F40", text_color="black") + if self.image_queue and len(self.image_queue[self.current_window]) >= 5: + self.history_export.configure(state="normal") + else: + self.history_export.configure(state="disabled") + if app.state() == 'zoomed': + self.request_upper_size_button2.configure(text="img2img / inpaint", text_color="#FFFF97") + + def on_escape(event=None): + self.focus_set() + self.bind('', on_escape) + + self.control_pressed = False + self.bind_all("", on_ctrl_press) + self.bind_all("", on_ctrl_release) + self.bind('', copy_file) + self.last_window_size = None + self.random_artist_select = "global" + self.random_artist_list_length = 0 + self.protocol("WM_DELETE_WINDOW", lambda: (self.Automation_setting.destroy())) + self.wildcard_dict = None + self.wildacrds_dir = "" + + def on_arrow_key(event): + key_pressed = event.keysym + #self.focus_set() + if key_pressed == "Up" and self.image_history_button_up.cget("state") == "normal": + up_button_pressed() + elif key_pressed == "Down" and self.image_history_button_down.cget("state") == "normal": + down_button_pressed() + + def img2img(self): + if self.control_pressed: + instant_img2img(self) + self.control_pressed = False + else: + global img2img_window + if img2img_window is not None: + img2img_window.destroy() + img2img_window = None + _img2img(self) + + def _img2img(self): + global iimg, img2img_window, inpaint_mode + + def instant_image_generation(self, current_lookup): + try: + img2img_window.attributes('-topmost', False) + except: + pass + def image_to_base64(image): + image_bytesIO = io.BytesIO() + img.save(image_bytesIO, format="png") + return base64.b64encode(image_bytesIO.getvalue()).decode() + def mask_to_base64(image): + # PIL.Image 객체를 넘파이 배열로 변환 + i = np.array(image) + # RGBA 채널 처리를 위해 알파 채널 추가 전 이미지 처리 + if i.shape[-1] == 3: # RGB 이미지인 경우 + alpha = np.sum(i, axis=-1) > 0 # 알파 마스크 생성 + alpha = np.uint8(alpha * 255) # 알파 채널 값으로 변환 + rgba = np.dstack((i, alpha)) # RGBA 이미지 생성 + elif i.shape[-1] == 4: # 이미 RGBA 이미지인 경우 + rgba = i + else: + raise ValueError("Unsupported image format") + + # 넘파이 배열에서 PIL.Image 객체로 변환 + img = Image.fromarray(rgba) + + # PIL.Image 객체를 Base64 문자열로 변환 + image_bytesIO = io.BytesIO() + img.save(image_bytesIO, format="png") + return base64.b64encode(image_bytesIO.getvalue()).decode() + try: + i2i_generate_button.configure(state="disabled") + except: + pass + try: + request_prompt = i2i_text_input.get("0.0", "end") + except: + request_prompt = self.text_input.get("0.0", "end") + try: + request_seed = current_lookup[2] + filename = current_lookup[3] + except: + current_lookup = self.image_queue[self.current_window].copy() + request_seed = current_lookup[2] + filename = current_lookup[3] + img = Image.open(filename) + send_image = image_to_base64(img) + scale_pre = self.cfg_scale_entry.get() + try: + steps = request_upper_size_steps.get() + except: + steps = 28 + try: + scale_pre = float(scale_pre) + except: + scale_pre = 5.0 + self.cfg_scale_var.set("5.0") + rescale_pre = self.prompt_guidance_rescale_entry.get() + try: + rescale_pre = float(rescale_pre) + except: + rescale_pre = 0 + self.prompt_guidance_rescale_var.set("0") + try: + steps = int(steps) + except: + steps = 28 + self.cfg_scale_var.set("28") + uncond_pre = self.uncond_strength_entry.get() + try: + uncond_pre = float(uncond_pre) + uncond_pre = round(uncond_pre / 0.05) * 0.05 + if (uncond_pre > 1.5): + uncond_pre = 1.5 + self.uncond_strength_entry.delete(0, "end") + self.uncond_strength_entry.insert(0, "1.5") + except: + uncond_pre = 1.0 + self.uncond_strength_entry.delete(0, "end") + self.uncond_strength_entry.insert(0, "1.0") + try: + i2i_negative_text = i2i_negative_input.get("0.0", "end-1c") + except: + i2i_negative_text = self.negative_prompt_input.get("0.0", "end-1c") + inpaint_check = sum(sum(row) for row in mirror_image) >= 50 + gen_request = { + "width":self.i2i_width if not inpaint_check else cwidth , + "height":self.i2i_height if not inpaint_check else cheight, + "quality_toggle":self.auto_quality_toggle.get(), + "seed":request_seed if self.i2i_seed_hold_check == True else random.randint(0,9999999999), + "sampler":self.sampler_button.get(), + "scale":scale_pre, + "uncond_scale":uncond_pre, + "sema":self.sema_button.get(), + "sema_dyn": self.dyn_button.get(), + "cfg_rescale": rescale_pre, + "prompt": request_prompt, + "negative":i2i_negative_text, + "user_screen_size": self.get_max_size(), + "start_time": self.start_time, + "access_token": self.access_token, + "save_folder": self.output_file_path, + "png_rule": self.name_var.get(), + "type": "upper" if not inpaint_check else "inpaint", + "steps": steps if steps <= 50 else 50, + "image": send_image + } + if not inpaint_check: + gen_request["strength"] = round(self.i2i_value, 2) + gen_request["noise"] = 0 + if gen_request["type"] == "inpaint": + gen_request["mask"] = mask_to_base64(create_and_save_mask_image()) + gen_request["add_original_image"] = True if overlay_original_image_var.get()==1 else False + def run_generation(): + if gen_request["png_rule"] == "count": + self.generation_count += 1 + gen_request["count"] =self.generation_count + self.state_label.configure(text ="state : img2img 요청됨 ", text_color = "#FFFF97") + result_image, result_prompt, result_seed, info, filename = NAIA_generation.generate(gen_request) + self.state_label.configure(text ="state : img2img 요청 반환됨", text_color = "#DCE4EE") + self.get_anlas() + i2i_generate_button.configure(state="normal") + if info: + temp = info.get('Comment', '') + temp = temp[temp.find("prompt")+10:temp.find("skip_cfg_below_sigma")-3].replace('"','') + else: + temp = result_prompt + self.image_label_report.configure(state="normal") + self.image_label_report.delete("0.0", "end") + self.image_label_report.configure(text_color="#DCE4EE") + self.image_label_report.insert("0.0", temp) + self.image_label_report.configure(state="disabled") + if result_image: + if app.state() != 'zoomed': + instant_result_image = customtkinter.CTkImage(result_image, size=(620,620)) + else: + current_image = Image.open(filename) + original_width, original_height = current_image.size + max_size = app.winfo_screenheight()-100 + if original_width > max_size or original_height > max_size: + new_image = Image.new("RGB", (max_size, max_size), "black") + new_image.paste(current_image, ((max_size - original_width) // 2, (max_size - original_height) // 2)) + instant_result_image = customtkinter.CTkImage(new_image, size=(max_size, max_size)) + else: + instant_result_image = customtkinter.CTkImage(current_image, size=(max_size, max_size)) + self.image_label.configure(image=instant_result_image) + img_label_result.grid(row=0, column=1) + img_label_result.configure(image=customtkinter.CTkImage(result_image, size=(576, 576))) + set_image_to_queue(result_image, result_prompt, str(result_seed), filename) + generation_thread = threading.Thread(target=run_generation, daemon=True) + generation_thread.start() + + def find_max_resolution(width, height, max_pixels=2166784, multiple_of=64): + ratio = width / height + + max_width = int((max_pixels * ratio)**0.5) + max_height = int((max_pixels / ratio)**0.5) + + max_width = (max_width // multiple_of) * multiple_of + max_height = (max_height // multiple_of) * multiple_of + + while max_width * max_height > max_pixels: + max_width -= multiple_of + max_height = int(max_width / ratio) + max_height = (max_height // multiple_of) * multiple_of + + return (max_width, max_height) + + img2img_window = customtkinter.CTkToplevel() + img2img_window.title("img2img") + img2img_window.attributes('-topmost', True) + img2img_window.resizable(width=False, height=False) + + current_lookup = copy.deepcopy(self.image_queue[self.current_window]) + filename = current_lookup[3] + img = Image.open(filename) + width, height = img.size + + img2img_window_left = customtkinter.CTkFrame(img2img_window) + img2img_window_left.grid(row=0, column=0, pady=5, padx=5, sticky="nsew") + img2img_window_right = customtkinter.CTkFrame(img2img_window, width=586) + img2img_window_right.grid(row=0, column=1, pady=5, padx=5, sticky="nsew") + #img2img_window_menu = customtkinter.CTkFrame(img2img_window, width=140) + #img2img_window_menu.grid(row=0, column=2, pady=5, padx=5, sticky="nsew") + + iimg = None + inpaint_mode = False + iimg = Image.open(filename).convert("RGB") + original_image = iimg.copy() + cwidth, cheight = iimg.size + + # mirror_width와 mirror_height 계산 + mirror_width = width // 8 + mirror_height = height // 8 + mirror_image = [[0 for _ in range(mirror_height)] for _ in range(mirror_width)] + + img_label = customtkinter.CTkCanvas(img2img_window_left, width=cwidth, height=cheight) + img_label.grid(row=0, column=0) + img_label_result = customtkinter.CTkLabel(img2img_window_left, text="") + + # 마스크 이미지 생성 및 저장 함수 + def create_and_save_mask_image(): + # 마스크 이미지 생성: 원본 이미지 크기의 1/8, 투명 배경 + mask_image = Image.new('RGBA', (mirror_width, mirror_height), (0, 0, 0, 0)) + draw = ImageDraw.Draw(mask_image) + + # 이중리스트를 순회하며 value > 0인 위치에 하얀색으로 칠함 + for x in range(mirror_width): + for y in range(mirror_height): + if mirror_image[x][y] > 0: + draw.point((x, y), fill=(255, 255, 255, 255)) + + # 마스크 이미지를 파일로 저장 + return mask_image + + tk_image = ImageTk.PhotoImage(iimg) + canvas = img_label + canvas.create_image(0, 0, anchor="nw", image=tk_image) + canvas.image = tk_image # 참조를 유지 + + def create_custom_cursor(): + # 40x40px 투명 이미지 생성 + cursor_image = Image.new("RGBA", (40, 40), (0, 0, 0, 0)) + draw = ImageDraw.Draw(cursor_image) + # 1px 하얀색 테두리 그리기 + draw.rectangle([0, 0, 39, 39], outline=(255, 255, 255, 255)) + return cursor_image + + # 마우스 이동 이벤트에 대한 핸들러 + def on_mouse_move(event): + # 이전에 그려진 커스텀 커서가 있다면 삭제 + canvas.delete("custom_cursor") + # 마우스 위치에 커스텀 커서 이미지를 표시 + cursor_image = create_custom_cursor() + tk_cursor_image = ImageTk.PhotoImage(cursor_image) + # canvas에 커스텀 커서 이미지 렌더링. ��� 때, 'tags' 옵션을 사용하여 태그를 지정 + canvas.create_image(event.x, event.y, image=tk_cursor_image, anchor="center", tags="custom_cursor") + # 커스텀 커서 이미지의 참조를 별도로 유지 + canvas.cursor_image = tk_cursor_image + + # 마우스 이동 이벤트 바인딩 + canvas.bind("", on_mouse_move) + + def inpaint_check(): + global inpaint_mode + check = sum(sum(row) for row in mirror_image) >= 50 + if(check and not inpaint_mode): + inpaint_mode = True + i2i_slider_label.grid_forget() + i2i_slider.grid_forget() + i2i_resolution_label.grid_forget() + i2i_radio_frame.grid_forget() + overlay_original_image.grid(row=0, column=0, padx=30, sticky="n") + cancel_inpainting_button.grid(row=0, column=1, padx=30, sticky="n") + elif(inpaint_mode and not check): + inpaint_mode = False + overlay_original_image.grid_forget() + cancel_inpainting_button.grid_forget() + i2i_slider_label.grid(row=0, column=0, sticky="nsew") + i2i_slider.grid(row=0, column=1, sticky="nsew") + i2i_resolution_label.grid(row=1, column=0, sticky="nsew") + i2i_radio_frame.grid(row=1, column=1, pady=5, sticky="nsew") + + + def update_canvas_image(): + global tk_image # 전역 변수 사용 + tk_image = ImageTk.PhotoImage(iimg) + canvas.create_image(0, 0, anchor="nw", image=tk_image, tags="original_image") # 'tags' 옵션을 사용하여 태그 지정 + canvas.image = tk_image # 원본 이미지 참조 업데이트 + + def on_canvas_click_or_move(event): + x, y = event.x // 8, event.y // 8 + paint_on_image(x, y) + update_canvas_image() + + def paint_on_image(x, y): + draw = ImageDraw.Draw(iimg, 'RGBA') + for i in range(max(0, x-2), min(mirror_width, x+3)): + for j in range(max(0, y-2), min(mirror_height, y+3)): + mirror_image[i][j] += 1 # 마스크 업데이트 + if mirror_image[i][j] == 1: # 마스크 값이 0보다 큰 경우에만 페인트 칠함 + draw.rectangle([(i*8, j*8), ((i+1)*8-1, (j+1)*8-1)], fill=(0, 0, 255, 128)) + inpaint_check() + + def remove_paint_from_image(x, y): + global iimg + for i in range(max(0, x-2), min(mirror_width, x+3)): + for j in range(max(0, y-2), min(mirror_height, y+3)): + mirror_image[i][j] = 0 # 마스크 값을 0으로 설정 + + # 원본 이미지에서 해당 영역의 픽셀 값을 가져옴 + original_pixels = original_image.load() + current_pixels = iimg.load() + + for xi in range(i*8, min((i+1)*8, width)): + for yi in range(j*8, min((j+1)*8, height)): + # 원본 이미지의 픽셀 값을 현재 이미지에 복원 + current_pixels[xi, yi] = original_pixels[xi, yi] + inpaint_check() + + def on_canvas_right_click_or_move(event): + # 마우스 우클릭 위치를 축소된 좌표로 변환 + x, y = event.x // 8, event.y // 8 + # 페인트 제거 함수 호출 + remove_paint_from_image(x, y) + # 변경된 이미지를 캔버스에 다시 표시 + update_canvas_image() + + # 우클릭 이벤트와 우클릭 드래그 이벤트에 대한 바인딩 + canvas.bind("", on_canvas_right_click_or_move) + canvas.bind("", on_canvas_right_click_or_move) + canvas.bind("", on_canvas_click_or_move) + canvas.bind("", on_canvas_click_or_move) + + highlight_tags = ["aqua","black","blonde","blue","brown","cyan","green","grey","orange","pink","purple","red","violet","white","yellow","mouth", "eyes", " cap", " ears", " girl", " ornament", " hat", "beret", " ear "] + + def insert_with_color(): + words = [word.strip() for word in current_lookup[1].split(',')] + for index, word in enumerate(words): + highlight = False + start_index = i2i_text_input.index("end-1c") + if index == len(words) - 1: # 마지막 원소인 경우 + i2i_text_input.insert("end", word) + else: + i2i_text_input.insert("end", word + ", ") + end_index = i2i_text_input.index("end-1c") + + for tag in highlight_tags: + if tag in word: + highlight = True + if word == "hat": highlight = True + # 조건 확인 + if word in cd.character_dictionary or word in tagbag.bag_of_tags[5:] or 'tail' in word or 'pupil' in word: + i2i_text_input.tag_add(word, start_index, end_index) + i2i_text_input.tag_config(word, foreground="#FFFF97") + elif highlight: + i2i_text_input.tag_add(word, start_index, end_index) + i2i_text_input.tag_config(word, foreground="#AED395") + + i2i_text_input_label = customtkinter.CTkLabel(img2img_window_right, text=" ------------------- i2i 프롬프트 ------------------- ", font=large_font) + i2i_text_input_label.grid(row = 0, sticky="w" ) + text_hyperlink = customtkinter.CTkLabel(img2img_window_right, text="가이드 열기 (arca.live)", font=customtkinter.CTkFont('Pretendard', 13), width=180, text_color="lightblue", cursor="hand2", state="disabled") + text_hyperlink.grid(row=0, column=0, padx=5, pady=5, sticky="e") + #text_hyperlink.bind("", lambda e: webbrowser.open_new("https://arca.live/b/aiart/95877361")) + i2i_text_input = customtkinter.CTkTextbox(img2img_window_right, width=490, height=240,font=v_large_font) + i2i_text_input.grid(row=1, column=0, pady=5, sticky="nsew") + insert_with_color() + + def slider_event(value): + i2i_slider_label.configure(text=f"Strength : {value:.2f}") + self.i2i_value = value + + def get_i2i_neagtive(filename): + with Image.open(filename) as img: + info = img.info.get('Comment', '') + start_pattern = '"uc": "' + end_pattern = '", "' + start_index = info.find(start_pattern) + if start_index != -1: + # 실제 추출할 문자열의 시작 인덱스 조정 + start_index += len(start_pattern) + + # 종료 패턴의 인덱스를 찾기 + end_index = info.find(end_pattern, start_index) + if end_index != -1: + # 문자열 추출 + extracted_string = info[start_index:end_index] + else: + if app.current_window in app.import_image_negative: + extracted_string = app.import_image_negative[app.current_window] + else: + extracted_string = "" + return extracted_string + + i2i_offset_input_label = customtkinter.CTkLabel(img2img_window_right, text=" ------------------- i2i 네거티브 ------------------- ", font=large_font) + i2i_offset_input_label.grid(row = 2, column=0,sticky="w" ) + i2i_character_search_button = customtkinter.CTkButton(img2img_window_right, text="캐릭터 검색", fg_color="#7030A0", hover_color="#481F67", font=my_font, command= lambda: open_Character_search(self)) + i2i_character_search_button.grid(row=2, column=0, pady=5, padx=5, sticky="e") + i2i_negative_input = customtkinter.CTkTextbox(img2img_window_right, width=490, height=115,font=v_large_font) + i2i_negative_input.grid(row=3, column=0, pady=5, sticky="nsew") + i2i_negative = get_i2i_neagtive(filename) + i2i_negative_input.insert("0.0", i2i_negative) + + i2i_slider_frame = customtkinter.CTkFrame(img2img_window_right, fg_color="#333333") + i2i_slider_frame.grid(row=4, column=0, pady=5, padx=5, sticky="nsew") + + i2i_value = round(self.i2i_value, 2) + i2i_seed_hold_check = 1 if self.i2i_seed_hold_check else 0 + i2i_slider_label = customtkinter.CTkLabel(i2i_slider_frame, text="Strength : "+str(i2i_value), font=large_font, width=140) + i2i_slider_label.grid(row=0, column=0, sticky="nsew") + i2i_slider = customtkinter.CTkSlider(i2i_slider_frame, from_=0, to=0.99, number_of_steps=100, command=slider_event, width=340) + i2i_slider.grid(row=0, column=1, sticky="nsew") + i2i_slider.set(i2i_value) + i2i_resolution_label = customtkinter.CTkLabel(i2i_slider_frame, text="해상도 배율", font=large_font, width=140) + i2i_resolution_label.grid(row=1, column=0, sticky="nsew") + + def restore_original_image(): + global tk_image, iimg + iimg = Image.open(filename).convert("RGB") + tk_image = ImageTk.PhotoImage(iimg.copy()) # 원본 이미지를 사용하여 Tkinter 호환 이미지 생성 + canvas.create_image(0, 0, anchor="nw", image=tk_image) + canvas.image = tk_image + for i in range(len(mirror_image)): + for j in range(len(mirror_image[i])): + mirror_image[i][j] = 0 + inpaint_check() + + overlay_original_image_var = customtkinter.IntVar() + overlay_original_image = customtkinter.CTkCheckBox(i2i_slider_frame, text="Overlay Original Image", font=large_font, width=140, variable=overlay_original_image_var) + cancel_inpainting_button = customtkinter.CTkButton(i2i_slider_frame, text="인페인트 취소", font=large_font, command=restore_original_image) + + i2i_radio_frame = customtkinter.CTkFrame(i2i_slider_frame, fg_color="#333333") + i2i_radio_frame.grid(row=1, column=1, pady=5, sticky="nsew") + + radio_var = customtkinter.IntVar(value=self.i2i_resolution) + i2i_request_width = customtkinter.IntVar(value=width) + i2i_request_height = customtkinter.IntVar(value=height) + img2img_window.after(2500, lambda: img2img_window.attributes('-topmost', False)) + + + + def radiobutton_event(): + v = radio_var.get() + if v == 0: + request_width = width + request_height = height + self.i2i_resolution = 0 + elif v == 1: + request_width, request_height = find_max_resolution(width, height, 2166784) + self.i2i_resolution = 1 + elif v == 2: + request_width, request_height = find_max_resolution(width, height, 2662400) + self.i2i_resolution = 2 + i2i_resolution_label.configure(text=f"{request_width} x {request_height}") + i2i_request_width.set(request_width) + self.i2i_width = request_width + i2i_request_height.set(request_height) + self.i2i_height = request_height + + radio_button_1 = customtkinter.CTkRadioButton(i2i_radio_frame, text="해상도 유지", command=radiobutton_event, variable= radio_var, value=0, font=large_font) + radio_button_1.grid(row=0, column=0, sticky="nsew") + radio_button_2 = customtkinter.CTkRadioButton(i2i_radio_frame, text="2.16 MP", command=radiobutton_event, variable= radio_var, value=1, font=large_font) + radio_button_2.grid(row=0, column=1, sticky="nsew") + radio_button_3 = customtkinter.CTkRadioButton(i2i_radio_frame, text="2.66 MP", command=radiobutton_event, variable= radio_var, value=2, font=large_font) + radio_button_3.grid(row=0, column=2, sticky="nsew") + radiobutton_event() + + i2i_generate_frame = customtkinter.CTkFrame(img2img_window_right, fg_color="#2B2B2B") + i2i_generate_frame.grid(row=5, column=0, pady=5, sticky="n") + + i2i_offset_input_label2 = customtkinter.CTkLabel(i2i_generate_frame, text=" 해상도와 Strength에 따라 Anlas가 소모됩니다 ", font=large_font, text_color="#FFFF97") + i2i_offset_input_label2.grid(row = 0, column=0, columnspan=3, sticky="n" ) + + def seed_button_event(): + self.i2i_seed_hold_check = True if request_upper_size_seed_hold.get() == 1 else False + + request_upper_size_seed_hold_var = customtkinter.IntVar(value=i2i_seed_hold_check) + request_upper_size_seed_hold = customtkinter.CTkCheckBox(i2i_generate_frame, text="시드고정 Step : ", font=my_font, variable=request_upper_size_seed_hold_var, command=seed_button_event) + request_upper_size_seed_hold.grid(row=1, column=0, padx=5, pady=5, sticky="nsew") + request_upper_size_seed_hold.select() if i2i_seed_hold_check == 1 else request_upper_size_seed_hold.deselect() + request_upper_size_steps = customtkinter.StringVar(value="28") + request_upper_size_steps_entry = customtkinter.CTkEntry(i2i_generate_frame, font=my_font, textvariable=request_upper_size_steps, width=60) + request_upper_size_steps_entry.grid(row=1, column=1,padx=5, pady=5, sticky="nsew") + i2i_generate_button = customtkinter.CTkButton(i2i_generate_frame, text="Request I2I", font=large_font, command=lambda: instant_image_generation(self, current_lookup)) + i2i_generate_button.grid(row = 1, column= 2, pady=5, sticky="n" ) + + def instant_img2img(self): + current_lookup = self.image_queue[self.current_window].copy() + def instant_image_generation(self): + def image_to_base64(image): + image_bytesIO = io.BytesIO() + img.save(image_bytesIO, format="png") + return base64.b64encode(image_bytesIO.getvalue()).decode() + request_prompt = current_lookup[1] + request_seed = current_lookup[2] + filename = current_lookup[3] + img = Image.open(filename) + send_image = image_to_base64(img) + scale_pre = self.cfg_scale_entry.get() + steps = 28 + try: + scale_pre = float(scale_pre) + except: + scale_pre = 5.0 + self.cfg_scale_var.set("5.0") + rescale_pre = self.prompt_guidance_rescale_entry.get() + try: + rescale_pre = float(rescale_pre) + except: + rescale_pre = 0 + self.prompt_guidance_rescale_var.set("0") + try: + steps = int(steps) + except: + steps = 28 + #self.cfg_scale_var.set("28") + uncond_pre = self.uncond_strength_entry.get() + try: + uncond_pre = float(uncond_pre) + uncond_pre = round(uncond_pre / 0.05) * 0.05 + if (uncond_pre > 1.5): + uncond_pre = 1.5 + self.uncond_strength_entry.delete(0, "end") + self.uncond_strength_entry.insert(0, "1.5") + except: + uncond_pre = 1.0 + self.uncond_strength_entry.delete(0, "end") + self.uncond_strength_entry.insert(0, "1.0") + i2i_negative_text = self.negative_prompt_input.get("0.0", "end-1c") + gen_request = { + "width":self.i2i_width, + "height":self.i2i_height, + "quality_toggle":self.auto_quality_toggle.get(), + "seed":request_seed if self.i2i_seed_hold_check == True else random.randint(0,9999999999), + "sampler":self.sampler_button.get(), + "scale":scale_pre, + "uncond_scale":uncond_pre, + "sema":self.sema_button.get(), + "sema_dyn": self.dyn_button.get(), + "cfg_rescale": rescale_pre, + "prompt": request_prompt, + "negative":i2i_negative_text, + "user_screen_size": self.get_max_size(), + "start_time": self.start_time, + "access_token": self.access_token, + "save_folder": self.output_file_path, + "png_rule": self.name_var.get(), + "type": "upper", + "steps": steps if steps <= 50 else 50, + "image": send_image, + "strength": round(self.i2i_value, 2), + "noise": 0 + } + def run_generation(): + if gen_request["png_rule"] == "count": + self.generation_count += 1 + gen_request["count"] =self.generation_count + self.state_label.configure(text ="state : img2img 요청됨 ", text_color = "#FFFF97") + result_image, result_prompt, result_seed, info, filename = NAIA_generation.generate(gen_request) + self.state_label.configure(text ="state : img2img 요청 반환됨", text_color = "#DCE4EE") + self.get_anlas() + if info: + temp = info.get('Comment', '') + temp = temp[temp.find("prompt")+10:temp.find("skip_cfg_below_sigma")-3].replace('"','') + else: + temp = result_prompt + self.image_label_report.configure(state="normal") + self.image_label_report.delete("0.0", "end") + self.image_label_report.configure(text_color="#DCE4EE") + self.image_label_report.insert("0.0", temp) + self.image_label_report.configure(state="disabled") + if result_image: + if app.state() != 'zoomed': + instant_result_image = customtkinter.CTkImage(result_image, size=(620,620)) + else: + current_image = Image.open(filename) + original_width, original_height = current_image.size + max_size = app.winfo_screenheight()-100 + if original_width > max_size or original_height > max_size: + new_image = Image.new("RGB", (max_size, max_size), "black") + new_image.paste(current_image, ((max_size - original_width) // 2, (max_size - original_height) // 2)) + instant_result_image = customtkinter.CTkImage(new_image, size=(max_size, max_size)) + else: + instant_result_image = customtkinter.CTkImage(current_image, size=(max_size, max_size)) + self.image_label.configure(image=instant_result_image) + set_image_to_queue(result_image, result_prompt, str(result_seed), filename) + generation_thread = threading.Thread(target=run_generation, daemon=True) + generation_thread.start() + + def find_max_resolution(width, height, max_pixels=2166784, multiple_of=64): + ratio = width / height + + max_width = int((max_pixels * ratio)**0.5) + max_height = int((max_pixels / ratio)**0.5) + + max_width = (max_width // multiple_of) * multiple_of + max_height = (max_height // multiple_of) * multiple_of + + while max_width * max_height > max_pixels: + max_width -= multiple_of + max_height = int(max_width / ratio) + max_height = (max_height // multiple_of) * multiple_of + + return (max_width, max_height) + + + img = Image.open(current_lookup[3]) + width, height = img.size + v = self.i2i_resolution + if v == 0: + self.i2i_width = width + self.i2i_height = height + elif v == 1: + self.i2i_width, self.i2i_height = find_max_resolution(width, height, 2166784) + elif v == 2: + self.i2i_width, self.i2i_height = find_max_resolution(width, height, 2662400) + instant_image_generation(self) + + self.bind("", on_arrow_key) + self.bind("", on_arrow_key) + self.image_generation_repeat = 1 + self.image_generation_repeat_flag = False + self.image_generation_repeat_current = 0 + self.wildcard_preopen_repeat = 1 + self.wildcard_preopen_repeat_flag = False + self.wildcard_preopen_repeat_current = 1 + self.previous_fix_prompt = None + self.i2i_value = 0.7 + self.i2i_seed_hold_check = True + self.i2i_resolution = 0 + self.i2i_width = 0 + self.i2i_height = 0 + self.i2i_steps = 28 + self.image_label.drop_target_register(DND_ALL) + self.image_label.dnd_bind("<>", self.on_drop) + self.after(500, highlight_text) + self.after(500, highlight_text_fixed) + self.after(500, highlight_text_after) + self.after(500, highlight_text_autohide) + self.after(500, highlight_text_negative) + self.after(500, highlight_text_cond) + self.after(500, highlight_text_cond_neg) + self.after(1000, open_extend_frame) + #세상에 시발 이 위까지 전부 __init__에 들어가있음 + + + def on_drop(self, event): + def read_info_from_image_stealth(image): + # trying to read stealth pnginfo + width, height = image.size + pixels = image.load() + + if image.mode != 'RGBA': + return {} + + binary_data = '' + buffer_a = '' + index_a = 0 + confirming_signature = True + reading_param_len = False + reading_param = False + read_end = False + + for x in range(width): + for y in range(height): + + r, g, b, a = pixels[x, y] + buffer_a += str(a & 1) + index_a += 1 + + if confirming_signature and x == 0: + if y == 119: # index_a == len('stealth_pngcomp') * 8: + decoded_sig = bytearray(int(buffer_a[i:i + 8], 2) for i in + range(0, len(buffer_a), 8)).decode('utf-8', errors='ignore') + if decoded_sig == 'stealth_pngcomp': + confirming_signature = False + reading_param_len = True + buffer_a = '' + index_a = 0 + + elif reading_param_len: + if y == 151: # index_a == 32: + param_len = int(buffer_a, 2) + reading_param_len = False + reading_param = True + buffer_a = '' + index_a = 0 + + elif reading_param: + if index_a == param_len: + binary_data = buffer_a + read_end = True + break + else: + read_end = True + break + + if read_end: + break + + if binary_data != '': + # Convert binary string to UTF-8 encoded text + byte_data = bytearray(int(binary_data[i:i + 8], 2) + for i in range(0, len(binary_data), 8)) + try: + decoded_data = gzip.decompress( + bytes(byte_data)).decode('utf-8') + return decoded_data + except Exception as e: + print(e) + pass + + return {} + + def download_image_from_url(url): + response = requests.get(url) + response.raise_for_status() # 에러가 발생했을 경우 예외를 던짐 + return Image.open(io.BytesIO(response.content)) + + def extract_keywords_from_image(image): + if image.info and image.info.get('dpi') is None: + result = image.info.get('Comment', '') + if result == '': + result = image.info.get('parameters', '') + if result != '': + try: + result = json.loads(result) + except: + result = image.info.get('parameters', '') + return result + else: + self.image_label_report.configure(state="normal", text_color = "#FFFF97") + self.image_label_report.delete("0.0", "end") + self.image_label_report.insert("0.0", " : 이미지에서 NAI 정보를 얻는데 실패하였습니다. 이미지에 메타데이터가 없습니다.") + self.image_label_report.configure(state="disabled") + else: + return json.loads(result) + else: + stealth_info = read_info_from_image_stealth(image) + if isinstance(stealth_info, str): + # 문자열 내의 모든 '\\'를 제거하거나 다른 문자로 대체 + try: + result = stealth_info.replace("\\", "") + comment_data = json.loads(result) + return comment_data + except: + return stealth_info.replace("\\", "") + elif stealth_info and 'Comment' in stealth_info: + try: + # Comment 값이 JSON 문자열인지 확인하고, 파싱 + comment_data = json.loads(stealth_info['Comment']) + # 'prompt' 키의 값을 추출하여 반환 + if 'prompt' in comment_data: + return comment_data["prompt"].strip() + else: + self.image_label_report.configure(state="normal", text_color = "#FFFF97") + self.image_label_report.delete("0.0", "end") + self.image_label_report.insert("0.0", " : 이미지에서 NAI 정보를 얻는데 실패하였습니다. comment_data가 존재하지만 NAI와 호환되는 Stable Diffusion 이미지가 아닙니다.") + self.image_label_report.configure(state="disabled") + except json.JSONDecodeError: + self.image_label_report.configure(state="normal", text_color = "#FFFF97") + self.image_label_report.delete("0.0", "end") + self.image_label_report.insert("0.0", " : 이미지에서 NAI 정보를 얻는데 실패하였습니다.") + self.image_label_report.configure(state="disabled") + else: + self.image_label_report.configure(state="normal", text_color = "#FFFF97") + self.image_label_report.delete("0.0", "end") + self.image_label_report.insert("0.0", " : 이미지에서 NAI 정보를 얻는데 실패하였습니다. 이미지에 메타데이터가 없습니다.") + self.image_label_report.configure(state="disabled") + + file_path_or_url = event.data + file_path_or_url = file_path_or_url.strip("{}") + from_url = False + try: + # URL의 유효성 검사 + if file_path_or_url.startswith(('http://', 'https://')): + from_url = True + img = download_image_from_url(file_path_or_url) + else: + if file_path_or_url.startswith(('blob:')): + self.image_label_report.configure(state="normal", text_color = "#FFFF97") + self.image_label_report.delete("0.0", "end") + self.image_label_report.insert("0.0", " : NAI 홈페이지에서 생성한 이미지는 NAIA로 즉시 복사할 수 없습니다. 이미지를 다운로드 한 뒤, 드래그 & 드랍을 통해 NAIA에 전달 해 주세요.") + self.image_label_report.configure(state="disabled") + return + try: + img = Image.open(file_path_or_url) + except: + try: + file_path_or_url = file_path_or_url.replace("\\", '') + img = Image.open(file_path_or_url) + except: + try: + file_path_or_url = file_path_or_url.replace("\\", '/') + img = Image.open(file_path_or_url) + except: + self.image_label_report.configure(state="normal", text_color = "#FFFF97") + self.image_label_report.delete("0.0", "end") + self.image_label_report.insert("0.0", f" : Tkdnd2로부터 유효하지 않은 file path 또는 url : '{file_path_or_url}' 를 전달 받았습니다.") + self.image_label_report.configure(state="disabled") + return + + # 이미지 객체로부터 키워드 추출 + description = extract_keywords_from_image(img) + if description == '' or description == None: + return + elif type(description) == type(' '): + text_type, contents = 'str', description + if '"Software": "NovelAI"' in contents: + software = 'NAI' + else: + software = 'Local' + else: + text_type, contents = 'dict', description + if 'sm' in contents: + software = 'NAI' + else: + software = 'Local' + + #image window + import_window = customtkinter.CTkToplevel() + import_window.title("Image with Metadata found!") + import_window.attributes('-topmost', True) + import_window.resizable(width=False, height=False) + + import_window_left = customtkinter.CTkFrame(import_window) + import_window_left.grid(row=0, column=0, pady=5, padx=5, sticky="nsew") + import_window_right = customtkinter.CTkFrame(import_window, width=596) + import_window_right.grid(row=0, column=1, pady=5, padx=5, sticky="nsew") + + width, height = img.size + new_size = max(width, height) + new_image = Image.new("RGB", (new_size, new_size), 'black') + left = (new_size - width) // 2 + top = (new_size - height) // 2 + new_image.paste(img, (left, top)) + + import_window_img = customtkinter.CTkImage(new_image, size=(576, 576)) + import_window_img_label = customtkinter.CTkLabel(import_window_left, text="", image=import_window_img) + import_window_img_label.grid(row=0, column=0) + + if software == 'Local': + local_prompt = customtkinter.CTkTextbox(import_window_right, height=576, width=586, font=customtkinter.CTkFont('Pretendard', 14)) + local_prompt.grid(row=0, column=0, pady=5, padx=5, sticky="nsew") + contents = 'Image Type : Local Generated \n\n' + str(contents).replace('\n', '\n\n') + local_prompt.insert("0.0", contents) + else: + if text_type == 'str': + try: + start_marker = '"Comment": "' + end_marker = '", "request_type"' + start_index = contents.find(start_marker) + len(start_marker) + end_index = contents.find(end_marker, start_index) + text = contents[start_index:end_index] + contents = {} + except: + local_prompt = customtkinter.CTkTextbox(import_window_right, height=766, width=766, font=customtkinter.CTkFont('Pretendard', 15)) + local_prompt.grid(row=0, column=0, pady=5, padx=5, sticky="nsew") + contents = 'Image Type : Local Generated \n\n' + str(contents).replace('\n', '\n\n') + local_prompt.insert("0.0", contents) + try: + start_marker = 'prompt": "' + end_marker = '", "steps' + start_index = text.find(start_marker) + len(start_marker) + end_index = text.find(end_marker, start_index) + contents['prompt'] = text[start_index:end_index] + text = text[end_index:] + except: + pass + try: + start_marker = '"uc": "' + end_marker = '", "request_type' + start_index = text.find(start_marker) + len(start_marker) + end_index = text.find(end_marker, start_index) + contents['uc'] = text[start_index:] + text = text[:start_index] + except: + pass + keys = ["steps", "scale", "uncond_scale", "cfg_rescale", "seed", "sampler", "sm", "sm_dyn"] + for key in keys: + # 정규 표현식 패턴: key 다음에 나오는 : 공백을 포함한 후 어떤 값이든(숫자, 문자열 등) 매치 + pattern = r'"{}":\s*([^,}}]+)'.format(key) + match = re.search(pattern, text) + + if match: + # 값을 추출 (그룹 1) + value = match.group(1) + + # 숫자이면 숫자로 변환, 그 외의 경우는 그대로 사용 (True, False, 문자열 등) + try: + # 정수, 실수 변환 시도 + if '.' in value: + contents[key] = float(value) + else: + contents[key] = int(value) + except ValueError: + # boolean 변환 시도 + if value.lower() == "true": + contents[key] = True + elif value.lower() == "false": + contents[key] = False + else: + # 문자열 그대로 저장 (쌍따옴표 제거) + contents[key] = value.strip('"') + + my_font = customtkinter.CTkFont('Pretendard', 13) + large_font = customtkinter.CTkFont('Pretendard', 14) + NAI_prompt_label = customtkinter.CTkLabel(import_window_right, width=586, font=my_font, text=" -------------------- 프롬프트 -------------------- ") + NAI_prompt_label.grid(row=0, column=0, padx=5, sticky="nsew") + NAI_prompt = customtkinter.CTkTextbox(import_window_right, width=586, height=205, font=large_font) + NAI_prompt.grid(row=1, column=0, pady=5, padx=5, sticky="nsew") + NAI_negative_label = customtkinter.CTkLabel(import_window_right, width=586, font=my_font, text=" --------------- 네거티브 프롬프트 --------------- ") + NAI_negative_label.grid(row=2, column=0, padx=5, sticky="nsew") + NAI_negative = customtkinter.CTkTextbox(import_window_right, width=586,height=130, font=large_font) + NAI_negative.grid(row=3, column=0, pady=5, padx=5, sticky="nsew") + NAI_prompt.insert("0.0", contents['prompt']) + NAI_prompt.configure(state="disabled") + NAI_negative.insert("0.0", contents['uc']) + NAI_negative.configure(state="disabled") + + import_frame1 = customtkinter.CTkFrame(import_window_right, width=586, fg_color="#2B2B2B") + import_frame1.grid(row=4, column=0, pady=5, padx=5, sticky="nsew") + + i_steps_label = customtkinter.CTkLabel(import_frame1, font=my_font, text="Steps : ") + i_steps_label.grid(row=0, column=0, padx=5, sticky="nsew") + i_steps = customtkinter.CTkEntry(import_frame1, font=my_font, width=35) + i_steps.grid(row=0, column=1, padx=5, sticky="nsew") + i_steps.insert(0, str(contents['steps'])) + i_steps.configure(state="disabled") + + i_scale_label = customtkinter.CTkLabel(import_frame1, font=my_font, text="CFG Scale : ") + i_scale_label.grid(row=0, column=2, padx=5, sticky="nsew") + i_scale = customtkinter.CTkEntry(import_frame1, font=my_font, width=40) + i_scale.grid(row=0, column=3, padx=5, sticky="nsew") + i_scale.insert(0, str(contents['scale'])) + i_scale.configure(state="disabled") + + i_uncond_label = customtkinter.CTkLabel(import_frame1, font=my_font, text="UC Strength : ") + i_uncond_label.grid(row=0, column=4, padx=5, sticky="nsew") + i_uncond = customtkinter.CTkEntry(import_frame1, font=my_font, width=40) + i_uncond.grid(row=0, column=5, padx=5, sticky="nsew") + i_uncond.insert(0, str(contents['uncond_scale'])) + i_uncond.configure(state="disabled") + + i_rescale_label = customtkinter.CTkLabel(import_frame1, font=my_font, text="CFG Rescale : ") + i_rescale_label.grid(row=0, column=6, padx=5, sticky="nsew") + i_rescale = customtkinter.CTkEntry(import_frame1, font=my_font, width=40) + i_rescale.grid(row=0, column=7, padx=5, sticky="nsew") + i_rescale.insert(0, str(contents['cfg_rescale'])) + i_rescale.configure(state="disabled") + + import_frame2 = customtkinter.CTkFrame(import_window_right, width=586, fg_color="#2B2B2B") + import_frame2.grid(row=5, column=0, pady=5, padx=5, sticky="nsew") + + i_seed_label = customtkinter.CTkLabel(import_frame2, font=my_font, text="seed : ") + i_seed_label.grid(row=0, column=0, padx=5, sticky="nsew") + i_seed = customtkinter.CTkEntry(import_frame2, font=my_font, width=100) + i_seed.grid(row=0, column=1, padx=5, sticky="nsew") + i_seed.insert(0, str(contents['seed'])) + + i_sampler_label = customtkinter.CTkLabel(import_frame2, font=my_font, text="sampler : ") + i_sampler_label.grid(row=0, column=2, padx=5, sticky="nsew") + i_sampler = customtkinter.CTkEntry(import_frame2, font=my_font, width=100) + i_sampler.grid(row=0, column=3, padx=5, sticky="nsew") + i_sampler.insert(0, str(contents['sampler'])) + + i_smea = customtkinter.CTkCheckBox(import_frame2, font=my_font, text="SMEA") + i_smea.grid(row=0, column=4, padx=5, sticky="nsew") + if contents['sm'] == True: + i_smea.select() + else: + i_smea.deselect() + i_dyn = customtkinter.CTkCheckBox(import_frame2, font=my_font, text="SMEA+DYN") + i_dyn.grid(row=0, column=5, padx=5, sticky="nsew") + if contents['sm_dyn'] == True: + i_dyn.select() + else: + i_dyn.deselect() + i_smea.configure(state="disabled") + i_dyn.configure(state="disabled") + + import_frame3 = customtkinter.CTkFrame(import_window_right, width=586, fg_color="#2B2B2B") + import_frame3.grid(row=6, column=0, pady=5, padx=5, sticky="nsew") + + i_resolution_label = customtkinter.CTkLabel(import_frame3, font=my_font, text="Resolution : ") + i_resolution_label.grid(row=0, column=0, padx=5, sticky="nsew") + i_resolution = customtkinter.CTkEntry(import_frame3, font=my_font, width=100) + i_resolution.grid(row=0, column=1, padx=5, sticky="nsew") + i_resolution.insert(0, f"{width} x {height}") + + import_frame4 = customtkinter.CTkFrame(import_window_right, width=586, fg_color="#2B2B2B") + import_frame4.grid(row=7, column=0, pady=10, padx=5, sticky="n") + + def image_to_base64(image): + image_bytesIO = io.BytesIO() + image.save(image_bytesIO, format="PNG") + return base64.b64encode(image_bytesIO.getvalue()).decode() + + def save_base64_image(base64_string): + image_data = base64.b64decode(base64_string) + output_file_path = f"output_NAI\\{self.start_time}\\import" + d = Path(output_file_path) + d.mkdir(parents=True, exist_ok=True) + filename = (d / f"{app.import_image_count:05}.png" ) + filename.write_bytes(image_data) + return filename + + def image_to_queue(): + if from_url == True: + base64_string = image_to_base64(img) + filename = save_base64_image(base64_string) + app.ext_set_image_to_queue(new_image.copy(), contents['prompt'], str(contents['seed']), filename) + app.import_image_count += 1 + else: + app.ext_set_image_to_queue(new_image.copy(), contents['prompt'], str(contents['seed']), file_path_or_url) + app.import_image_negative[len(app.image_queue)-1] = contents['uc'] + + import_image_to_queue = customtkinter.CTkButton(import_frame4, font=my_font, text="이미지/프롬프트를 큐에 삽입", command=image_to_queue) + import_image_to_queue.grid(row=0, column=0, padx=5, sticky="nsew") + if text_type == 'str': + import_image_to_queue.configure(state="disabled") + + def prompt_insert(): + app.text_input.delete("0.0", "end") + app.text_input.insert("0.0", contents['prompt']) + app.negative_prompt_input.delete("0.0", "end") + app.negative_prompt_input.insert("0.0", contents['uc']) + app.automation_button.deselect() + + import_prompts = customtkinter.CTkButton(import_frame4, font=my_font, text="프롬프트/네거티브 삽입", fg_color="grey", hover_color="grey10", command=prompt_insert) + import_prompts.grid(row=0, column=1, padx=5, sticky="nsew") + + # ["steps", "scale", "uncond_scale", "cfg_rescale", "seed", "sampler", "sm", "sm_dyn" + def settings_insert(): + app.cfg_scale_var.set(str(contents['scale'])) + app.cfg_scale_entry.delete(0, "end") + app.cfg_scale_entry.insert(0, str(contents['scale'])) + app.uncond_strength_entry.delete(0, "end") + app.uncond_strength_entry.insert(0, str(contents['uncond_scale'])) + app.prompt_guidance_rescale_var.set(str(contents['cfg_rescale'])) + app.prompt_guidance_rescale_entry.delete(0, "end") + app.prompt_guidance_rescale_entry.insert(0, str(contents['cfg_rescale'])) + app.seed_fix_button.select() + app.seed_entry.delete(0, "end") + app.seed_entry.insert(0, str(contents['seed'])) + app.sampler_var.set(contents['sampler']) + app.sampler_button.set(contents['sampler']) + if contents['sm'] == True: + app.sema_button.select() + else: + app.sema_button.deselect() + if contents['sm_dyn'] == True: + app.dyn_button.select() + else: + app.dyn_button.deselect() + app.random_resolution_button.deselect() + app.resolution_button.set(f"{width} x {height}") + app.resolution_var.set(f"{width} x {height}") + app.cond_negative_button.deselect() + app.automation_button.deselect() + + import_settings = customtkinter.CTkButton(import_frame4, font=my_font, text="설정값 일괄 삽입", fg_color="#F5F3C2", hover_color="#A69F40", text_color="black", command=settings_insert) + import_settings.grid(row=0, column=2, padx=5, sticky="nsew") + + + + + import_window.after(1500, lambda: import_window.attributes('-topmost', False)) + + except Exception as e: + self.image_label_report.configure(state="normal", text_color = "#FFFF97") + self.image_label_report.delete("0.0", "end") + self.image_label_report.insert("0.0", f"Error: {e}") + self.image_label_report.configure(state="disabled") + + + + def ext_set_image_to_queue(self, imagen, prompt, seed, filename): + self.image_queue.append([imagen, prompt, seed, filename]) + #self.history_export.configure(text=f"export ({len(self.image_queue)})") + self.request_upper_size_button.configure(state="normal") + self.current_window = self.window + if self.window == len(self.image_queue)-1: + self.window+=1 + self.update() + show_text = str(self.window)+" / "+ str(len(self.image_queue)) + + self.window_label.configure(text=show_text) + + def update(self): + if len(self.image_queue) <= 5: + if len(self.image_queue) >= 1: + self.image_history_0.configure(self.image_history_sub_frame, image=customtkinter.CTkImage(self.image_queue[self.window-1][0], size=(100,100))) + if len(self.image_queue) >= 2: + self.image_history_0.configure(self.image_history_sub_frame, image=customtkinter.CTkImage(self.image_queue[self.window-1][0], size=(100,100))) + self.image_history_1.configure(self.image_history_sub_frame, image=customtkinter.CTkImage(self.image_queue[self.window-2][0], size=(100,100))) + if len(self.image_queue) >= 3: + self.image_history_0.configure(self.image_history_sub_frame, image=customtkinter.CTkImage(self.image_queue[self.window-1][0], size=(100,100))) + self.image_history_1.configure(self.image_history_sub_frame, image=customtkinter.CTkImage(self.image_queue[self.window-2][0], size=(100,100))) + self.image_history_2.configure(self.image_history_sub_frame, image=customtkinter.CTkImage(self.image_queue[self.window-3][0], size=(100,100))) + if len(self.image_queue) >= 4: + self.image_history_0.configure(self.image_history_sub_frame, image=customtkinter.CTkImage(self.image_queue[self.window-1][0], size=(100,100))) + self.image_history_1.configure(self.image_history_sub_frame, image=customtkinter.CTkImage(self.image_queue[self.window-2][0], size=(100,100))) + self.image_history_2.configure(self.image_history_sub_frame, image=customtkinter.CTkImage(self.image_queue[self.window-3][0], size=(100,100))) + self.image_history_3.configure(self.image_history_sub_frame, image=customtkinter.CTkImage(self.image_queue[self.window-4][0], size=(100,100))) + if len(self.image_queue) == 5: + self.image_history_0.configure(self.image_history_sub_frame, image=customtkinter.CTkImage(self.image_queue[self.window-1][0], size=(100,100))) + self.image_history_1.configure(self.image_history_sub_frame, image=customtkinter.CTkImage(self.image_queue[self.window-2][0], size=(100,100))) + self.image_history_2.configure(self.image_history_sub_frame, image=customtkinter.CTkImage(self.image_queue[self.window-3][0], size=(100,100))) + self.image_history_3.configure(self.image_history_sub_frame, image=customtkinter.CTkImage(self.image_queue[self.window-4][0], size=(100,100))) + self.image_history_4.configure(self.image_history_sub_frame, image=customtkinter.CTkImage(self.image_queue[self.window-5][0], size=(100,100))) + else: + self.image_history_0.configure(self.image_history_sub_frame, image=customtkinter.CTkImage(self.image_queue[self.window-1][0], size=(100,100))) + self.image_history_1.configure(self.image_history_sub_frame, image=customtkinter.CTkImage(self.image_queue[self.window-2][0], size=(100,100))) + self.image_history_2.configure(self.image_history_sub_frame, image=customtkinter.CTkImage(self.image_queue[self.window-3][0], size=(100,100))) + self.image_history_3.configure(self.image_history_sub_frame, image=customtkinter.CTkImage(self.image_queue[self.window-4][0], size=(100,100))) + self.image_history_4.configure(self.image_history_sub_frame, image=customtkinter.CTkImage(self.image_queue[self.window-5][0], size=(100,100))) + if len(self.image_queue) == 6: + self.image_history_button_down.configure(state="normal") + + + + def save_settings(self): + settings = { + "NAI_ID": self.NAI_ID if self.NAI_ID else " ", + "access_token": self.access_token if self.access_token else " ", + "searach_keyword": self.search_label_entry.get() if len(self.search_label_entry.get()) > 3 else " ", + "exclude_keyword": self.exclude_label_entry.get() if len(self.exclude_label_entry.get()) > 3 else " ", + "explicit": self.rating_select_explicit.get(), + "nsfw": self.rating_select_nsfw.get(), + "sensitive": self.rating_select_sensitive.get(), + "general": self.rating_select_general.get(), + "rm_artist": self.rm_artist_name_button.get(), + "rm_copyright": self.rm_copyright_name_button.get(), + "rm_character": self.rm_characteristic_button.get(), + "prompt": self.text_input.get("0.0", "end-1c") if len(self.text_input.get("0.0", "end")) > 4 else " ", + "negative": self.negative_prompt_input.get("0.0", "end-1c") if len(self.negative_prompt_input.get("0.0", "end")) > 4 else " ", + "fix": self.fixed_prompt_input.get("0.0", "end-1c") if len(self.fixed_prompt_input.get("0.0", "end")) > 4 else " ", + "after": self.fixed_prompt_after_input.get("0.0", "end-1c") if len(self.fixed_prompt_after_input.get("0.0", "end")) > 4 else " ", + "auto_hide": self.auto_hide_keyword_input.get("0.0", "end-1c") if len(self.auto_hide_keyword_input.get("0.0", "end")) > 4 else " ", + "cfg_scale": str(self.cfg_scale_entry.get()), + "uncond_scale": str(self.uncond_strength_entry.get()), + "guidance": str(self.prompt_guidance_rescale_entry.get()), + "sampler": self.sampler_var.get(), + "random": self.random_resolution_button.get(), + "sema": self.sema_button.get(), + "dyn": self.dyn_button.get(), + "personal_folder":self.output_file_path_personal, + "save_folder":self.output_file_path if self.output_file_path_personal else " ", + "png_rule":self.name_var.get(), + "quality_toggle":self.auto_quality_toggle.get(), + "cond_prompt": self.conditional_prompt_input.get("0.0", "end-1c") if len(self.conditional_prompt_input.get("0.0", "end")) > 4 else " ", + "cond_negative": self.conditional_negative_input.get("0.0", "end-1c") if len(self.conditional_negative_input.get("0.0", "end")) > 4 else " ", + "activate_cond_prompt": self.cond_prompt_button.get(), + "activate_cond_negative": self.cond_negative_button.get(), + "prompt_rating": self.current_prompt_rating, + "resolution": self.resolution_button.get(), + "wildcard_preopen": self.toggle_wildcard_preopen.get(), + "i2i": [self.i2i_value, self.i2i_seed_hold_check, self.i2i_resolution], + "rm_extend": self.rm_extend_var.get() + } + with open('app_settings.json', 'w', encoding='utf-8') as f: + json.dump(settings, f, ensure_ascii=False, indent=4) + if type(self.cached_rows) != type(None) and not self.cached_rows.empty: + self.cached_rows.to_parquet('txt2img_temp_prompt.parquet') + #print("finished") + + def load_settings(self): + if os.path.exists('app_settings.json'): + with open('app_settings.json', 'r', encoding='utf-8') as f: + try: + settings = json.load(f) + try: + self.NAI_ID = settings["NAI_ID"] if len(settings["NAI_ID"]) > 2 else None + except: pass + try: + self.access_token = settings["access_token"] if len(settings["access_token"]) > 2 else None + if self.access_token: + self.NAI_Account_Login.configure(state="disabled") + self.NAI_Token_Remove.configure(state="normal") + self.NAI_Account_State.configure(text="검색 UI 숨기기") + self.image_generation_button.configure(state="normal") + except: pass + try: + self.search_label_entry.insert(0, settings["searach_keyword"]) + except: pass + try: + self.exclude_label_entry.insert(0, settings["exclude_keyword"]) + except: pass + try: + self.rating_select_var_explicit.set(settings["explicit"]) + if settings["explicit"] == 1: self.rating_select_explicit.select() + else: self.rating_select_explicit.deselect() + except: pass + try: + self.rating_select_var_nsfw.set(settings[ "nsfw"]) + if settings["nsfw"] == 1: self.rating_select_nsfw.select() + else: self.rating_select_nsfw.deselect() + except: pass + try: + self.rating_select_var_sensitive.set(settings["sensitive"]) + if settings["sensitive"] == 1: self.rating_select_sensitive.select() + else: self.rating_select_sensitive.deselect() + except: pass + try: + self.rating_select_var_general.set(settings["general"]) + if settings["general"] == 1: self.rating_select_general.select() + else: self.rating_select_general.deselect() + except: pass + try: + self.rm_artist_name_var = settings["rm_artist"] + if settings["rm_artist"] == 1: self.rm_artist_name_button.select() + else: self.rm_artist_name_button.deselect() + except: pass + try: + self.rm_copyright_name_var = settings["rm_copyright"] + if settings["rm_copyright"] == 1: self.rm_copyright_name_button.select() + else: self.rm_copyright_name_button.deselect() + except: pass + try: + self.rm_characteristic_var.set(settings["rm_character"]) + if settings["rm_character"] == 1: self.rm_characteristic_button.select() + else: self.rm_characteristic_button.deselect() + except: pass + try: + self.text_input.insert("0.0", settings["prompt"]) + except: pass + try: + self.negative_prompt_input.delete("0.0", "end") + self.negative_prompt_input.insert("0.0", settings["negative"]) + except: pass + try: + self.fixed_prompt_input.insert("0.0", settings["fix"]) + except: pass + try: + self.fixed_prompt_after_input.insert("0.0", settings["after"]) + except: pass + try: + self.auto_hide_keyword_input.insert("0.0", settings[ "auto_hide"]) + except: pass + try: + self.cfg_scale_entry.delete(0, "end") + self.cfg_scale_entry.insert(0, str(settings["cfg_scale"])) + self.cfg_scale_var.set(settings["cfg_scale"]) + except: pass + try: + self.prompt_guidance_rescale_entry.delete(0, "end") + self.prompt_guidance_rescale_entry.insert(0, str(settings["guidance"])) + self.prompt_guidance_rescale_var.set(settings["guidance"]) + except: pass + try: + self.sampler_var.set(settings["sampler"]) + self.sampler_button.set(settings["sampler"]) + except: pass + try: + self.random_resolution_var.set(settings["random"]) + if settings["random"] == 1: self.random_resolution_button.select() + else: self.random_resolution_button.deselect() + except: pass + try: + self.sema_button_var.set(settings["sema"]) + if settings["sema"] == 1: self.sema_button.select() + else: self.sema_button.deselect() + except: pass + try: + self.dyn_button_var.set(settings["dyn"]) + if settings["dyn"] == 1: self.dyn_button.select() + else: self.dyn_button.deselect() + except: pass + try: + if settings["personal_folder"] == True: + self.output_file_path_personal =True + self.output_file_path = settings["save_folder"] + else: self.output_file_path_personal =False + except: pass + try: + self.name_var.set(settings["png_rule"]) + except: pass + try: + self.auto_quality_toggle_var=settings["quality_toggle"] + if settings["quality_toggle"] == 1: self.auto_quality_toggle.select() + else: self.auto_quality_toggle.deselect() + except: pass + try: + self.conditional_prompt_input.insert("0.0", settings[ "cond_prompt"]) + except: pass + try: + self.conditional_negative_input.insert("0.0", settings[ "cond_negative"]) + except: pass + try: + self.cond_prompt_button_var.set(settings["activate_cond_prompt"]) + if settings["activate_cond_prompt"] == 1: self.cond_prompt_button.select() + else: self.cond_prompt_button.deselect() + except: pass + try: + self.cond_prompt_button_var.set(settings["activate_cond_negative"]) + if settings["activate_cond_negative"] == 1: self.cond_negative_button.select() + else: self.cond_negative_button.deselect() + except: pass + try: + self.current_prompt_rating = settings["prompt_rating"] + except: pass + try: + self.resolution_var.set(settings["resolution"]) + self.resolution_button.set(settings["resolution"]) + except: pass + try: + self.toggle_wildcard_preopen_var.set(settings["wildcard_preopen"]) + if settings["wildcard_preopen"] == 1: self.toggle_wildcard_preopen.select() + else: self.toggle_wildcard_preopen.deselect() + except: pass + try: + self.uncond_strength_entry.delete(0, "end") + self.uncond_strength_entry.insert(0, settings["uncond_scale"]) + except: pass + try: + self.i2i_value, self.i2i_seed_hold_check, self.i2i_resolution = settings["i2i"][0], settings["i2i"][1],settings["i2i"][2] + except: pass + try: + self.rm_extend_var.set(settings["rm_extend"]) + self.rm_extend_button.select() if settings["rm_extend"] == 1 else self.rm_extend_button.deselect() + except: pass + except json.JSONDecodeError as e: + self.image_label_report.configure(state="normal", text_color = "#FFFF97") + self.image_label_report.insert("0.0", "세이브 파일을 불러오는데 실패하였습니다 : " + str(e)) + self.image_label_report.configure(state="disabled") + if os.path.exists('txt2img_temp_prompt.parquet'): + try: + self.cached_rows = pd.read_parquet('txt2img_temp_prompt.parquet') + self.cached_prompt_label.configure(text = "남은 프롬프트 행 : "+str(len(self.cached_rows))) + if 'id' not in self.cached_rows.columns: + self.image_label_report.configure(state="normal", text_color = "#FFFF97") + self.image_label_report.insert("0.0", " 0215 버전부터 tags의 구성이 변경되었습니다. favorite 기능을 사용하기 위해 검색을 다시 수행 해 주세요.") + self.image_label_report.configure(state="disabled") + except: pass + + def exit_program(self): + self.save_settings() + app.destroy() + + def get_max_size(self): + width = self.winfo_screenheight() + return 768 if width < 1440 else 768 + + def update_fullscreen_image(self,new_window, new_image_label): + last_updated_image = None + while True: + time.sleep(1) + if not new_window.winfo_exists(): + break + if self.image_queue: + current_image = Image.open(self.image_queue[self.window-1][3]) + if current_image and current_image != last_updated_image: + #resized_image = self.resize_image_to_fit(current_image, new_window.winfo_screenheight()) + original_width, original_height = current_image.size + ratio = new_window.winfo_screenheight() / float(original_height) + new_width = int(original_width * ratio) + tk_image = customtkinter.CTkImage(current_image, size=(new_width,new_window.winfo_screenheight())) + new_window.after(0, lambda img=tk_image: new_image_label.configure(image=img)) + new_image_label.image = tk_image + last_updated_image = current_image + + def show_fullscreen_image(self): + new_window = customtkinter.CTkToplevel() + new_window.attributes('-fullscreen', True) + new_window.state('zoomed') + new_window.configure(bg='black') + + new_image_label = customtkinter.CTkLabel(new_window, text=" ") + new_image_label.pack(expand=True, fill='both') + new_window.bind("", lambda e: new_window.destroy()) + + update_thread = threading.Thread(target=self.update_fullscreen_image, args=(new_window, new_image_label), daemon=True) + update_thread.daemon = True + update_thread.start() + + def resize_image_to_fit(self, image, target_height): + original_width, original_height = image.size + + ratio = target_height / float(original_height) + new_width = int(original_width * ratio) + + resized_image = image.resize((new_width, target_height), Image.Resampling.LANCZOS) + return resized_image + + def open_prompt_window(self): + prompt_window = customtkinter.CTkToplevel() + prompt_window.title("추천 프롬프트") + prompt_window.attributes('-topmost', True) + prompt_window.resizable(width=False, height=False) + + text_label1 = customtkinter.CTkLabel(prompt_window, text="검색 결과 내 고빈도 키워드", font=customtkinter.CTkFont('Pretendard', 13), width=500) + text_label1.grid(row=0, column=0, padx=5, pady=5, sticky="nsew") + text_output1 = customtkinter.CTkTextbox(prompt_window, height=250, width=350, font=customtkinter.CTkFont('Pretendard', 15)) + text_output1.grid(row=1, column=0, padx=5, pady=5, sticky="nsew") + + text_label2 = customtkinter.CTkLabel(prompt_window, text="검색 결과 내 작가명 순위", font=customtkinter.CTkFont('Pretendard', 13), width=500) + text_label2.grid(row=2, column=0, padx=5, pady=5, sticky="nsew") + text_output2 = customtkinter.CTkTextbox(prompt_window, height=250, width=350, font=customtkinter.CTkFont('Pretendard', 15)) + text_output2.grid(row=3, column=0, padx=5, pady=5, sticky="nsew") + + text_label3 = customtkinter.CTkLabel(prompt_window, text="많이 사용된 캐릭터 순위", font=customtkinter.CTkFont('Pretendard', 13), width=500) + text_label3.grid(row=4, column=0, padx=5, pady=5, sticky="nsew") + text_output3 = customtkinter.CTkTextbox(prompt_window, height=250, width=350, font=customtkinter.CTkFont('Pretendard', 15)) + text_output3.grid(row=5, column=0, padx=5, pady=5, sticky="nsew") + + if type(self.cached_rows) != type(None) and not self.cached_rows.empty: + counts1 = Counter() + ndf = self.cached_rows[self.cached_rows['general'].notnull()] + for row in ndf['general']: + if row != None: + substrings = [substring.strip() for substring in row.split(',') if substring.strip()] + counts1.update(substrings) + top_200_keywords = counts1.most_common(200) + formatted_keywords = [f"{keyword}: {count}" for keyword, count in top_200_keywords] + formatted_text = "\n".join(formatted_keywords) + text_output1.insert("0.0", formatted_text) + del[ndf] + + counts2 = Counter() + ndf = self.cached_rows[self.cached_rows['artist'].notnull()] + for row in ndf['artist']: + if row != None: + substrings = [substring.strip() for substring in row.split(',') if substring.strip()] + counts2.update(substrings) + top_200_keywords = [(keyword, count) for keyword, count in counts2.most_common(200) if keyword in artist_dict] + formatted_keywords = [f"{keyword}: {count}" for keyword, count in top_200_keywords] + formatted_text = "\n".join(formatted_keywords) + text_output2.insert("0.0", formatted_text) + del[ndf] + + counts3 = Counter() + ndf = self.cached_rows[self.cached_rows['character'].notnull()] + for row in ndf['character']: + if row != None: + substrings = [substring.strip() for substring in row.split(',') if substring.strip()] + counts3.update(substrings) + top_200_keywords = counts3.most_common(200) + formatted_keywords = [f"{keyword}: {count}" for keyword, count in top_200_keywords] + formatted_text = "\n".join(formatted_keywords) + text_output3.insert("0.0", formatted_text) + del[ndf] + + + def random_artist_window(self): + rartist_window = customtkinter.CTkToplevel() + rartist_window.title("랜덤 작가명 설정") + rartist_window.attributes('-topmost', True) + rartist_window.resizable(width=False, height=False) + + rartist_window_left = customtkinter.CTkFrame(rartist_window, width=250) + rartist_window_left.grid(row=0, column=0, padx=5, pady=5, sticky="nsew") + #rartist_window_right = customtkinter.CTkFrame(rartist_window, width=250) + #rartist_window_right.grid(row=0, column=1, padx=5, pady=5, sticky="nsew") + text_label1 = customtkinter.CTkLabel(rartist_window_left, text="랜덤 작가 삽입 ��법 선택", font=customtkinter.CTkFont('Pretendard', 13), width=250) + text_label1.grid(row=0, column=0, padx=5, pady=5, sticky="nsew") + self.random_artist_select = customtkinter.StringVar() + ramdom_artist_global = customtkinter.CTkRadioButton(rartist_window_left, text="전체 작가명 리스트에서", variable=self.random_artist_select, value="global", font=customtkinter.CTkFont('Pretendard', 13)) + ramdom_artist_global.grid(row=1, column=0, padx=5, pady=5, sticky="w") + ramdom_artist_local = customtkinter.CTkRadioButton(rartist_window_left, text="검색 프롬프트 안에서", variable=self.random_artist_select, value="local", font=customtkinter.CTkFont('Pretendard', 13)) + ramdom_artist_local.grid(row=2, column=0, padx=5, pady=5, sticky="w") + text_label2 = customtkinter.CTkLabel(rartist_window_left, text="단부루 내 최소 이미지 수 설정(최소: 80)", font=customtkinter.CTkFont('Pretendard', 13), width=250) + text_label2.grid(row=3, column=0, padx=5, pady=5, sticky="nsew") + self.danbooru_minimum = customtkinter.IntVar(value=80) + danbooru_entry = customtkinter.CTkEntry(rartist_window_left, textvariable=self.danbooru_minimum, font=customtkinter.CTkFont('Pretendard', 13), width=50) + danbooru_entry.grid(row=4, column=0, padx=5, pady=5, sticky="n") + danbooru_set = customtkinter.CTkButton(rartist_window_left, text="새로 탑재",font=customtkinter.CTkFont('Pretendard', 13), width=50, command=self.get_artist) + danbooru_set.grid(row=5, column=0, padx=5, pady=5, sticky="n") + self.random_artist_list_length = customtkinter.CTkLabel(rartist_window_left, text="total length : 0", font=customtkinter.CTkFont('Pretendard', 13), width=250) + self.random_artist_list_length.grid(row=6, column=0, padx=5, pady=5, sticky="nsew") + text_label3 = customtkinter.CTkLabel(rartist_window_left, text="작가명 삽입 방식", font=customtkinter.CTkFont('Pretendard', 13), width=250) + text_label3.grid(row=7, column=0, padx=5, pady=5, sticky="nsew") + random_artist_radio1 = customtkinter.CTkRadioButton(rartist_window_left, text="앞에 artist: 붙이기", variable=self.random_artist_prefix, value="artist:", font=customtkinter.CTkFont('Pretendard', 13)) + random_artist_radio1.grid(row=8, column=0, padx=5, pady=5, sticky="w") + random_artist_radio2 = customtkinter.CTkRadioButton(rartist_window_left, text="뒤에 (artist) 붙이기", variable=self.random_artist_prefix, value="(artist)", font=customtkinter.CTkFont('Pretendard', 13)) + random_artist_radio2.grid(row=9, column=0, padx=5, pady=5, sticky="w") + random_artist_radio3 = customtkinter.CTkRadioButton(rartist_window_left, text="기본 스타일로", variable=self.random_artist_prefix, value="none", font=customtkinter.CTkFont('Pretendard', 13)) + random_artist_radio3.grid(row=10, column=0, padx=5, pady=5, sticky="w") + self.random_artist_list = customtkinter.CTkTextbox(rartist_window_left, height=350, width=250, font=customtkinter.CTkFont('Pretendard', 15)) + self.random_artist_list.grid(row=11, column=0, padx=5, pady=5, sticky="nsew") + text_label4 = customtkinter.CTkLabel(rartist_window_left, text="고정 프롬프트에서는 로 호출", font=customtkinter.CTkFont('Pretendard', 13), width=250) + text_label4.grid(row=12, column=0, padx=5, pady=5, sticky="nsew") + + #TODO : 정규식 기반의 랜덤작가 인원/브래킷 관리 기능 추가 + #text_label4 = customtkinter.CTkLabel(rartist_window_right, text="랜덤작가 수 가중치", font=customtkinter.CTkFont('Pretendard', 13), width=250) + #text_label4.grid(row=0, column=0, padx=5, pady=5, sticky="nsew") + #text_label5 = customtkinter.CTkLabel(rartist_window_right, text="가중치:int, 작가수(int), ", font=customtkinter.CTkFont('Pretendard', 13), width=250) + #text_label5.grid(row=1, column=0, padx=5, pady=5, sticky="nsew") + + def reactivate_wildcards(self): + self.activate_wildcards() + pretty_string = "" + for key, value in self.wildcard_dict.items(): + pretty_string += f"{key}: {value}\n" + self.wildcard_text.configure(state="normal") + self.wildcard_text.delete('0.0', "end") + self.wildcard_text.insert('0.0', pretty_string) + self.wildcard_text.configure(state="disabled") + + def open_wildcard_folder(self): + if not os.path.exists(self.wildcards_dir): + os.makedirs(self.wildcards_dir) + os.startfile(self.wildcards_dir) + + def open_wildcard_window(self): + wildcard_window = customtkinter.CTkToplevel() + wildcard_window.title("와일드카드 관리") + wildcard_window.attributes('-topmost', True) + wildcard_window.resizable(width=False, height=False) + + wildcard_window_left = customtkinter.CTkFrame(wildcard_window) + wildcard_window_left.grid(row=0, column=0, sticky="nsew") + wildcard_window_right = customtkinter.CTkFrame(wildcard_window) + wildcard_window_right.grid(row=0, column=1, sticky="nsew") + + text_label_frame = customtkinter.CTkFrame(wildcard_window_right, fg_color="#2B2B2B") + text_label_frame.grid(row=0, column=0, padx=5, pady=5, sticky="nsew") + text_label3 = customtkinter.CTkLabel(text_label_frame, text="와일드카드 관리", font=customtkinter.CTkFont('Pretendard', 13), width=300) + text_label3.grid(row=0, column=0, padx=5, pady=5, sticky="nsew") + text_hyperlink = customtkinter.CTkLabel(text_label_frame, text="가이드 열기 (arca.live)", font=customtkinter.CTkFont('Pretendard', 13), width=180, text_color="lightblue", cursor="hand2") + text_hyperlink.grid(row=0, column=1, padx=5, pady=5, sticky="nsew") + text_hyperlink.bind("", lambda e: webbrowser.open_new("https://arca.live/b/aiart/95877361")) + + def wildcard_folder_callback(choice): + if choice != '': + if choice != '(+) 폴더 추가': + value = list(self.wildcard_dict[wildcard_folder_select_var.get()])+['(+) 새 와일드카드'] + wildcard_folder_select_var.set(choice) + wildcard_file_select.configure(values=value) + wildcard_file_select.set('') + else: + value = list(self.wildcard_dict.keys()) + dialog = customtkinter.CTkInputDialog(text="새 와일드카드 폴더명을 입력하세요 (같은 이름 불가):", title="폴더 생성") + input_value = dialog.get_input() + if input_value and input_value not in value: + os.makedirs(self.wildcards_dir+'/'+input_value) + self.reactivate_wildcards() + wildcard_folder_select.configure(values=list(self.wildcard_dict.keys())+['(+) 폴더 추가']) + wildcard_folder_select.set(input_value) + wildcard_file_select.set('') + else: + wildcard_folder_select.set('') + + + def wildcard_select_callback(choice): + if choice != '': + if choice == '(+) 새 와일드카드': + value = list(self.wildcard_dict[wildcard_folder_select_var.get()]) + dialog = customtkinter.CTkInputDialog(text="새 와일드카드의 이름을 입력하세요 (같은 이름 불가):", title="와일드카드 생성") + input_value = dialog.get_input() + if input_value and input_value not in value: + filepath = self.wildcards_dir+'/'+input_value if wildcard_folder_select_var.get() == "none" else self.wildcards_dir+'/'+wildcard_folder_select_var.get()+'/'+input_value + with open(filepath+'.txt', 'w') as file: + pass + self.reactivate_wildcards() + value2 = list(self.wildcard_dict[wildcard_folder_select_var.get()])+['(+) 새 와일드카드'] + wildcard_file_select.configure(values=value2) + wildcard_file_select.set('') + else: + self.wildcard_file_select_var = choice if wildcard_folder_select_var.get() == "none" else wildcard_folder_select_var.get()+'/'+choice + filepath = self.wildcards_dir+'/'+choice if wildcard_folder_select_var.get() == "none" else self.wildcards_dir+'/'+wildcard_folder_select_var.get()+'/'+choice + if os.path.exists(filepath): + with open(filepath, 'r', encoding='utf-8') as file: + lines = file.readlines() + updated_lines = [] + for line in lines: + line = line.strip() + updated_lines.append(line) + wildcard_select_text.configure(state="normal") + wildcard_select_text.delete("0.0","end") + wildcard_select_text.insert("0.0", '\n'.join(updated_lines)) + wildcard_select_text.configure(state="disabled") + self.current_wildcard_path = filepath + + def fix_wildcard(): + if fix_button.cget("text") == "수정": + save_button.configure(state="normal") + wildcard_select_text.configure(state="normal") + fix_button.configure(text="취소") + wildcard_folder_select.configure(state="disabled") + wildcard_file_select.configure(state="disabled") + coy_button.configure(state="disabled") + elif fix_button.cget("text") == "취소": + save_button.configure(state="disabled") + wildcard_select_text.configure(state="disabled") + fix_button.configure(text="수정") + wildcard_folder_select.configure(state="normal") + wildcard_file_select.configure(state="normal") + coy_button.configure(state="normal") + + def save_wildcard(): + text = wildcard_select_text.get("1.0", "end-1c") + with open(self.current_wildcard_path, "w", encoding="utf-8") as file: + file.write(text) + wildcard_select_text.configure(state="disabled") + fix_button.configure(text="수정") + wildcard_folder_select.configure(state="normal") + wildcard_file_select.configure(state="normal") + coy_button.configure(state="normal") + save_button.configure(state="disabled") + + def copy_wildcard(): + text = self.wildcard_file_select_var + text = text.replace(".txt","") + pyperclip.copy('<'+text+'>') + + wildcard_window_select_frame = customtkinter.CTkFrame(wildcard_window_right) + wildcard_window_select_frame.grid(row=1, column=0, sticky="n") + text_label4 = customtkinter.CTkLabel(wildcard_window_select_frame, text="폴더 선택:", font=customtkinter.CTkFont('Pretendard', 13)) + text_label4.grid(row=0, column=0, padx=15, pady=5, sticky="nsew") + text_label5 = customtkinter.CTkLabel(wildcard_window_select_frame, text="파일 선택:", font=customtkinter.CTkFont('Pretendard', 13)) + text_label5.grid(row=0, column=2, padx=15, pady=5, sticky="nsew") + wildcard_folder_select_var = customtkinter.StringVar(value="none") + wildcard_folder_select = customtkinter.CTkComboBox(wildcard_window_select_frame,values=list(self.wildcard_dict.keys())+['(+) 폴더 추가'],command=wildcard_folder_callback, variable=wildcard_folder_select_var, font=customtkinter.CTkFont('Pretendard', 13)) + wildcard_folder_select.grid(row=0, column=1, padx=15, pady=5, sticky="nsew") + self.wildcard_file_select_var = "" + self.current_wildcard_path = "" + wildcard_file_select = customtkinter.CTkComboBox(wildcard_window_select_frame,values=list(self.wildcard_dict[wildcard_folder_select_var.get()])+['(+) 새 와일드카드'],command=wildcard_select_callback, font=customtkinter.CTkFont('Pretendard', 13)) + wildcard_file_select.grid(row=0, column=3, padx=15, pady=5, sticky="nsew") + wildcard_file_select.set("") + text_label6 = customtkinter.CTkLabel(wildcard_window_select_frame, text="폴더 및 파일의 삭제기능은 제공되지 않습니다. 와일드카드 폴더에서 직접 삭제하세요.", font=customtkinter.CTkFont('Pretendard', 13)) + text_label6.grid(row=1, column=0, columnspan=4, padx=5, pady=5, sticky="n") + fix_button = customtkinter.CTkButton(wildcard_window_select_frame, text="수정", font=customtkinter.CTkFont('Pretendard', 13), width=100, hover_color="grey10", command=fix_wildcard) + fix_button.grid(row=2, column=0, padx=15, pady=5, sticky="n") + save_button = customtkinter.CTkButton(wildcard_window_select_frame, text="저장", font=customtkinter.CTkFont('Pretendard', 13), width=100, fg_color="grey", hover_color="grey10", command=save_wildcard, state="disabled") + save_button.grid(row=2, column=1, padx=15, pady=5, sticky="n") + coy_button = customtkinter.CTkButton(wildcard_window_select_frame, text="<와일드카드>를 클립보드에 복사", font=customtkinter.CTkFont('Pretendard', 13), fg_color="#7030A0", hover_color="#481F67", command=copy_wildcard) + coy_button.grid(row=2, column=2, columnspan=2, padx=5, pady=5, sticky="n") + text_label7 = customtkinter.CTkLabel(wildcard_window_right, text=" Enter 입력시 다른 행으로 취급됩니다. 수정 후 꼭 저장 버튼을 누르세요.", font=customtkinter.CTkFont('Pretendard', 13), text_color="#FFFF97") + text_label7.grid(row=3, column=0, columnspan=4, padx=5, pady=5, sticky="n") + wildcard_select_text = customtkinter.CTkTextbox(wildcard_window_right, font=customtkinter.CTkFont('Pretendard', 14), width=490, height=280, state="disabled") + wildcard_select_text.grid(row=2, column=0, columnspan=4, padx=5, pady=5, sticky="nsew") + + #wildcard_window_left = customtkinter.CTkFrame(wildcard_window, width=500) + #wildcard_window_left.grid(row=0, column=0, padx=5, pady=5, sticky="nsew") + text_label1 = customtkinter.CTkLabel(wildcard_window_left, text="와일드카드 정보", font=customtkinter.CTkFont('Pretendard', 13), width=500) + text_label1.grid(row=0, column=0, padx=5, pady=5, sticky="nsew") + self.wildcard_text = customtkinter.CTkTextbox(wildcard_window_left, font=customtkinter.CTkFont('Pretendard', 13), width=500) + self.wildcard_text.grid(row=1, column=0, padx=5, pady=5, sticky="nsew") + text_label2 = customtkinter.CTkLabel(wildcard_window_left, text="txt파일 내 가중치 문법(100: keyword)은 계속 유효합니다. default=100", font=customtkinter.CTkFont('Pretendard', 13), width=500) + text_label2.grid(row=2, column=0, padx=5, pady=5, sticky="nsew") + self.wildcard_button_frame = customtkinter.CTkFrame(wildcard_window_left) + self.wildcard_button_frame.grid(row=3, column=0, padx=5, pady=5, sticky="n") + self.reactive_button = customtkinter.CTkButton(self.wildcard_button_frame, font=customtkinter.CTkFont('Pretendard', 13), text="와일드카드 업데이트", command= self.reactivate_wildcards) + self.reactive_button.grid(row=0, column=0, padx=5, pady=5, sticky="n") + self.open_wildcard_button = customtkinter.CTkButton(self.wildcard_button_frame, font=customtkinter.CTkFont('Pretendard', 13), text="와일드카드 폴더 열기", command= self.open_wildcard_folder) + self.open_wildcard_button.grid(row=0, column=1, padx=15, pady=5, sticky="n") + self.wildcard_text_sample = customtkinter.CTkTextbox(wildcard_window_left, font=customtkinter.CTkFont('Pretendard', 13), width=500, height=100, text_color="#FFFF97") + self.wildcard_text_sample.grid(row=4, column=0, padx=5, pady=5, sticky="nsew") + self.wildcard_text_sample.insert("0.0", "Example : 1girl, >, , , <<__colors__clothes>|<{{clothes}}>>, looking at viewer") + self.wildcard_text_sample.configure(state="disabled") + text_label3 = customtkinter.CTkLabel(wildcard_window_left, text=" 형태 표기는 '|'로 구분되는 인스턴트 와일드카드입니다.", font=customtkinter.CTkFont('Pretendard', 13), width=500) + text_label3.grid(row=5, column=0, padx=5, pady=5, sticky="nsew") + self.reactivate_wildcards() + + def get_artist(self): + self.random_artist_list.delete("0.0", "end") + danbooru_minimum = self.danbooru_minimum.get() + if self.random_artist_select.get() == "local": + counts = Counter() + ndf = self.cached_rows[self.cached_rows['artist'].notnull()] + for row in ndf['artist']: + if row != None: + substrings = [substring.strip() for substring in row.split(',') if substring.strip()] + counts.update(substrings) + top_500_keywords = [(keyword, count) for keyword, count in counts.most_common(500) if keyword in artist_dict] + filtered_keywords = [(keyword, count) for keyword, count in top_500_keywords if artist_dict.get(keyword, 0) > danbooru_minimum] + formatted_keywords = [f"{keyword}: {count}" for keyword, count in filtered_keywords] + keywords_only = [item.split(": ")[0] for item in formatted_keywords] + self.random_artist_list_length.configure(text='total length : ' + str(len(filtered_keywords))) + self.random_artist = keywords_only + try: + with open("wildcards/random_artist.txt", 'w') as file: + for keyword in keywords_only: + if self.random_artist_prefix.get() == "artist:": + file.write("artist:"+ keyword + '\n') + elif self.random_artist_prefix.get() == "(artist)": + file.write(keyword + ' (artist)\n') + else: + file.write(keyword + '\n') + except: pass + formatted_text = "\n".join(formatted_keywords) + self.random_artist_list.insert("0.0", formatted_text) + else: + filtered_keywords = [keyword for keyword, count in artist_dict.items() if count > danbooru_minimum] + formatted_keywords = [f"{keyword}: {count}" for keyword, count in artist_dict.items() if count > danbooru_minimum] + self.random_artist_list_length.configure(text='total length : ' + str(len(filtered_keywords))) + formatted_text = "\n".join(formatted_keywords) + self.random_artist_list.insert("0.0", formatted_text) + self.random_artist = filtered_keywords + try: + with open("wildcards/random_artist.txt", 'w') as file: + for keyword in filtered_keywords: + if self.random_artist_prefix.get() == "artist:": + file.write("artist:"+ keyword + '\n') + elif self.random_artist_prefix.get() == "(artist)": + file.write(keyword + ' (artist)\n') + else: + file.write(keyword + '\n') + except: pass + self.random_artist_button.configure(state="normal") + + def activate_wildcards(self): + wildcards_dir = os.path.join(os.getcwd(), 'wildcards') + + # wildcards 폴더가 있는지 확인하고, 없으면 생성 + if not os.path.exists(wildcards_dir): + os.makedirs(wildcards_dir) + + # wildcard_dict 딕셔너리 초기화 + wildcard_dict = {'none': []} + + # wildcards 폴더 내의 파일 및 폴더명 획득 + for item in os.listdir(wildcards_dir): + item_path = os.path.join(wildcards_dir, item) + # 파일인 경우 + if os.path.isfile(item_path) and item.endswith('.txt'): + wildcard_dict['none'].append(item) + # 폴더인 경우 + elif os.path.isdir(item_path): + wildcard_dict[item] = [] + # 해당 폴더 내의 파일들을 탐색 + for subitem in os.listdir(item_path): + subitem_path = os.path.join(item_path, subitem) + if os.path.isfile(subitem_path) and subitem.endswith('.txt'): + wildcard_dict[item].append(subitem) + self.wildcard_dict = wildcard_dict + self.wildcards_dir = wildcards_dir + + def get_wildcard(self, input_str): + def read_file_with_fallback(file_path, encodings=['utf-8', 'cp949']): + for encoding in encodings: + try: + with open(file_path, 'r', encoding=encoding, errors='ignore') as file: + return file.readlines() + except UnicodeDecodeError: + continue + return None + + bracket_count = input_str.count('[') + brace_count = input_str.count('{') + + modified_input_str = input_str.replace('[', '').replace(']', '').replace('{', '').replace('}', '') + + file_path = "" + if '/' in modified_input_str: + folder, filename = modified_input_str.split('/', 1) + file_path = os.path.join(self.wildcards_dir, folder, filename + '.txt') + valid_file = folder in self.wildcard_dict and filename + ".txt" in self.wildcard_dict[folder] and os.path.exists(file_path) + else: + file_path = os.path.join(self.wildcards_dir, modified_input_str + ".txt") + valid_file = modified_input_str + ".txt" in self.wildcard_dict['none'] and os.path.exists(file_path) + + if valid_file: + lines = read_file_with_fallback(file_path) + + if lines: + choice_dic = {} + for line in lines: + match = re.match(r'(\d*\.?\d+):(.+)', line) + if match: + value, keyword = float(match.group(1)), match.group(2).strip() + else: + value, keyword = 100, line.strip() + choice_dic[keyword] = value + + keywords = list(choice_dic.keys()) + weights = list(choice_dic.values()) + result = random.choices(keywords, weights=weights, k=1)[0].strip() + else: + result = modified_input_str + + if brace_count > 0: + return '{' * brace_count + result + '}' * brace_count + elif bracket_count > 0: + return '[' * bracket_count + result + ']' * bracket_count + else: + return result + + #Code from https://github.com/DCP-arca/NAI-Auto-Generator/blob/main/nai_generator.py + #Author : DCP-arca + def get_anlas(self): + try: + response = requests.get("https://api.novelai.net/user/subscription", headers={ + "Authorization": f"Bearer {self.access_token}"}) + data_dict = json.loads(response.content) + trainingStepsLeft = data_dict['trainingStepsLeft'] + anlas = int(trainingStepsLeft['fixedTrainingStepsLeft']) + \ + int(trainingStepsLeft['purchasedTrainingSteps']) + app.my_anlas.set(anlas) + self.anlas_label.configure(text="Anlas : "+str(app.my_anlas.get()), text_color="#FFFF97") + except Exception as e: + print(e) + + return None + + def open_instant_event(self): + my_font = customtkinter.CTkFont('Pretendard', 13) + instant_event_window = customtkinter.CTkToplevel() + instant_event_window.title("인스턴트 이벤트 (작업 도중 종료하지 마세요)") + instant_event_window.attributes('-topmost', True) + instant_event_window.resizable(width=False, height=False) + tabview = customtkinter.CTkTabview(instant_event_window) + tabview.grid(row=0, column=0, padx=5, pady=5) + generation_frame = customtkinter.CTkFrame(instant_event_window, height=300) + generation_frame.grid(row=0, column=1, padx=5, pady=5) + + generation_label = customtkinter.CTkLabel(generation_frame, text="0 / 0", font=my_font) + generation_label.grid(row=0, column=0, padx=5, pady=5, sticky="n") + self.instant_stop_bit = False + + def stop_gen(): + self.instant_stop_bit = True + generation_stop_button.configure(state = "disabled") + xyz_execute_button.configure(state="normal") + + def close_window(): + stop_gen() + instant_event_window.destroy() + + generation_stop_button = customtkinter.CTkButton(generation_frame, text="Stop", font=my_font, state="disabled", command=stop_gen) + generation_stop_button.grid(row=1, column=0, padx=5, pady=25, sticky="n") + instant_event_window.protocol("WM_DELETE_WINDOW", close_window) + + #Tab 관리 + tabview.add("X/Y plot") + + def xyz_excute(): + comboboxes = [xyz_box1, xyz_box2] + prompts = [xyz_prompt1, xyz_prompt2] + xyz_list = [] + prompt_list = [] + prompt_len = 0 + for i, combobox in enumerate(comboboxes): + value = combobox.get() + #None + if value == "None": + continue + #CFG Scale + elif value == "CFG Scale": + xyz_list.append("CFG Scale") + input_string = prompts[i].get("0.0", "end-1c") + split_strings = [s.strip() for s in input_string.split(',')] + float_values = [float(s) for s in split_strings] + cfg_scale_list = [] + while float_values[0] <= float_values[1]: + cfg_scale_list.append(float_values[0]) + float_values[0] += float_values[2] + cfg_scale_list = [round(v, 1) for v in cfg_scale_list] + #PG.Rescale + elif value == "PG.Rescale": + xyz_list.append("PG.Rescale") + input_string = prompts[i].get("0.0", "end-1c") + split_strings = [s.strip() for s in input_string.split(',')] + float_values = [float(s) for s in split_strings] + pg_rescale_list = [] + while float_values[0] <= float_values[1]: + pg_rescale_list.append(float_values[0]) + float_values[0] += float_values[2] + pg_rescale_list = [round(v, 1) for v in pg_rescale_list] + #프롬프트 강조 + elif value == "프롬프트 강조": + xyz_list.append("프롬프트 강조") + input_string = prompts[i].get("0.0", "end-1c") + split_strings = [s.strip() for s in input_string.split(',')] + split_strings[0] = str(split_strings[0]) + split_strings[1] = int(split_strings[1]) + split_strings[2] = int(split_strings[2]) + split_strings[3] = int(split_strings[3]) + keyword = split_strings[0] + ### 와일드카드 처리 ### + if prompt_len == 0: + before_wildcard = self.text_input.get("0.0", "end-1c") + before_wildcard = [item.strip() for item in before_wildcard.split(',')] + if '<' in self.text_input.get("0.0", "end-1c"): + #### 단계 1 : 인스턴트 와일드카드 처리 ### + for i, keyword in enumerate(before_wildcard): + if keyword.startswith('<') and keyword.endswith('>'): + vbar_check = keyword[1:-1] + if '|' in vbar_check: + choices = vbar_check.split('|') # '|'를 기준으로 split + choice_dic = {} + for choice in choices: + match = re.match(r'(\d*\.?\d+):(.+)', choice) + if match: + value, keyword = float(match.group(1)), match.group(2).strip() + else: + value, keyword = 1, choice.strip() + choice_dic[keyword] = value + keywords = list(choice_dic.keys()) + weights = list(choice_dic.values()) + selected_instant_wildcard = random.choices(keywords, weights=weights, k=1)[0] + before_wildcard[i] = selected_instant_wildcard + #### 단계 2 : 글로벌 와일드카드 처리 ### + for i, keyword in enumerate(before_wildcard): + if "<" in keyword: + input_str = keyword.strip('<>').strip() + if("__" in input_str): + adjectives = re.findall(r'__(.*?)__', input_str) + last_keyword = re.split(r'__.*?__', input_str)[-1] + adjective_string = "" + for adjective in adjectives: + adjective_string += (self.get_wildcard(adjective) + " ") + before_wildcard[i] = adjective_string + self.get_wildcard(last_keyword) + else: + before_wildcard[i] = self.get_wildcard(input_str) + keyword_index = before_wildcard.index(keyword) + start = split_strings[1] + end = split_strings[2] + step = split_strings[3] + prompt_list.append([]) + first_prompt = [] + for i in range(start, end + 1, step): + if i < 0: + before_wildcard[keyword_index] = '[' * abs(i) + keyword + ']' * abs(i) + first_prompt.append('[' * abs(i) + keyword + ']' * abs(i)) + elif i == 0: + before_wildcard[keyword_index] = keyword + first_prompt.append(keyword) + else: + before_wildcard[keyword_index] = '{' * i + keyword + '}' * i + first_prompt.append('{' * i + keyword + '}' * i) + prompt_list[prompt_len].append(before_wildcard.copy()) + prompt_len+=1 + else: + start = split_strings[1] + end = split_strings[2] + step = split_strings[3] + second_prompt = [] + for i in range(start, end + 1, step): + if i < 0: + second_prompt.append('[' * abs(i) + keyword + ']' * abs(i)) + elif i == 0: + second_prompt.append(keyword) + else: + second_prompt.append('{' * i + keyword + '}' * i) + second_prompt_keyword = keyword + #프롬프트 스왑 + elif value == "프롬프트 스왑": + xyz_list.append("프롬프트 스왑") + input_string = prompts[i].get("0.0", "end-1c") + split_strings = [s.strip() for s in input_string.split(',')] + keyword = split_strings[0] + ### 와일드카드 처리 ### + if prompt_len == 0: + before_wildcard = self.text_input.get("0.0", "end-1c") + before_wildcard = [item.strip() for item in before_wildcard.split(',')] + if '<' in self.text_input.get("0.0", "end-1c"): + #### 단계 1 : 인스턴트 와일드카드 처리 ### + for i, keyword in enumerate(before_wildcard): + if keyword.startswith('<') and keyword.endswith('>'): + vbar_check = keyword[1:-1] + if '|' in vbar_check: + choices = vbar_check.split('|') # '|'를 기준으로 split + choice_dic = {} + for choice in choices: + match = re.match(r'(\d*\.?\d+):(.+)', choice) + if match: + value, keyword = float(match.group(1)), match.group(2).strip() + else: + value, keyword = 1, choice.strip() + choice_dic[keyword] = value + keywords = list(choice_dic.keys()) + weights = list(choice_dic.values()) + selected_instant_wildcard = random.choices(keywords, weights=weights, k=1)[0] + before_wildcard[i] = selected_instant_wildcard + #### 단계 2 : 글로벌 와일드카드 처리 ### + for i, keyword in enumerate(before_wildcard): + if "<" in keyword: + input_str = keyword.strip('<>').strip() + if("__" in input_str): + adjectives = re.findall(r'__(.*?)__', input_str) + last_keyword = re.split(r'__.*?__', input_str)[-1] + adjective_string = "" + for adjective in adjectives: + adjective_string += (self.get_wildcard(adjective) + " ") + before_wildcard[i] = adjective_string + self.get_wildcard(last_keyword) + else: + before_wildcard[i] = self.get_wildcard(input_str) + keyword_index = before_wildcard.index(keyword) + prompt_list.append([]) + first_prompt = [] + for i in split_strings: + before_wildcard[keyword_index] = i + first_prompt.append(i) + prompt_list[prompt_len].append(before_wildcard.copy()) + prompt_len+=1 + else: + second_prompt = [] + for i in split_strings: + second_prompt.append(i) + second_prompt_keyword = keyword + elif value == "SMEA 0/1/dyn": + xyz_list.append("SMEA") + smea_list = [(False, False), (True, False), (True, True)] + elif value == "Samplers": + xyz_list.append("Samplers") + sampler_list = ["k_euler", "k_euler_ancestral", "k_dpmpp_2s_ancestral", "k_dpmpp_sde", "k_dpmpp_2m"] + + #gen_request 생성 + NAI_width, NAI_height = self.resolution_button.get().split('x') + NAI_width = NAI_width.strip() + NAI_height = NAI_height.strip() + scale_pre = self.cfg_scale_entry.get() + try: + scale_pre = float(scale_pre) + except: + scale_pre = 5.0 + app.cfg_scale_var.set("5.0") + rescale_pre = app.prompt_guidance_rescale_entry.get() + try: + rescale_pre = float(rescale_pre) + except: + rescale_pre = 0 + app.prompt_guidance_rescale_var.set("0") + uncond_pre = self.uncond_strength_entry.get() + try: + uncond_pre = float(uncond_pre) + uncond_pre = round(uncond_pre / 0.05) * 0.05 + if (uncond_pre > 1.5): + uncond_pre = 1.5 + self.uncond_strength_entry.delete(0, "end") + self.uncond_strength_entry.insert(0, "1.5") + except: + uncond_pre = 1.0 + self.uncond_strength_entry.delete(0, "end") + self.uncond_strength_entry.insert(0, "1.0") + _gen_request = { + "width":NAI_width, + "height":NAI_height, + "quality_toggle":self.auto_quality_toggle.get(), + "seed":self.seed_entry.get(), + "sampler":self.sampler_button.get(), + "scale":scale_pre, + "uncond_scale":uncond_pre, + "sema":self.sema_button.get(), + "sema_dyn": self.dyn_button.get(), + "cfg_rescale": rescale_pre, + "prompt": self.text_input.get("0.0", "end-1c"), + "negative":self.negative_prompt_input.get("0.0", "end-1c"), + "user_screen_size": self.get_max_size(), + "start_time": self.start_time, + "access_token": self.access_token, + "save_folder": self.output_file_path, + "png_rule": self.name_var.get(), + "type": "normal", + "rating":self.current_prompt_rating + } + x = [] + y = [] + z = [] + xyzs = [x, y, z] + prompt_len = 0 + ybar = None + for i, xyz in enumerate(xyz_list): + if i == 0: + if xyz_list[i] == "CFG Scale": + for cfg in cfg_scale_list: + gen_request = _gen_request.copy() + gen_request["scale"] = cfg + xyzs[i].append(gen_request.copy()) + xbar = [str("CFG: {:.1f}".format(x)) for x in cfg_scale_list] + elif xyz_list[i] == "PG.Rescale": + for pgr in pg_rescale_list: + gen_request = _gen_request.copy() + gen_request["cfg_rescale"] = pgr + xyzs[i].append(gen_request.copy()) + xbar = [str("PGR: {:.1f}".format(x)) for x in pg_rescale_list] + elif xyz_list[i] == "프롬프트 강조": + for prompt in prompt_list[prompt_len]: + gen_request = _gen_request.copy() + gen_request["prompt"] = ', '.join(prompt) + xyzs[i].append(gen_request.copy()) + xbar = first_prompt + prompt_len+=1 + elif xyz_list[i] == "프롬프트 스왑": + for prompt in prompt_list[prompt_len]: + gen_request = _gen_request.copy() + gen_request["prompt"] = ', '.join(prompt) + xyzs[i].append(gen_request.copy()) + xbar = first_prompt + prompt_len+=1 + elif xyz_list[i] == "SMEA": + for tp in smea_list: + gen_request = _gen_request.copy() + gen_request["sema"], gen_request["sema_dyn"] = tp + xyzs[i].append(gen_request.copy()) + xbar = ['None', "SMEA", "SMEA+DYN"] + elif xyz_list[i] == "Samplers": + for sam in sampler_list: + gen_request = _gen_request.copy() + gen_request["sampler"] = sam + xyzs[i].append(gen_request.copy()) + xbar = ["k_euler", "k_euler_ancestral", "k_dpmpp_2s_ancestral", "k_dpmpp_sde", "k_dpmpp_2m"] + elif i == 1: + if xyz_list[i] == "CFG Scale": + for cfg in cfg_scale_list: + _x = copy.deepcopy(x) + for value in _x: + value["scale"] = cfg + y.append(_x) + ybar = [str("CFG: {:.1f}".format(x)) for x in cfg_scale_list] + elif xyz_list[i] == "PG.Rescale": + for pgr in pg_rescale_list: + _x = copy.deepcopy(x) + for value in _x: + value["cfg_rescale"] = pgr + y.append(_x) + ybar = [str("PGR: {:.1f}".format(x)) for x in pg_rescale_list] + elif xyz_list[i] == "프롬프트 강조": + if prompt_len == 0: + for prompt in prompt_list[prompt_len]: + _x = copy.deepcopy(x) + for value in _x: + value["prompt"] = prompt + y.append(_x) + ybar = prompt_list[prompt_len] + else: + for keyword in second_prompt: + _x = copy.deepcopy(x) + for value in _x: + prompt = value["prompt"] + prompt = [item.strip() for item in prompt.split(',')] + idx = prompt.index(second_prompt_keyword) + prompt[idx] = keyword + value["prompt"] = ', '.join(prompt) + y.append(_x) + ybar = second_prompt + elif xyz_list[i] == "프롬프트 스왑": + if prompt_len == 0: + for prompt in prompt_list[prompt_len]: + _x = copy.deepcopy(x) + for value in _x: + value["prompt"] = prompt + y.append(_x) + ybar = prompt_list[prompt_len] + else: + for keyword in second_prompt: + _x = copy.deepcopy(x) + for value in _x: + prompt = value["prompt"] + prompt = [item.strip() for item in prompt.split(',')] + idx = prompt.index(second_prompt_keyword) + prompt[idx] = keyword + value["prompt"] = ', '.join(prompt) + y.append(_x) + ybar = second_prompt + elif xyz_list[i] == "SMEA": + for tp in smea_list: + _x = copy.deepcopy(x) + for value in _x: + value["sema"], value["sema_dyn"] = tp + y.append(_x) + ybar = ['None', "SMEA", "SMEA+DYN"] + elif xyz_list[i] == "Samplers": + for sam in sampler_list: + _x = copy.deepcopy(x) + for value in _x: + value["sampler"] = sam + y.append(_x) + ybar = ["k_euler", "k_euler_ancestral", "k_dpmpp_2s_ancestral", "k_dpmpp_sde", "k_dpmpp_2m"] + xyz_list = [] + prompt_list = [] + prompt_len = 0 + + def run_generation(xy, xbar, ybar=None): + if isinstance(xy[0], list): + ydim = len(xy) + xdim = len(xy[0]) + else: + ydim = 1 + xdim = len(xy) + + max_count = str(ydim * xdim) + generation_label.configure(text = "0" +' / '+ max_count) + if not ybar: ybar = " " + + label_font_size = 40 + label_space = 500 + image_width = int(NAI_width) + image_height = int(NAI_height) + canvas_width = label_space + image_width * xdim + canvas_height = image_height * ydim + label_space + final_image = Image.new("RGB", (canvas_width, canvas_height), "white") + font = ImageFont.truetype("arial.ttf", label_font_size) + draw = ImageDraw.Draw(final_image) + draw.text( + (label_space +((image_width * xdim) / 2), label_space // 3), + f"seed : {self.seed_entry.get()}", + fill="black", + font=font, + anchor="mm" + ) + + xyz_execute_button.configure(state = "disabled") + generation_stop_button.configure(state = "normal") + pass_counter = 0 + + for yloop in range(ydim): + if self.instant_stop_bit: + break + for xloop in range(xdim): + if ydim > 1: + gen_request = xy[yloop][xloop] + else: + gen_request = xy[xloop] + app.image_generation_button.configure(border_width = 2) + generation_pass = False + while_counter = 0 + while (not self.instant_stop_bit and not generation_pass): + self.state_label.configure(text ="state : NAI 이미지 생성 대기중 ... ", text_color = "#FFFF97") + if while_counter >= 15: + self.instant_stop_bit = True + if gen_request["png_rule"] == "count": + app.generation_count += 1 + gen_request["count"] =app.generation_count + result_image, result_prompt, result_seed, info, filename = NAIA_generation.generate(gen_request) + self.state_label.configure(text ="state : idle", text_color = "#DCE4EE") + app.image_generation_button.configure(border_width = 0) + if info: + temp = info.get('Comment', '') + temp = temp[temp.find("prompt")+10:temp.find("skip_cfg_below_sigma")-3].replace('"','') + generation_pass = True + pass_counter += 1 + else: + temp = result_prompt + while_counter += 1 + generation_pass = False + self.running_flag = False + generation_label.configure(text = str(pass_counter) +' / '+ max_count) + self.image_label_report.configure(state="normal") + self.image_label_report.delete("0.0", "end") + self.image_label_report.configure(text_color="#DCE4EE") + self.image_label_report.insert("0.0", temp) + self.image_label_report.configure(state="disabled") + if result_image: + if app.state() != 'zoomed': + instant_result_image = customtkinter.CTkImage(result_image, size=(620,620)) + else: + current_image = Image.open(filename) + original_width, original_height = current_image.size + max_size = app.winfo_screenheight()-100 + if original_width > max_size or original_height > max_size: + new_image = Image.new("RGB", (max_size, max_size), "black") + new_image.paste(current_image, ((max_size - original_width) // 2, (max_size - original_height) // 2)) + instant_result_image = customtkinter.CTkImage(new_image, size=(max_size, max_size)) + else: + instant_result_image = customtkinter.CTkImage(current_image, size=(max_size, max_size)) + self.image_label.configure(image=instant_result_image) + app.ext_set_image_to_queue(result_image, result_prompt, str(result_seed), filename) + x_position = label_space + (xloop * image_width) + y_position = label_space + (yloop * image_height) + final_image.paste(Image.open(filename), (x_position, y_position)) + draw = ImageDraw.Draw(final_image) + if yloop == 0: # x축 레이블은 한 번만 추가 + draw.text( + (x_position + image_width / 2, label_space / 2), + xbar[xloop], + fill="black", + font=font, + anchor="mm" + ) + if xloop == 0: # y축 레이블은 매 행마다 추가 + draw.text( + (label_space // 2, y_position + image_height // 2), + ybar[yloop], + fill="black", + font=font, + anchor="mm" + ) + if self.instant_stop_bit: + break + time.sleep(2) + image_path = f"xy_plot_{self.start_time}_{self.xy_plot_count}.jpg" + final_image.save(image_path) + os.startfile(image_path) + self.xy_plot_count += 1 + generation_stop_button.configure(state = "disabled") + xyz_execute_button.configure(state="normal") + self.instant_stop_bit = False + pass_counter = 0 + + + if y: + generation_thread = threading.Thread(target=run_generation, args=(y, xbar, ybar), daemon=True) + generation_thread.start() + else: + generation_thread = threading.Thread(target=run_generation, args=(x, xbar), daemon=True) + generation_thread.start() + + + + + + + + + + + + + #XYZ plot + xyz_frame = customtkinter.CTkFrame(tabview.tab("X/Y plot")) + xyz_frame.grid(row=1, column=0, padx=5, pady=5) + xyz_label1 = customtkinter.CTkLabel(xyz_frame, text="X value : ", font=my_font) + xyz_label1.grid(row=0, column=0, padx=5, pady=5) + xyz_label2 = customtkinter.CTkLabel(xyz_frame, text="Y value : ", font=my_font) + xyz_label2.grid(row=1, column=0, padx=5, pady=5) + xyz_label3 = customtkinter.CTkLabel(xyz_frame, text="Z value : ", font=my_font) + #xyz_label3.grid(row=2, column=0, padx=5, pady=5) + xyz_values = ['None','CFG Scale', '프롬프트 강조', 'PG.Rescale', 'SMEA 0/1/dyn', 'Samplers', '프롬프트 스왑'] + xyz_box1 = customtkinter.CTkComboBox(xyz_frame, values=xyz_values, font=my_font) + xyz_box2 = customtkinter.CTkComboBox(xyz_frame, values=xyz_values, font=my_font) + xyz_box3 = customtkinter.CTkComboBox(xyz_frame, values=xyz_values, font=my_font, state="disabled") + xyz_box1.grid(row=0, column=1, padx=5, pady=5) + xyz_box2.grid(row=1, column=1, padx=5, pady=5) + #xyz_box3.grid(row=2, column=1, padx=5, pady=5) + xyz_prompt1 = customtkinter.CTkTextbox(xyz_frame, width= 300, height=80, font=my_font) + xyz_prompt2 = customtkinter.CTkTextbox(xyz_frame, width= 300, height=80, font=my_font) + xyz_prompt3 = customtkinter.CTkTextbox(xyz_frame, width= 300, height=80, font=my_font, state="disabled") + xyz_prompt1.grid(row=0, column=2, padx=5, pady=5) + xyz_prompt2.grid(row=1, column=2, padx=5, pady=5) + #xyz_prompt3.grid(row=2, column=2, padx=5, pady=5) + xyz_execute_button = customtkinter.CTkButton(xyz_frame, text="작업 시작", font=my_font, command=xyz_excute) + xyz_execute_button.grid(row=3, columnspan=3, padx=5, pady=5, sticky="n") + + ''' + self.resolution_button2 = customtkinter.CTkComboBox(self.hidden_frame, width=160,values=["1024 x 1024", "960 x 1088", "896 x 1152", "832 x 1216", "1088 x 960", "1152 x 896", "1216 x 832"], variable=self.resolution_var, font=my_font) + self.resolution_button2.grid(row=3, column=1, padx=5, sticky="w")' + ''' + + + + +if __name__ == "__main__": + customtkinter.set_appearance_mode("dark") + + # ----- i2i inpaint를 위한 global 변수 ----- + iimg = None + img2img_window = None + inpaint_mode = False + # ----- 여기까지 global 변수 ----- + + app = App() + app.load_settings() + app.protocol("WM_DELETE_WINDOW", app.exit_program) + app.activate_wildcards() + app.get_anlas() + app.after(1000, lambda: setattr(app, 'last_window_size', (app.winfo_width(), app.winfo_height()))) + app.mainloop()