import tkinter as tk import tkinter.ttk as ttk from tkinter import filedialog from tkinter import Canvas, Scrollbar, Frame, Button from PIL import Image, ImageTk import os import csv import random import time from collections import Counter import re import ast import pyautogui import threading from pynput import keyboard from base64 import urlsafe_b64encode from hashlib import blake2b import argon2 import requests import json from os import environ as env import zipfile import io from pathlib import Path from datetime import datetime import numpy as np from PIL import Image, ImageOps, ImageDraw, ImageFont,ImageTk from IPython.display import display import win32clipboard from io import BytesIO from datetime import datetime import arti_list, tagbag, wlist, copyright_list_reformatted, remove_result_e, remove_result_qe import character_dictionary as cd import pandas as pd import webbrowser from collections import OrderedDict import platform import io from openpyxl import Workbook from openpyxl.drawing.image import Image as OpenpyxlImage BASE_URL="https://api.novelai.net" copyright_keys = copyright_list_reformatted.copyright_list 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()) qe_word = nsfw_word + questionable_word def argon_hash(email: str, password: str, size: int, domain: str) -> str: pre_salt = f"{password[:6]}{email}{domain}" # salt blake = blake2b(digest_size=16) blake.update(pre_salt.encode()) salt = blake.digest() raw = argon2.low_level.hash_secret_raw( password.encode(), salt, 2, int(2000000 / 1024), 1, size, argon2.low_level.Type.ID, ) hashed = urlsafe_b64encode(raw).decode() return hashed def login(key) -> str: response = requests.post(f"{BASE_URL}/user/login", json={ "key": key }) # catch any errors return response.json()["accessToken"] def get_access_key(email: str, password: str) -> str: return argon_hash(email, password, 64, "novelai_data_access_key")[:64] def get_max_size(): global window width = window.winfo_screenheight() return 768 if width < 1440 else 1024 def generate_image(access_token, prompt, model, action, parameters): data = { "input": prompt, "model": model, "action": action, "parameters": parameters, } response = requests.post(f"{BASE_URL}/ai/generate-image", json=data, headers={ "Authorization": f"Bearer {access_token}" }) # catch any errors return response.content def history_queue(image, prompt, seed): global history history.append([image, prompt, seed]) def save_to_excel(): workbook = Workbook() sheet = workbook.active sheet['A1'] = 'Prompt' sheet['B1'] = 'Seed' sheet['C1'] = 'Image' image_objects = [] # BytesIO 객체를 추적하기 위한 리스트 # history 데이터를 반복하며 Excel에 저장 for index, (pil_image, prompt, seed) in enumerate(history, start=1): row_num = index + 1 # 행 번호 (Excel 행은 1부터 시작) # 프롬프트와 시드 삽입 sheet.cell(row=row_num, column=1, value=prompt) sheet.cell(row=row_num, column=2, value=seed) # PIL 이미지를 BytesIO 객체에 PNG 형식으로 저장 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("history_with_images.xlsx") # 모든 BytesIO 객체 닫기 for img_obj in image_objects: img_obj.close() print("엑셀 파일에 저장됨") def show_history(): global history # 새로운 toplevel 창 생성 history_window = tk.Toplevel(window) history_window.title("History") # 창의 크기 history_window.geometry("1920x1080") # Scrollable Canvas 설정 canvas = Canvas(history_window) scrollbar = Scrollbar(history_window, orient="vertical", command=canvas.yview) scrollable_frame = Frame(canvas) # 스크롤 이벤트 바인딩 def _on_mousewheel(event): canvas.yview_scroll(int(-1*(event.delta/120)), "units") if platform.system() == "Windows": history_window.bind_all("", _on_mousewheel) else: history_window.bind_all("", _on_mousewheel) history_window.bind_all("", _on_mousewheel) def on_frame_configure(event): canvas.configure(scrollregion=canvas.bbox("all")) scrollable_frame.bind("", on_frame_configure) canvas.create_window((0, 0), window=scrollable_frame, anchor="nw") canvas.configure(yscrollcommand=scrollbar.set) # history_window에서 썸네일과 텍스트를 표시할 grid 설정 for index, (image, prompt, seed) in enumerate(history): # 이미지를 썸네일로 변환 image.thumbnail((192, 192)) photo = ImageTk.PhotoImage(image) # 썸네일 이미지 라벨 생성 image_label = tk.Label(scrollable_frame, image=photo) image_label.image = photo # 참조를 유지 image_label.grid(row=index, column=0) # prompt와 seed를 결합한 텍스트 생성 text_prompt = prompt + ' Seed: ' + str(seed) text_widget = tk.Text(scrollable_frame, height=10, width=75) text_widget.insert('1.0', text_prompt) text_widget.grid(row=index, column=1) text_widget.config(state='disabled') # 텍스트 위젯을 읽기 전용으로 설정 save_button = Button(history_window, text="Save to Excel", command=save_to_excel) save_button.pack() # Canvas와 Scrollbar를 pack하여 UI에 추가 canvas.pack(side="left", fill="both", expand=True) scrollbar.pack(side="right", fill="y") def generate(width, height, positive, negative, button): global temp_clipboard_image global current_sampler global NAI_width, NAI_height global running_flag global last_generation_seed try: scale = float(entry_CFG_value.get()) except: scale = 5.0 if scale > 7.0: scale = 7.0 elif scale < 4.0: scale = 4.0 else: scale = round(scale, 1) try: scale2 = float(entry_rescale_value.get()) except: scale2 = 0 if scale2 > 1.00: scale = 1.00 elif scale2 < 0.0: scale = 0.00 else: scale2 = round(scale2, 2) params = { "legacy": False, "quality_toggle": False, "width": width, "height": height, "n_samples": 1, "seed": random.randint(0,9999999999), "extra_noise_seed": random.randint(0,9999999999), "sampler": current_sampler, "steps": 28, "scale": scale, "uncond_scale": 1.0, "negative_prompt": negative_text.get("1.0", "end-1c").strip(), "sm" : sema_button_var.get(), "sm_dyn" : dyn_button_var.get(), "decrisper": False, "controlnet_strength": 1.0, "add_original_image": False, "cfg_rescale": scale2, "noise_schedule": "native", } if (hold_seed_var.get()): params['seed'] = last_generation_seed def resize_and_fill(image, max_size=None): if max_size is None: max_size = get_max_size() original_width, original_height = image.size if original_width > max_size or original_height > max_size: # 비율을 유지하면서 크기 조정 image.thumbnail((max_size, max_size)) # 새 이미지 크기 계산 width, height = image.size new_image = Image.new("RGB", (max_size, max_size), "black") new_image.paste(image, ((max_size - width) // 2, (max_size - height) // 2)) return new_image else: return image def log_error(e, output_file_path="output_file_path"): # 현재 시간을 얻습니다 current_time = datetime.now().strftime("%m/%d %H:%M:%S") # 에러 로그 메시지 error_message = f"#### Error occured at {current_time} ####\nError: {e}\n############################################\n" # 지정된 출력 폴더의 error_log.txt 파일에 쓰기 with open(f"error_log.txt", "a") as file: file.write(error_message) global access_token, access_token_multi, error_count, multi_token_enable while (running_flag): time.sleep(1) try: running_flag = True tprint("Running : NAI request received") if not (multi_token_enable): zipped_bytes = generate_image(access_token, positive, "nai-diffusion-3", "generate", params) else: zipped_bytes = generate_image(access_token_multi, positive, "nai-diffusion-3", "generate", params) d = Path(f"output_NAI/{start_time}/txt2img") d.mkdir(parents=True, exist_ok=True) zipped = zipfile.ZipFile(io.BytesIO(zipped_bytes)) image_bytes = zipped.read(zipped.infolist()[0]) (d / f"{datetime.now().strftime('%Y%m%d_%H%M%S')}.png" ).write_bytes(image_bytes) i = Image.open(io.BytesIO(image_bytes)) i = ImageOps.exif_transpose(i).convert("RGB") if temp_clipboard_image is not None: temp_clipboard_image.close() temp_clipboard_image = i i_resized = resize_and_fill(i) if 'tk_image' in globals(): globals()['tk_image'] = None tk_image = ImageTk.PhotoImage(i_resized) image_label.config(image=tk_image) image_label.original_image = i.copy() image_label.image = tk_image # 참조 유지 output_file_path = "output_image.jpg" history_queue(i_resized.copy(), positive, params['seed']) i.save(output_file_path) error_count = 0 except Exception as e: #text_output.insert(tk.END, f"Error: {e}", fg='red') log_error(zipped_bytes.decode('utf-8')[2:-2], "path_to_output_folder") time.sleep(random.uniform(1.0, 2.5)) error_count += 1 if error_count > 5: mac_var.set(0) button.config(state=tk.NORMAL) global delay_offset time.sleep(delay_offset) running_flag = False last_generation_seed = params['seed'] entry_seed_value.set(last_generation_seed) if mac_var.get() and not running_flag: random_function() time.sleep(random.uniform(5.1, 8.5)) button.config(state=tk.DISABLED) window.event_generate(GENERATE_EVENT, when="tail") else: time.sleep(random.uniform(1.0, 2.5)) button.config(state=tk.NORMAL) tprint('state : idle') def filter_csv(input_file, output_file, _search_strings): output_directory = os.getcwd() output_file = os.path.join(output_directory, output_file) search_strings = [s.strip() for s in _search_strings.split(',')] with open(input_file, 'r', newline='', encoding='utf-8') as f_in, \ open(output_file, 'w', newline='', encoding='utf-8') as f_out: reader = csv.reader(f_in) writer = csv.writer(f_out) writer_count = 0 for row in reader: if all(search_str in value for search_str in search_strings for value in row): writer.writerow(row) writer_count += 1 return writer_count def open_file(): initial_dir = os.getcwd() filepath = filedialog.askopenfilename( initialdir=initial_dir, filetypes=[("CSV Files", "*.csv")] ) if filepath: entry_file_path.delete(0, tk.END) entry_file_path.insert(0, filepath) def search(): global total_rows, cached_rows # cached_rows를 global로 선언 input_file = entry_file_path.get() keywords = entry_keyword.get() output_file = 'txt2img_temp_prompt.csv' writer_count = filter_csv(input_file, output_file, keywords) total_rows = writer_count total_rows_count_label.config(text=f".csv 내 프롬프트 행: {writer_count}", fg="blue") text_output.insert(tk.END, f"총 {writer_count}개의 문자열이 검색되어 저장되었습니다.\n") # 검색 후 cached_rows 초기화 cached_rows = [] def exclude(): global total_rows, cached_rows # cached_rows를 global로 선언 input_file = entry_file_path.get() keywords = entry_keyword.get() output_file = 'txt2img_temp_prompt.csv' keyword_label.config(text="검색할 키워드: ") output_directory = os.getcwd() output_file = os.path.join(output_directory, output_file) search_strings = [s.strip() for s in keywords.split(',')] with open(input_file, 'r', newline='', encoding='utf-8') as f_in, \ open(output_file, 'w', newline='', encoding='utf-8') as f_out: reader = csv.reader(f_in) writer = csv.writer(f_out) writer_count = 0 for row in reader: if not any(search_str in value for search_str in search_strings for value in row): writer.writerow(row) writer_count += 1 total_rows = writer_count total_rows_count_label.config(text=f".csv 내 프롬프트 행: {writer_count}", fg="red") text_output.insert(tk.END, f"총 {writer_count}개의 문자열이 검색되어 저장되었습니다.\n") # 제외 후 cached_rows 초기화 cached_rows = [] def copy_image_to_clipboard(): global temp_clipboard_image image = temp_clipboard_image output = BytesIO() image.convert('RGB').save(output, format='BMP') data = output.getvalue()[14:] # BMP 파일 헤더 제거 output.close() win32clipboard.OpenClipboard() win32clipboard.EmptyClipboard() win32clipboard.SetClipboardData(win32clipboard.CF_DIB, data) win32clipboard.CloseClipboard() def reset(): cached_rows = None entry_file_path.delete(0, tk.END) entry_keyword.delete(0, tk.END) entry_deep_search.delete(0, tk.END) text_output.delete('1.0', tk.END) def rearrange_keywords_boys_girls(keywords): boy_keyword = None girl_keyword = None # 처음 10개 키워드를 확인 for keyword in keywords[:10]: if 'boy' in keyword and len(keyword) <= 5: boy_keyword = keyword if 'girl' in keyword and len(keyword) <= 5: girl_keyword = keyword # 'boy'와 'girl' 키워드가 존재하고, 조건을 만족하는 경우 if boy_keyword and girl_keyword: boy_index = keywords.index(boy_keyword) if boy_index != 0: keywords.insert(0, keywords.pop(boy_index)) girl_index = keywords.index(girl_keyword) if girl_index > boy_index: keywords.insert(boy_index + 1, keywords.pop(girl_index)) return keywords def random_function(): global last_deep_search_keywords, cached_rows, last_selected_row_keywords, top_100_keywords, previous_artist, top_100_counts global current_wildcard_character, current_wildcard_artist global previous_wildcard_artist, previous_wildcard_character, character_keys, copyright_keys current_deep_search_keywords = entry_deep_search.get().strip() auto_hide_keywords = entry_auto_hide.get().split(',') auto_hide_keywords = [keyword.strip() for keyword in auto_hide_keywords if keyword.strip()] current_artist = None curly_brackets_keywords = re.findall(r'\{([^}]+)\}', current_deep_search_keywords) # 각 브래킷 내 키워드 그룹을 처리하여 완벽 일치 키워드와 일반 키워드를 구분 processed_keywords = [] for group in curly_brackets_keywords: perfect_match_group = [kw[1:].strip() for kw in group.split('|') if kw.startswith('*')] regular_group = [kw.strip() for kw in group.split('|') if not kw.startswith('*')] processed_keywords.append((perfect_match_group, regular_group)) modified_deep_search_keywords = re.sub(r'\{[^}]+\}', '', current_deep_search_keywords) # 남은 키워드들을 쉼표로 분리하여 처리 remaining_keywords = [kw.strip() for kw in modified_deep_search_keywords.split(',') if kw.strip()] perfect_match_keywords = [kw[1:].strip() for kw in remaining_keywords if kw.startswith('*')] # exclude_keywords와 include_keywords 정의 exclude_keywords = [kw[1:].strip() for kw in remaining_keywords if kw.startswith('~') and not kw[1:].startswith('*')] include_keywords = [kw for kw in remaining_keywords if not kw.startswith('~') and not kw.startswith('*')] if current_deep_search_keywords != last_deep_search_keywords or not cached_rows: if not os.path.exists('txt2img_temp_prompt.csv'): text_output.delete('1.0', tk.END) text_output.insert(tk.END, "txt2img_temp_prompt.csv가 없어 재검색을 수행합니다. 이 작업에는 매우 긴 시간이 소요됩니다. tags.csv 파일에 대해 초기화 키워드 및 .csv 내보내기 기능을 활용 해 주세요.") try: with open('txt2img_temp_prompt.csv', 'r', newline='', encoding='utf-8') as f: reader = csv.reader(f) rows = [] for row in reader: if all(re.search(r'(?:^|, )' + re.escape(kw) + r'(?:,|$)', ', '.join(row)) for kw in perfect_match_keywords) \ and not any(re.search(r'\b' + re.escape(exclude) + r'\b', cell) for exclude in exclude_keywords for cell in row) \ and all(include in cell for include in include_keywords for cell in row): # '{}' 안의 키워드 처리 if all(any(re.search(r'(?:^|, )' + re.escape(kw) + r'(?:,|$)', ', '.join(cell)) for kw in perfect_group) or any(kw in cell for kw in regular_group) for perfect_group, regular_group in processed_keywords for cell in row): rows.append(row) cached_rows = rows except: try: search() with open('txt2img_temp_prompt.csv', 'r', newline='', encoding='utf-8') as f: reader = csv.reader(f) rows = [] for row in reader: if all(re.search(r'(?:^|, )' + re.escape(kw) + r'(?:,|$)', ', '.join(row)) for kw in perfect_match_keywords) \ and not any(re.search(r'\b' + re.escape(exclude) + r'\b', cell) for exclude in exclude_keywords for cell in row) \ and all(include in cell for include in include_keywords for cell in row): # '{}' 안의 키워드 처리 if all(any(re.search(r'(?:^|, )' + re.escape(kw) + r'(?:,|$)', ', '.join(cell)) for kw in perfect_group) or any(kw in cell for kw in regular_group) for perfect_group, regular_group in processed_keywords for cell in row): rows.append(row) cached_rows = rows except: def open_link(url): webbrowser.open_new(url) text_output.delete('1.0', tk.END) text_output.insert(tk.END, "tags.csv 파일을 [파일 열기] 버튼을 눌러 탑재하거나,") text_output.insert(tk.END, "https://huggingface.co/baqu2213/PoemForSmallFThings/blob/main/Danbooru%20Prompt%20Selector/tags_2023.csv", "link") text_output.insert(tk.END, " 에서 다운로드 하시기 바랍니다.") text_output.tag_config("link", foreground="blue", underline=True) text_output.tag_bind("link", "", lambda e: open_link("https://huggingface.co/baqu2213/PoemForSmallFThings/blob/main/Danbooru%20Prompt%20Selector/tags_2023.csv")) return last_deep_search_keywords = current_deep_search_keywords if not toggle_prompt_var.get(): text_output.delete('1.0', tk.END) if random_artist_var.get(): if(not entry_fixed_prompt.get()): entry_fixed_prompt.insert(tk.END,'1girl') if not os.path.exists("counting_result.txt"): keyword_counts = analyze_cached_rows(cached_rows) excluded_keywords = set(whitelist[:2974]) with open("counting_result.txt", "w") as file: for keyword, count in sorted(keyword_counts.items(), key=lambda item: item[1], reverse=True): if keyword not in excluded_keywords and keyword in afilter_30000: file.write(f"{keyword}: {count}\n") with open("counting_result.txt", "r") as file: lines = file.readlines() top_100_data = [ (line.split(":")[0].strip(), int(line.split(":")[1].strip())) for line in lines[:3000] if line.split(":")[0].strip() in afilter_30000[:8528] ] top_100_keywords, top_100_counts = zip(*top_100_data) if top_100_data else ([], []) elif (not top_100_keywords): with open("counting_result.txt", "r") as file: lines = file.readlines() top_100_data = [ (line.split(":")[0].strip(), int(line.split(":")[1].strip())) for line in lines[:3000] if line.split(":")[0].strip() in afilter_30000[:8528] ] top_100_keywords, top_100_counts = zip(*top_100_data) if top_100_data else ([], []) formatted_artist_random_keyword = [] if top_100_keywords: temp_keyword = [] temp_keyword += top_100_keywords whatthefuck = random.random() if(whatthefuck > 0.9): for i in range(4): random_keyword = random.choice(temp_keyword) temp_keyword.remove(random_keyword) if(i == 0): formatted_artist_random_keyword = "{{{artist:"+random_keyword+"}}}" if(i == 1): formatted_artist_random_keyword += ",{artist:"+random_keyword+"}" if(i == 2): formatted_artist_random_keyword += ",artist:"+random_keyword if(i == 3): formatted_artist_random_keyword += ",[[artist:"+random_keyword+"]]" elif(whatthefuck > 0.7): for i in range(3): random_keyword = random.choice(temp_keyword) temp_keyword.remove(random_keyword) if(i == 0): formatted_artist_random_keyword = "{{{artist:"+random_keyword+"}}}" if(i == 1): formatted_artist_random_keyword += ",{artist:"+random_keyword+"}" if(i == 2): formatted_artist_random_keyword += ",[[artist:"+random_keyword+"]]" elif(whatthefuck > 0.45): for i in range(2): random_keyword = random.choice(temp_keyword) temp_keyword.remove(random_keyword) if(i == 0): formatted_artist_random_keyword = "{{{artist:"+random_keyword+"}}}" if(i == 2): formatted_artist_random_keyword += ",[[artist:"+random_keyword+"]]" else: random_keyword = random.choice(temp_keyword) temp_keyword.remove(random_keyword) formatted_artist_random_keyword = "{{artist:"+random_keyword+"}}" current_artist = formatted_artist_random_keyword if not top_100_keywords: random_artist_var.set(0) else: formatted_artist_random_keyword = f"작가 키워드가 없음!!" if cached_rows: if toggle_prompt_var.get() and last_selected_row_keywords: random_row_keywords = [keyword.strip() for keyword in text_output.get("1.0", "end-1c").split(',')] random_row_keywords = [keyword.strip() for keyword in text_output.get("1.0", "end-1c").split(',')] #random_row_keywords[-1].strip() #if random_row_keywords[0]=='': random_row_keywords = last_selected_row_keywords text_output.delete('1.0', tk.END) else: if (safe_search_var.get()): for i in range(100): random_index = random.randint(0, len(cached_rows) - 1) random_row = cached_rows.pop(random_index) random_row_keywords = [keyword.strip() for keyword in random_row[0].split(',')] # random_row_keywords에 qe_word 해당하는 키워드가 없는지 확인 if not any(keyword in nsfw_word for keyword in random_row_keywords): break # nsfw_word에 해당하는 키워드가 없으므로 반복문 탈출 if len(cached_rows) == 0: random_row_keywords = [keyword.strip() for keyword in "1girl, blank eyes, cell phone, chibi, crying, crying with eyes open, gloom (expression), holding, holding phone, open mouth, phone, smartphone, solo, surprised, tears, turn pale, white background, simple background, Search fail (text)".split(',')] break if i == 99: random_row_keywords = random_row_keywords = [keyword.strip() for keyword in "1girl, blank eyes, cell phone, chibi, crying, crying with eyes open, gloom (expression), holding, holding phone, open mouth, phone, smartphone, solo, surprised, tears, turn pale, white background, simple background, Search fail (text)".split(',')] last_selected_row_keywords = random_row_keywords.copy() else: random_index = random.randint(0, len(cached_rows) - 1) random_row = cached_rows.pop(random_index) random_row_keywords = [keyword.strip() for keyword in random_row[0].split(',')] last_selected_row_keywords = random_row_keywords.copy() if(entry_fixed_prompt.get()): entry_text_keywords = entry_fixed_prompt.get() if(',' not in entry_text_keywords[-2:]): entry_text_keywords += ',' entry_text_keywords = entry_text_keywords.split(',') entry_text_keywords = [kw.strip() for kw in entry_text_keywords if kw.strip()] temp_first_keywords = [] #print("1 :", temp_first_keywords) # entry_text_keywords의 모든 키워드를 temp_first_keywords에 추가 for et_kw in entry_text_keywords: if ('boy' in et_kw or 'girl' in et_kw) or (et_kw not in random_row_keywords and et_kw not in random_row_keywords): temp_first_keywords.append(et_kw) # 'boy' 키워드 처리 boy_keywords = [kw for kw in random_row_keywords if 'boy' in kw and len(kw) <= 7] # 'girl' 키워드 처리 및 위치 조정 girl_keywords = [] if 'girl' in entry_text_keywords[0] and len(entry_text_keywords[0]) <= 7: girl_keywords = [kw for kw in random_row_keywords if 'girl' in kw and len(kw) <= 7] # random_row_keywords에서 boy와 girl 키워드 제거 for kw in boy_keywords + girl_keywords: if kw in random_row_keywords: random_row_keywords.remove(kw) for kw in temp_first_keywords: if kw in random_row_keywords: random_row_keywords.remove(kw) # temp_first_keywords에 boy와 girl 키워드를 적절한 위치에 추가 if(girl_keywords and 'girl' in temp_first_keywords[0]): temp_first_keywords.pop(0) #print(wildcard_var.get()) print(random_row_keywords) if(wildcard_var2.get() == 1): temp_len = len(girl_keywords) print('previous : ',previous_wildcard_character, 'type : ', type(previous_wildcard_character)) if previous_wildcard_character in random_row_keywords: random_row_keywords.remove(previous_wildcard_character) elif(previous_wildcard_character is not None): split_previous_wildcard_character = previous_wildcard_character.split(',') split_previous_wildcard_character = [keyword.strip() for keyword in split_previous_wildcard_character] for _keyword in split_previous_wildcard_character: if _keyword in random_row_keywords: random_row_keywords.remove(_keyword) if _keyword in random_row_keywords: random_row_keywords.remove(_keyword) previous_wildcard_character = get_random_keyword('character') girl_keywords.append(previous_wildcard_character) if(wildcard_var.get() == 1): print('previous : ',previous_wildcard_artist, 'type : ', type(previous_wildcard_artist)) if previous_wildcard_artist is not None and previous_wildcard_artist in random_row_keywords: random_row_keywords.remove(previous_wildcard_artist) elif(previous_wildcard_artist is not None): split_previous_wildcard_artist = previous_wildcard_artist.split(',') split_previous_wildcard_artist = [keyword.strip() for keyword in split_previous_wildcard_artist] split_previous_wildcard_artist[0] = split_previous_wildcard_artist[0] for _keyword in split_previous_wildcard_artist: if _keyword in random_row_keywords: random_row_keywords.remove(_keyword) previous_wildcard_artist = get_random_keyword('artist') if(wildcard_var2.get() == 1): girl_keywords.insert(temp_len+1,previous_wildcard_artist) else: girl_keywords.append(previous_wildcard_artist) #print(boy_keywords) #print(girl_keywords) #print(temp_first_keywords) if temp_first_keywords and 'girl' in temp_first_keywords[0]: girl_in_gkey = False for gkey in girl_keywords: if 'girl' in gkey: girl_in_gkey = True if not girl_in_gkey: temp_gkey = temp_first_keywords.pop(0) girl_keywords.insert(0, temp_gkey) temp_first_keywords = boy_keywords + girl_keywords + temp_first_keywords #print("2 :", temp_first_keywords) if remove_artist_var.get(): for keyword in random_row_keywords: if keyword in afilter_30000: random_row_keywords.remove(keyword) temp_rm = [] if rm_characteristic_var.get(): for keyword in random_row_keywords: if keyword in bag_of_tags or keyword in character_keys or "(" in keyword or "horns" in keyword: if keyword not in entry_text_keywords: temp_rm.append(keyword) for keyword in temp_rm: random_row_keywords.remove(keyword) #NSFW_assert_var nsfw_temp = [] if NSFW_assert_var.get(): for keyword in random_row_keywords: if not rm_characteristic_var.get(): if keyword not in qe_word and (keyword not in bag_of_tags and keyword not in character_keys and "(" not in keyword and "horns" not in keyword): nsfw_temp.append(keyword) else: if keyword not in qe_word: nsfw_temp.append(keyword) if nsfw_temp is not None: if not remove_artist_var.get(): temp_rm_arti= [] for keyword in nsfw_temp: if keyword in afilter_30000: temp_rm_arti.append(keyword) for keyword in temp_rm_arti: nsfw_temp.remove(keyword) if not rm_copyright_var.get(): for keyword in nsfw_temp: if keyword not in copyright_keys: random_row_keywords.remove(keyword) else: for keyword in nsfw_temp: random_row_keywords.remove(keyword) for keyword in random_row_keywords[:]: # 복사본을 순회하여 원본 리스트를 수정 if " (" in keyword: temp_first_keywords.append(keyword) random_row_keywords.remove(keyword) else: if not random_row_keywords: random_row_keywords = last_selected_row_keywords temp_first_keywords = [] for keyword in random_row_keywords[:]: # 복사본을 순회하여 원본 리스트를 수정 if " (" in keyword: temp_first_keywords.append(keyword) random_row_keywords.remove(keyword) t_temp = [] for keyword in temp_first_keywords: t_temp.append(keyword.strip()) t_temp = list(OrderedDict.fromkeys(t_temp)) temp_first_keywords = list(t_temp) #last_selected_row_keywords =boy_keywords + girl_keywords[:0] + random_row_keywords #print("3 :", temp_first_keywords) if random_artist_var.get(): temp_first_keywords.append(formatted_artist_random_keyword) #print("4 :", temp_first_keywords) if (previous_artist and previous_artist in temp_first_keywords): temp_first_keywords.remove(previous_artist) if (current_artist is (not None)): for i in range (len(current_artist)): previous_artist += current_artist[i] random_row_keywords = temp_first_keywords + random_row_keywords #print("5 :", random_row_keywords) #elif toggle_prompt_var.get(): random_row_keywords = temp_first_keywords + random_row_keywords else: random_row_keywords = temp_first_keywords + random_row_keywords patterns = auto_hide_keywords rm_reserved = [] for pattern in patterns: if pattern in random_row_keywords: rm_reserved.append(pattern) for pattern in rm_reserved: random_row_keywords.remove(pattern) if entry_fixed_prompt_after.get(): entry_after_text = [keyword.strip() for keyword in entry_fixed_prompt_after.get().split(',')] for at_kw in entry_after_text: if at_kw not in random_row_keywords and at_kw not in random_row_keywords: random_row_keywords.append(at_kw) rm_copyright = [] if(rm_copyright_var.get()): for keyword in random_row_keywords: if keyword in copyright_keys: rm_copyright.append(keyword) if rm_copyright is not None: for keyword in rm_copyright: random_row_keywords.remove(keyword) else: idx = 0 for keyword in random_row_keywords: if keyword in copyright_keys: random_row_keywords[idx] = keyword+" (copyright)" idx += 1 pop_character = [] for keyword in random_row_keywords: character_temp_keyword = keyword.replace('{', '').replace('}', '').replace('[', '').replace(']', '') if character_temp_keyword in character_keys: pop_character.append(keyword) if pop_character is not None: found_index = 0 # Search for 'girl' in the first four elements for i in range(min(10, len(random_row_keywords))): if 'girl' in random_row_keywords[i]: found_index = i break elif 'boy' in random_row_keywords[i]: found_index = i break elif 'other' in random_row_keywords[i]: found_index = i break for keyword in pop_character: found_index += 1 random_row_keywords.remove(keyword) random_row_keywords.insert(found_index,keyword) pop_artist = [] for keyword in random_row_keywords: artist_temp_keyword = keyword.replace('{', '').replace('}', '').replace('[', '').replace(']', '') if artist_temp_keyword in afilter_30000: pop_artist.append(keyword) if pop_artist is not None: for keyword in pop_artist: found_index += 1 random_row_keywords.remove(keyword) random_row_keywords.insert(found_index,"artist:"+keyword) random_row_keywords = rearrange_keywords_boys_girls(random_row_keywords) #print(random_row_keywords) text_output.insert(tk.END, f"{', '.join(random_row_keywords)}") if auto_copy_var.get(): copy_to_clipboard() else: text_output.insert(tk.END, "검색 조건에 맞는 데이터가 없거나 CSV 파일에 데이터가 없습니다.\n") cached_rows_count_label.config(text=f"심층검색 프롬프트 행: {len(cached_rows)}") def add_low_frequency_keywords(): global keyword_counts, most_common_count keyword_counts = analyze_cached_rows(cached_rows) if not keyword_counts: # keyword_counts가 비어있는 경우 함수를 빠져나옵니다. return # 가장 흔한 키워드의 빈도수를 가져옵니다. most_common_count = keyword_counts.most_common(1)[0][1] # 조건에 맞는 키워드들을 찾습니다. low_freq_keywords = [keyword for keyword, count in keyword_counts.items() if keyword not in last_deep_search_keywords and keyword not in whitelist and count / most_common_count <= 0.05] # 자동 숨김 키워드 입력창에 업데이트합니다. existing_keywords = entry_auto_hide.get().split(',') existing_keywords = [keyword.strip() for keyword in existing_keywords if keyword.strip()] # 공백 제거 및 빈 문자열 제거 # 기존 키워드에 새로운 저빈도 키워드를 추가합니다. updated_keywords = existing_keywords + low_freq_keywords unique_keywords = list(set(updated_keywords)) # 중복 제거 for keyword in unique_keywords: if(keyword in afilter_30000): unique_keywords.remove(keyword) entry_auto_hide.delete(0, tk.END) entry_auto_hide.insert(0, ', '.join(unique_keywords)) def copy_to_clipboard(): window.clipboard_clear() entry_text = entry_fixed_prompt.get() if(',' not in entry_text and entry_text): entry_text += ',' combined_text = text_output.get("1.0", tk.END) window.clipboard_append(combined_text) def turbo_stop(button): global turbo_stop_bit button.config(state=tk.DISABLED) turbo_stop_bit = True tprint("Stopping pipelines ... ") window.after(20000, lambda: button_generate.config(state='normal')) def exit_program(): global stop_event, auto_thread if stop_event is not None: stop_event.set() if auto_thread is not None and auto_thread.is_alive(): auto_thread.join() # 스레드가 종료될 때까지 기다림 window.destroy() def save_settings(): global NAI_ID with open('app_settings.txt', 'w', encoding='utf-8') as f: #1 if(len(entry_file_path.get()) > 4): f.write(entry_file_path.get() + '\n') else: f.write(" "+'\n') if(len(entry_keyword.get()) > 3): #2 f.write(entry_keyword.get() + '\n') else: f.write(" "+'\n') if(len(entry_deep_search.get()) > 3): #3 f.write(entry_deep_search.get() + '\n') else: f.write(" "+'\n') if(len(entry_fixed_prompt.get()) > 4): #4 f.write(entry_fixed_prompt.get() + '\n') else: f.write(" "+'\n') if(len(entry_fixed_prompt_after.get()) > 4): #5 f.write(entry_fixed_prompt_after.get() + '\n') else: f.write(" "+'\n') if(len(entry_auto_hide.get()) > 3): #6 f.write(entry_auto_hide.get() + '\n') else: f.write(" "+'\n') if(len(negative_text.get("1.0", tk.END)) > 4): #7 f.write(negative_text.get("1.0", tk.END)) else: f.write(" "+'\n') if(NAI_ID): f.write(NAI_ID + '\n' ) #항상 마지막에 오도록 기억 #8 def load_settings(): NAI_ID = "" if os.path.exists('app_settings.txt'): with open('app_settings.txt', 'r', encoding='utf-8') as f: settings = f.readlines() entry_file_path.insert(0, settings[0].strip()) entry_keyword.insert(0, settings[1].strip()) entry_deep_search.insert(0, settings[2].strip()) entry_fixed_prompt.insert(0, settings[3].strip()) entry_fixed_prompt_after.insert(0, settings[4].strip()) entry_auto_hide.insert(0, settings[5].strip()) if(len(settings)>=7): negative_text.insert(tk.END, settings[6].strip()) if(len(settings)>=8): NAI_ID = settings[7].strip() #항상 마지막에 오도록 기억 return NAI_ID def open_prompt_window(): # 새 창 생성 prompt_window = tk.Toplevel(window) prompt_title = f"{entry_keyword.get()}+{last_deep_search_keywords}" if last_deep_search_keywords else "추천 프롬프트" prompt_window.title(prompt_title) # 첫 번째 Text Output Box 설정 text_output1 = tk.Text(prompt_window, height=10, width=50) text_output1.pack(padx=10, pady=10) # 굵은 글씨 스타일 태그를 설정합니다. text_output1.tag_configure('bold', font=('Arial', 10, 'bold')) # 두 번째 Text Output Box 설정 text_output2 = tk.Text(prompt_window, height=10, width=50) text_output2.pack(padx=10, pady=10) # 세 번째 Text Output Box 설정 text_output3 = tk.Text(prompt_window, height=10, width=50) text_output3.pack(padx=10, pady=10) # cached_rows 분석 keyword_counts = analyze_cached_rows(cached_rows) # 가장 높은 count 값을 가져옵니다. most_common_count = keyword_counts.most_common(1)[0][1] if keyword_counts else 0 # text_output1에 가장 흔한 키워드를 출력하고, 조건에 따라 굵게 표시합니다. for keyword, count in keyword_counts.most_common(50): if count / most_common_count >= 0.5: # count가 가장 높은 키워드 대비 60% 이상인 경우 text_output1.insert(tk.END, f"{keyword}, ", 'bold') else: text_output1.insert(tk.END, f"{keyword}, ") # text_output2에 모든 키워드와 빈도수 출력 for keyword, count in keyword_counts.most_common(200): text_output2.insert(tk.END, f"{keyword}: {count}, ") excluded_keywords = set(whitelist[:2974]) artist_count = 0 with open("counting_result.txt", "w") as file: for keyword, count in sorted(keyword_counts.items(), key=lambda item: item[1], reverse=True): if keyword not in excluded_keywords and keyword in afilter_30000: file.write(f"{keyword}: {count}\n") if(artist_count < 150): text_output3.insert(tk.END, f"{keyword}: {count}, ") artist_count += 1 # 닫기 버튼 설정 close_button = tk.Button(prompt_window, text="닫기", command=prompt_window.destroy) close_button.pack(pady=10) def analyze_cached_rows(cached_rows): global top_100_keywords top_100_keywords = [] counts = Counter() for row in cached_rows: substrings = [substring.strip() for value in row for substring in value.split(',') if substring.strip()] counts.update(substrings) return counts def exit_program(): save_settings() window.destroy() def on_ctrl_enter(event): random_function() def on_shift_enter(event): random_function() copy_to_clipboard() def export_csv(): source_file = os.path.join(os.getcwd(), 'txt2img_temp_prompt.csv') if not os.path.exists(source_file): print("소스 파일이 존재하지 않습니다.") return # 기본 파일 이름을 사용자가 입력한 키워드로 설정 default_filename = f"{entry_keyword.get()}.csv" if entry_keyword.get() else "exported.csv" file_types = [('CSV 파일', '*.csv')] dest_file = filedialog.asksaveasfilename(defaultextension='.csv', filetypes=file_types, initialfile=default_filename) if dest_file: # 사용자가 파일 이름을 선택했을 경우 import shutil shutil.copy(source_file, dest_file) print(f"파일이 저장되었습니다: {dest_file}") def export_csv_search(): if not cached_rows: print("비어있는 프롬프트행.") return # 기본 파일 이름을 사용자가 입력한 키워드로 설정 default_filename = f"{entry_keyword.get()}.csv" if entry_keyword.get() else "exported.csv" file_types = [('CSV 파일', '*.csv')] dest_file = filedialog.asksaveasfilename(defaultextension='.csv', filetypes=file_types, initialfile=default_filename) if dest_file: # 사용자가 파일 위치를 선택했다면 with open(dest_file, 'w', newline='', encoding='utf-8') as file: writer = csv.writer(file) for row in cached_rows: writer.writerow(row) # 각 행을 csv 파일에 쓴다 def for_canonical(f): return lambda k: f(listener.canonical(k)) # 라벨을 업데이트하는 함수 def update_label(label, text): def task(): label.config(text=text) label.after(1000, task) # 메인 스레드에서 UI 업데이트 def NAI_generation(width, height, button): global access_token_multi, NAI_width, NAI_height global button_stop, running_flag def run_function(): time.sleep(1) random_function() time.sleep(2) window.event_generate(GENERATE_EVENT, when="tail") origin = text_output.get("1.0", tk.END) pretest =origin.split(', ') if (turbo_var.get() == 1): if (('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])): tprint('부스트 기능은 1girl, 1boy, sex / boys와 호환됩니다.','color=red') if not mac_var.get(): button.config(state='normal') return else: thread = threading.Thread(target=run_function) thread.start() return if(rand_resolution_var.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) selected_resolution.set(random_resolution) if(turbo_var.get() == 1 and access_token): button_stop.config(state='normal') NAI_generation_turbo(NAI_width, NAI_height, button) else: if not (running_flag): NAI_generation_normal(NAI_width, NAI_height, button) def Auto_login_check(): global access_token, access_token_multi if os.path.exists('token.txt'): with open('token.txt', 'r', encoding='utf-8') as f: tokens = f.readlines() if(tokens[0]): access_token = tokens[0].strip() tprint("1 Account automatically loaded : ",access_token[-5:]) if(access_token): button_generate.config(state='normal') def character_search_thread(): search_thread = threading.Thread(target=character_search, daemon=True) search_thread.start() def character_search(): 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('1.0', tk.END) character_prompt.insert(tk.END, f"{', '.join(character)}") cosplay_prompt.delete('1.0', tk.END) cosplay_prompt.insert(tk.END, f"{', '.join(cosplay)}") def on_key_release(event): # Get the current search keyword search_keyword = search_entry.get() # Update the listbox with matching keywords update_listbox(search_keyword) def analyze_keywords_in_data(parquet_file, keyword): # Parquet 파일 읽기 df = pd.read_parquet(parquet_file) bag_of_tags = ['penis', 'character name', 'upper body', 'full body', 'artist name', 'male focus', 'open mouth', 'mature male', 'muscular', 'muscular male', 'closed mouth','closed eyes','1girl', '1boy', '1other', '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', 'artist name', 'male focus', 'open mouth', 'mature male', 'muscular', 'muscular male', 'closed mouth','closed eyes','1girl', '1boy', '1other', '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(0, keyword) result_list.insert(0, "["+keyword+" (cosplay)]") return result_list1, result_list def update_listbox(search_keyword): # Clear the listbox listbox.delete(0, tk.END) # Check if the search keyword is not empty if search_keyword: # Find matching keywords in the dictionary # Sort them by count in descending order matching_keywords = sorted( ((k, v) for k, v in cd.character_dictionary.items() if search_keyword.lower() in k.lower()), key=lambda item: item[1], reverse=True ) # Insert the matching keywords into the listbox for keyword, count in matching_keywords: listbox.insert(tk.END, f"{keyword} - {count}") else: # Sort all keywords by count in descending order all_keywords = sorted(cd.character_dictionary.items(), key=lambda item: item[1], reverse=True) for keyword, count in all_keywords: listbox.insert(tk.END, f"{keyword} - {count}") # 메인 윈도우 생성 ch_search_window = tk.Toplevel(window) ch_search_window.title("캐릭터 검색") # 프레임 생성 (left, center, right) left_frame = tk.Frame(ch_search_window) center_frame = tk.Frame(ch_search_window) right_frame = tk.Frame(ch_search_window) # 프레임 배치 left_frame.grid(row=0, column=0, sticky="nsew") center_frame.grid(row=0, column=1, sticky="ns") right_frame.grid(row=0, column=2, sticky="nsew") # left_frame 내부 구성 search_label = tk.Label(left_frame, text="검색 하려는 캐릭터:") search_label.grid(row=0, column=0, padx=10, pady=5, sticky="ew") search_entry = tk.Entry(left_frame, width=35) search_entry.grid(row=1, column=0, padx=10, pady=5, sticky="ew") search_entry.bind("", on_key_release) result_label = tk.Label(left_frame, text="매칭 캐릭터:") result_label.grid(row=2, column=0, padx=10, pady=5, sticky="ew") listbox = tk.Listbox(left_frame) listbox.grid(row=3, column=0, padx=10, pady=5, sticky="nsew") # center_frame 내부 구성 search_button = tk.Button(center_frame, text="검색", command=on_search) search_button.grid(row=2, column=0, rowspan=4, padx=10, pady=175) def character_prompt_auto_insert(): 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() prompt = character_prompt.get("1.0", tk.END).replace(keyword+',','') if(len(prompt) > 8): text_output.delete('1.0', tk.END) entry_prompt = entry_fixed_prompt.get().replace('1girl', f'1girl, {keyword}') result_text = entry_prompt+", "+prompt[:-2]+", "+entry_fixed_prompt_after.get() text_output.insert(tk.END, result_text) return (len(prompt) > 8) def character_prompt_auto_generate(): global running_flag if(character_prompt_auto_insert() and not running_flag): window.event_generate(GENERATE_EVENT, when="tail") def character_cosplay_auto_insert(): prompt = cosplay_prompt.get("1.0", tk.END) if(len(prompt) > 8): text_output.delete('1.0', tk.END) result_text = entry_fixed_prompt.get()+", "+prompt[:-2]+", "+entry_fixed_prompt_after.get() text_output.insert(tk.END, result_text) return (len(prompt) > 8) def character_cosplay_auto_generate(): if(character_cosplay_auto_insert() and not running_flag): window.event_generate(GENERATE_EVENT, when="tail") def wildcard_insertion(): global bag_of_tags, character_wildcard_saved text = character_prompt.get("1.0", tk.END).split(',') text_list = [keyword.strip() for keyword in text] charactersitics = [] count = 0 for texts in text_list: if count < 4 and texts in bag_of_tags[5:] and 'tail' not in texts and 'pupil' not in texts: charactersitics.append(texts) count += 1 keyword = text_list[0] character_wildcard_saved.append('100:{{'+keyword+', '+', '.join(charactersitics)+'}}') character_prompt.insert(tk.END, '\n저장된 와일드카드 : 100:{{'+keyword+', '+', '.join(charactersitics)+'}} 탑재는 따로 한번 더 해주셔야 합니다.') # right_frame 내부 구성 character_prompt_frame = tk.Frame(right_frame) character_prompt_frame.grid(row=0, column=0, padx=10, pady=5, sticky="ew") prompt_label = tk.Label(character_prompt_frame, text="캐릭터 프롬프트:") prompt_label.grid(row=0, column=0, padx=10, pady=5, sticky="ew") character_prompt_insert = tk.Button(character_prompt_frame, text="자동입력", command=character_prompt_auto_insert) character_prompt_insert.grid(row=0, column=1, padx=10, pady=5, sticky="ew") character_prompt_generate = tk.Button(character_prompt_frame, text="즉시생성", command=character_prompt_auto_generate) character_prompt_generate.grid(row=0, column=2, padx=10, pady=5, sticky="ew") character_prompt_wildcard = tk.Button(character_prompt_frame, text="와일드카드 삽입", command=wildcard_insertion) character_prompt_wildcard.grid(row=0, column=3, padx=10, pady=5, sticky="ew") character_prompt = tk.Text(right_frame, height=10) character_prompt.grid(row=1, column=0, padx=10, pady=5, sticky="nsew") #row=3, column=0, padx=10, pady=5, sticky="nsew" character_cosplay_frame = tk.Frame(right_frame) character_cosplay_frame.grid(row=2, column=0, padx=10, pady=5, sticky="ew") cosplay_label = tk.Label(character_cosplay_frame, text="캐릭터 (Cosplay):") cosplay_label.grid(row=0, column=0, padx=10, pady=5, sticky="ew") character_cosplay_insert = tk.Button(character_cosplay_frame, text="자동입력", command=character_cosplay_auto_insert) character_cosplay_insert.grid(row=0, column=1, padx=10, pady=5, sticky="ew") character_prompt_generate = tk.Button(character_cosplay_frame, text="즉시생성", command=character_cosplay_auto_generate) character_prompt_generate.grid(row=0, column=2, padx=10, pady=5, sticky="ew") cosplay_prompt = tk.Text(right_frame, height=10) cosplay_prompt.grid(row=3, column=0, padx=10, pady=5, sticky="ew") # 가중치 설정 left_frame.grid_rowconfigure(0, weight=1) left_frame.grid_rowconfigure(1, weight=1) left_frame.grid_rowconfigure(2, weight=1) left_frame.grid_rowconfigure(3, weight=7) right_frame.grid_rowconfigure(0, weight=1) right_frame.grid_rowconfigure(1, weight=4) right_frame.grid_rowconfigure(2, weight=1) right_frame.grid_rowconfigure(3, weight=4) # 윈도우 가중치 설정 ch_search_window.grid_columnconfigure(0, weight=3, minsize=250) # left_frame ch_search_window.grid_columnconfigure(1, weight=1) # center_frame ch_search_window.grid_columnconfigure(2, weight=6) # right_frame all_keywords = sorted(cd.character_dictionary.items(), key=lambda item: item[1], reverse=True) for keyword, count in all_keywords: listbox.insert(tk.END, f"{keyword} - {count}") def NAI_generation_normal(width, height, button): button.config(state=tk.DISABLED) positive = text_output.get("1.0", tk.END) negative = negative_text.get("1.0", "end-1c").strip() global auto_count_left_flag, auto_count_left if(auto_count_left_flag and auto_count_left > 0): auto_count_left -= 1 thread = threading.Thread(target=generate, args=(width, height, positive, negative, button)) thread.start() def NAI_generation_turbo(width, height, button): global turbo_count, access_token, access_token_multi button.config(state=tk.DISABLED) positive = text_output.get("1.0", tk.END) negative = negative_text.get("1.0", "end-1c").strip() def SELECT(positive): lines = positive result = { "boys": False, "girls": False, "1girl": False, "1boy": False, "1other": False, "others": False } state = { "nude,": False, "pov,": False, "cum,": False, "after ": False, "pussy juice": False, "barefoot": False, "breasts": False, "ejaculation": False, } def insert_spaces(source_list, reference_list): modified_list = source_list.copy() for index, keyword in enumerate(reference_list): if keyword not in source_list: space_count = len(keyword) # 키워드 길이만큼의 공백 문자 modified_list.insert(index, ' ' * space_count) return modified_list keywords = positive.split(', ') filtered_keywords = [] removed_indices = [] positive0, positive1, positive2, positive3 = None, None, None, None for word in result.keys(): if word in lines: result[word] = True for word in state.keys(): if word in positive: state[word] = True key_index = int((len(keywords)/2)-1) global bag_of_tags eye_color = ['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'] hair_color = ['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'] if not wildcard_var2.get() and not rm_characteristic_var.get(): key_features = [] for tag in bag_of_tags: if tag in keywords: key_features.append(tag) keywords.remove(tag) if not any(color in key_features for color in eye_color): key_features.append(random.choice(eye_color)) if not any(color in key_features for color in hair_color): key_features.append(random.choice(hair_color)) key_feature = "{" +' ,'.join(key_features) + "}" keywords.insert(2, key_feature) if(result["1boy"]) or (result["boys"]): if(result["1girl"]): if('sex,' in positive): sex_pos_keywords = ['stomach bulge','insertion', 'fucked silly', 'x-ray', 'orgasm', 'cross-section', 'uterus', 'overflow', 'rape', 'vaginal', 'anal'] facial_keywords = ['tongue','ahegao'] temp_sex_pos = [] temp_facial = [] cum_events = [] explicit_check = [] if 'open mouth' in keywords: keywords.remove('open mouth') if 'closed mouth' in keywords: keywords.remove('closed mouth') if 'after rape' in keywords: keywords.remove('after rape') explicit_check.append('after rape') for keyword in keywords: if ('sex' not in keyword and 'cum' not in keyword and 'ejaculation' not in keyword and 'vaginal' not in keyword and 'penetration' not in keyword) and all(sex_pos not in keyword for sex_pos in sex_pos_keywords) and all(facial not in keyword for facial in facial_keywords): filtered_keywords.append(keyword) elif 'sex' in keyword: removed_indices.append(keyword) elif 'penetration' in keyword: removed_indices.append(keyword) elif 'cum' in keyword and keyword != 'cum': cum_events.append(keyword) elif any(sex_pos in keyword for sex_pos in sex_pos_keywords): for sex_pos in sex_pos_keywords: if sex_pos in keyword: temp_sex_pos.append(sex_pos) elif any(facial not in keyword for facial in facial_keywords): for facial in facial_keywords: if facial in keyword: temp_facial.append(facial) filtered_keywords.insert(int((len(filtered_keywords)/2)-1), ' no penetration, imminent penetration') filtered_keywords_positive0 = filtered_keywords.copy() filtered_keywords.remove(' no penetration, imminent penetration') #0 imminent penetration, imminent sex for i, keyword in enumerate(filtered_keywords): if 'pantyhose' in keyword: filtered_keywords[i] = 'torn ' + filtered_keywords[i] #1 default key_index = int((len(filtered_keywords)/2)-1) if 'pussy' in filtered_keywords: key_index = filtered_keywords.index('pussy') if 'penis' in filtered_keywords: key_index = filtered_keywords.index('penis') filtered_keywords[key_index:key_index] = ['motion lines', 'surprised'] for keyword in removed_indices: if 'cum' not in keyword and 'ejaculation' not in keyword: filtered_keywords.insert(key_index,keyword) if(temp_sex_pos): filtered_keywords[key_index:key_index] = temp_sex_pos if('clothed sex' in filtered_keywords and not 'bottomless' in filtered_keywords): filtered_keywords.insert(filtered_keywords.index('clothed sex')+1, 'bottomless') pos1_copied_keywords = filtered_keywords.copy() for i, keyword in enumerate(pos1_copied_keywords): if 'closed eyes' in keyword: rand_num = random.randint(0,2) if(rand_num == 0): pos1_copied_keywords[i] = 'half-' + pos1_copied_keywords[i] elif(rand_num == 1 and 'closed eyes' in pos1_copied_keywords): pos1_copied_keywords.remove('closed eyes') filtered_keywords[i] = 'half-closed eyes' filtered_keywords_positive1 = pos1_copied_keywords.copy() #2 ejaculation,cum in pussy key_index = filtered_keywords.index('surprised') filtered_keywords.remove('surprised') filtered_keywords[key_index:key_index] = ["ejaculation","cum"] for keyword in removed_indices: if 'cum' in keyword: filtered_keywords.insert(key_index,keyword) if(temp_facial): filtered_keywords[key_index:key_index] =temp_facial filtered_keywords_positive2 = filtered_keywords.copy() #3 after sex, after ejaculation for i, keyword in enumerate(filtered_keywords): if 'closed eyes' in keyword: rand_num = random.randint(0,2) if(rand_num == 0 and filtered_keywords[i] != 'half-closed eyes'): filtered_keywords[i] = 'half-' + filtered_keywords[i] elif(rand_num == 1): filtered_keywords[i] = 'empty eyes' else: filtered_keywords[i] = 'empty eyes, half-closed eyes' if 'sex' in filtered_keywords: key_index = filtered_keywords.index('sex') elif 'group sex' in filtered_keywords: key_index = filtered_keywords.index('group sex') filtered_keywords.remove('ejaculation') filtered_keywords[key_index:key_index] = ['cum drip', 'erection'] + cum_events if(explicit_check): filtered_keywords[key_index:key_index] = explicit_check if 'sex' in filtered_keywords and 'group sex' not in filtered_keywords: if('pussy' in filtered_keywords and not 'anal' in filtered_keywords): filtered_keywords.insert(filtered_keywords.index('sex')+1, 'after vaginal, spread pussy') elif('anal' in filtered_keywords): filtered_keywords.insert(filtered_keywords.index('sex')+1, 'after anus, cum in ass') filtered_keywords.insert(filtered_keywords.index('sex'), 'after sex') filtered_keywords.remove('sex') elif 'group sex' in filtered_keywords: if('vaginal' in filtered_keywords and not 'anal' in filtered_keywords): filtered_keywords.insert(filtered_keywords.index('group sex')+1, 'after vaginal, spread pussy') if 'multiple penises' in filtered_keywords: filtered_keywords.insert(filtered_keywords.index('group sex')+3, 'cum on body, bukkake') elif('anal' in filtered_keywords): filtered_keywords.insert(filtered_keywords.index('group sex')+1, 'after anus, cum in ass') if 'multiple penises' in filtered_keywords: filtered_keywords.insert(filtered_keywords.index('group sex')+3, 'cum on body, bukkake') else: filtered_keywords.insert(filtered_keywords.index('group sex')+1, 'cum on body, {bukkake}') temp_post_keyword = [] for keyword in sex_pos_keywords: if not (keyword == 'orgasm' or keyword == 'overflow'): if keyword in filtered_keywords: temp_post_keyword.append(keyword) for keyword in temp_post_keyword: filtered_keywords.remove(keyword) positive0 = ', '.join(insert_spaces(filtered_keywords_positive0, filtered_keywords)).strip() print("round 1 : ",positive0) positive1 = ', '.join(insert_spaces(filtered_keywords_positive1, filtered_keywords)).strip() print("round 2 : ",positive1) positive2 = ', '.join(insert_spaces(filtered_keywords_positive2, filtered_keywords)).strip() print("round 3 : ",positive2) positive3 = ', '.join(filtered_keywords).strip() print("round 4 : ",positive3) elif(', penis' in positive): if(', oral' in positive): pass elif(', paizuri' in positive): pass elif('job,' in positive): if('handjob' in positive): pass elif('footjob' in positive): pass return positive0, positive1, positive2, positive3 positive0, positive1, positive2, positive3 = SELECT(text_output.get("1.0", tk.END)); if not hold_seed_var.get(): seed = random.randint(0,9999999999) else: seed = last_generation_seed sleep0 = random.uniform(13.5,19.5) sleep1 = sleep0 + random.uniform(13.5,19.5) sleep2 = sleep1 + random.uniform(13.5,19.5) sleep3 = sleep2 + random.uniform(13.5,19.5) global auto_count_left_flag, auto_count_left, turbo_stop_bit if(auto_count_left_flag and auto_count_left > 0): auto_count_left -= 5 thread_init = threading.Thread(target=generate_turbo, args=(seed,turbo_count, access_token, width, height, positive, negative, button, 0, 0)) thread_init.start() thread0 = threading.Thread(target=generate_turbo, args=(seed,turbo_count, access_token, width, height, positive0, negative, button, 1, sleep0)) thread0.start() thread1 = threading.Thread(target=generate_turbo, args=(seed,turbo_count, access_token, width, height, positive1, negative, button, 2, sleep1)) thread1.start() thread2 = threading.Thread(target=generate_turbo, args=(seed,turbo_count, access_token, width, height, positive2, negative, button, 3, sleep2)) thread2.start() thread3 = threading.Thread(target=generate_turbo, args=(seed,turbo_count, access_token, width, height, positive3, negative, button, 4, sleep3)) thread3.start() turbo_count += 1 def generate_turbo(_seed, turbo_count, tb_access_token,width, height, positive, negative, button, pipe_number, sleeptime): global temp_clipboard_image global current_sampler global NAI_width, NAI_height global running_flag global image_queue global turbo_stop_bit global task_finished if(pipe_number == 0): image_queue.clear() try: scale = float(entry_CFG_value.get()) except: scale = 5.0 if scale > 7.0: scale = 7.0 elif scale < 4.0: scale = 4.0 else: scale = round(scale, 1) try: scale2 = float(entry_rescale_value.get()) except: scale2 = 0 if scale2 > 1.00: scale = 1.00 elif scale2 < 0.0: scale = 0.00 else: scale2 = round(scale2, 2) params = { "legacy": False, "quality_toggle": False, "width": width, "height": height, "n_samples": 1, "seed": _seed, "extra_noise_seed": random.randint(0,9999999999), "sampler": current_sampler, "steps": 28, "scale": scale, "uncond_scale": 1.0, "negative_prompt": negative, "sm" : sema_button_var.get(), "sm_dyn" : dyn_button_var.get(), "decrisper": False, "controlnet_strength": 1.0, "add_original_image": False, "cfg_rescale": scale2, "noise_schedule": "native", } if (hold_seed_var.get()): global last_generation_seed params['seed'] = last_generation_seed def merge_images(image_queue): if len(image_queue) < 4: return None # 이미지가 4개 미만인 경우 None을 반환 # 모든 이미지의 크기를 동일하게 조정 # 예시를 위해 첫 번째 이미지의 크기를 사용합니다. 필요에 따라 수정 가능 width, height = image_queue[0].size new_image = Image.new('RGB', (width * 2, height * 2)) # 이미지 위치 매핑 positions = [(0, 0), (width, 0), (0, height), (width, height)] # 이미지 병합 for i, img in enumerate(image_queue): pos = positions[i] new_image.paste(img, pos) return new_image def insert_queue(i, _number, turbo_count): global image_queue, temp_clipboard_image while len(image_queue) < 4: image_queue.append(None) if _number <= 4: image_queue[_number-1] = i.copy() i_resized = resize_and_fill(i) box_position = (i_resized.width - 25, 10) draw = ImageDraw.Draw(i_resized) draw.rectangle([box_position, (box_position[0] + 25, box_position[1] + 25)], fill="black") font = ImageFont.load_default() text_position = (box_position[0] + 12, box_position[1] + 12) # 텍스트 위치 조정 draw.text(text_position, str(_number), fill="white", font=font) if 'tk_image' in globals(): globals()['tk_image'] = None tk_image = ImageTk.PhotoImage(i_resized) image_label.config(image=tk_image) image_label.original_image = i.copy() image_label.image = tk_image # 참조 유지 history_queue(i_resized.copy(), positive, params['seed']) if all(img is not None for img in image_queue): time.sleep(5) merged_image = merge_images(image_queue) temp_clipboard_image = merged_image.copy() if merged_image is not None: output_directory = Path(f"output_NAI/{start_time}/turbo/grid") output_directory.mkdir(parents=True, exist_ok=True) file_path = output_directory / f"{turbo_count:05}.png" merged_image.save(file_path) i_resized = resize_and_fill(merged_image) if 'tk_image' in globals(): globals()['tk_image'] = None tk_image = ImageTk.PhotoImage(i_resized) image_label.config(image=tk_image) image_label.original_image = i_resized.copy() image_label.image = tk_image # 참조 유지 output_file_path = "output_image.jpg" merged_image.save(output_file_path) image_queue = [] # 이미지 큐 초기화 def resize_and_fill(image, max_size=None): if max_size is None: max_size = get_max_size() original_width, original_height = image.size if original_width > max_size or original_height > max_size: # 비율을 유지하면서 크기 조정 image.thumbnail((max_size, max_size)) # 새 이미지 크기 계산 width, height = image.size new_image = Image.new("RGB", (max_size, max_size), "black") new_image.paste(image, ((max_size - width) // 2, (max_size - height) // 2)) return new_image else: return image def log_error(e, output_file_path="output_file_path"): # 현재 시간을 얻습니다 current_time = datetime.now().strftime("%m/%d %H:%M:%S") # 에러 로그 메시지 error_message = f"#### Error occured at {current_time} ####\nError: {e}\n############################################\n" # 지정된 출력 폴더의 error_log.txt 파일에 쓰기 with open(f"error_log.txt", "a") as file: file.write(error_message) if(pipe_number != 0): pipe_start_time = time.time() while time.time() - pipe_start_time < sleeptime: if(turbo_stop_bit): return time.sleep(1) while running_flag: time.sleep(1) print('running_flag : ',running_flag,'pipe_number : ',pipe_number, ' : already running ') if(turbo_stop_bit): return print('containing : ',pipe_number) running_flag = True tprint("Running : request received, pipeline : "+str(pipe_number)) global error_count try: zipped_bytes = generate_image(tb_access_token, positive, "nai-diffusion-3", "generate", params) if(pipe_number != 0): d = Path(f"output_NAI/{start_time}/turbo") d.mkdir(parents=True, exist_ok=True) else: d = Path(f"output_NAI/{start_time}/turbo/preprocessing") d.mkdir(parents=True, exist_ok=True) zipped = zipfile.ZipFile(io.BytesIO(zipped_bytes)) image_bytes = zipped.read(zipped.infolist()[0]) (d / f"{turbo_count:05}_{pipe_number}.png" ).write_bytes(image_bytes) i = Image.open(io.BytesIO(image_bytes)) i = ImageOps.exif_transpose(i).convert("RGB") error_count = 0 if(pipe_number != 0): if temp_clipboard_image is not None: temp_clipboard_image.close() if(pipe_number == 1 and len(image_queue) < 4): image_queue.clear() temp_clipboard_image = i insert_queue(i, pipe_number, turbo_count) tprint("image inserted, pipe number : "+str(pipe_number)) else: if temp_clipboard_image is not None: temp_clipboard_image.close() temp_clipboard_image = i i_resized = resize_and_fill(i) if 'tk_image' in globals(): globals()['tk_image'] = None tk_image = ImageTk.PhotoImage(i_resized) image_label.config(image=tk_image) image_label.original_image = i.copy() image_label.image = tk_image # 참조 유지 except Exception as e: #text_output.insert(tk.END, f"Error: {e}", fg='red') log_error(zipped_bytes.decode('utf-8')[2:-2], "path_to_output_folder") error_count += 1 if error_count > 5: mac_var.set(0) time.sleep(random.uniform(3.0, 9.0)) insert_queue(temp_clipboard_image, pipe_number, turbo_count) task_finished += 1 #터보 기능의 안전장치입니다. time.sleep(random.uniform(3.0, 5.5)) if(pipe_number == 4): button.config(state=tk.NORMAL) running_flag = False last_generation_seed = params['seed'] entry_seed_value.set(last_generation_seed) global delay_offset time.sleep(delay_offset) stopped = False if (turbo_stop_bit): time.sleep(1.5) turbo_stop_bit = False button.config(state=tk.NORMAL) stopped = True if (pipe_number != 4): tprint("Stopped : request stopped before assign pipeline :"+str(pipe_number+1)) if pipe_number == 4 and mac_var.get(): wait_limit = 0 while(task_finished < 5 and wait_limit < 20): wait_limit += 1 print(f"Wait other pipeline ... : ({wait_limit}/20)") time.sleep(3) if(wait_limit == 20): print(f"Process : {turbo_count} request time-out") task_finished = 0 else: task_finished = 0 running_flag = False random_function() time.sleep(random.uniform(2.1, 5.5)) if not (turbo_stop_bit): button.config(state=tk.DISABLED) window.event_generate(GENERATE_EVENT, when="tail") else: turbo_stop_bit = False if(mac_var.get() and not running_flag): button.config(state=tk.DISABLED) window.event_generate(GENERATE_EVENT, when="tail") else: if(pipe_number == 4): global button_stop button_stop.config(state='disabled') tprint('state : idle') def NAI_setting(button, button_generate): global NAI_ID NAI_setting_window = tk.Toplevel(window) NAI_setting_window_title = "NAI 로그인" def NAI_close(): NAI_setting_window.destroy() def NAI_token_save(button): global access_token with open('token.txt', 'w', encoding='utf-8') as f: f.write(access_token) NAI_State_label.config(text="(프로그램 경로에 token.txt가 저장되었습니다.)", fg="blue") button.config(state=tk.DISABLED) def NAI_connect(button, connect_button, token_button): global access_token, NAI_ID username = entry_NAI_ID.get().strip() password = entry_NAI_PW.get().strip() access_key = get_access_key(username, password) try: access_token = login(access_key) access_result = requests.get("https://api.novelai.net/user/information", headers={ "Authorization": f"Bearer {access_token}" }) NAI_State_label.config(text="(로그인 성공, 닫기 버튼을 눌러주세요.)", fg="blue") button.config(state=tk.DISABLED) button_generate.config(state="normal") connect_button.config(state=tk.DISABLED) NAI_ID = username token_button.config(state=tk.NORMAL) except Exception as e: print(e) NAI_close() button_frame0 = tk.Frame(NAI_setting_window) button_frame0.grid(row=0, padx=5, pady=5, sticky='ew') button_frame1 = tk.Frame(button_frame0) button_frame1.grid(row=0, padx=5, pady=5, sticky='ew') button_frame1.columnconfigure(0, weight=1) button_frame1.columnconfigure(1, weight=3) button_frame1.columnconfigure(2, weight=3) button_frame1.columnconfigure(3, weight=1) NAI_ID_label = tk.Label(button_frame1, text="NAI ID: ") NAI_ID_label.grid(row=0, column=0, sticky='ew') entry_NAI_ID = tk.Entry(button_frame1) entry_NAI_ID.grid(row=0, column=1, columnspan=3, padx=5, pady=5, sticky='ew') if(NAI_ID): entry_NAI_ID.insert(tk.END,NAI_ID) NAI_PW_label = tk.Label(button_frame1, text="NAI PW: ") NAI_PW_label.grid(row=1, column=0,sticky='ew') entry_NAI_PW = tk.Entry(button_frame1, show="*") entry_NAI_PW.grid(row=1, column=1, columnspan=3,padx=5, pady=5, sticky='ew') token_output = tk.Button(button_frame0, text="로그인 정보 기억하기", command=lambda: NAI_token_save(token_output)) token_output.grid(row=2, column=1, padx=5, pady=5, sticky='ew') connect_button = tk.Button(button_frame0, text="연결", command=lambda: NAI_connect(button, connect_button, token_output)) connect_button.grid(row=2, column=0, padx=5, pady=5, sticky='ew') close_button = tk.Button(button_frame0, text="닫기", command=NAI_close) close_button.grid(row=2, column=2, padx=5, pady=5, sticky='ew') NAI_State_label = tk.Label(button_frame0, text="해당 접속기능은 정상적인 접속 패턴이 아닌점 참고 부탁드립니다.", fg="red") NAI_State_label.grid(row=3, column=0, sticky='ew') def NAI_setting_multi(button): global NAI_ID_multi NAI_setting_window_multi = tk.Toplevel(window) NAI_setting_window_title = "NAI 로그인" def NAI_close(): NAI_setting_window_multi.destroy() NAI_ID_label = tk.Label(NAI_setting_window_multi, text="NAI ID: ") NAI_ID_label.grid(row=0, column=0, columnspan=2, sticky='w') entry_NAI_ID = tk.Entry(NAI_setting_window_multi, width=50) entry_NAI_ID.grid(row=0, column=1, columnspan=2, padx=100, pady=5, sticky='e') if(NAI_ID_multi): entry_NAI_ID.insert(tk.END,NAI_ID_multi) NAI_PW_label = tk.Label(NAI_setting_window_multi, text="NAI PW: ") NAI_PW_label.grid(row=1, column=0, columnspan=2, sticky='w') entry_NAI_PW = tk.Entry(NAI_setting_window_multi, width=50, show="*") entry_NAI_PW.grid(row=1, column=1, columnspan=2, padx=100, pady=5, sticky='e') def NAI_connect(button, connect_button): global access_token_multi, NAI_ID, NAI_ID_multi username = entry_NAI_ID.get().strip() password = entry_NAI_PW.get().strip() access_key = get_access_key(username, password) try: access_token_multi = login(access_key) access_result = requests.get("https://api.novelai.net/user/information", headers={ "Authorization": f"Bearer {access_token}" }) if NAI_ID == NAI_ID_multi: NAI_State_label.config(text="(같은 계정으로 로그인 하셨습니다.)", fg="red") else: NAI_State_label.config(text="(로그인 성공, 닫기 버튼을 눌러주세요.)", fg="blue") button.config(state=tk.DISABLED) connect_button.config(state=tk.DISABLED) NAI_ID_multi = username except Exception as e: print(e) NAI_close() connect_button = tk.Button(NAI_setting_window_multi, text="연결", command=lambda: NAI_connect(button, connect_button)) connect_button.grid(row=2, column=1, columnspan=2, padx=5, pady=5, sticky='w') close_button = tk.Button(NAI_setting_window_multi, text="닫기", command=NAI_close) close_button.grid(row=2, column=2, columnspan=2, padx=5, pady=5, sticky='w') NAI_State_label = tk.Label(NAI_setting_window_multi, text="해당 기능은 NAI Opus 구독 계정 2개가 요구됩니다..", fg="red") NAI_State_label.grid(row=3, column=1, sticky='ew') def on_resolution_change(*args): global NAI_width, NAI_height resolution = selected_resolution.get() NAI_width, NAI_height = resolution.split(' x ') def on_option_select(value): global current_sampler current_sampler = value def open_file_explorer(): output_file_path = "output_NAI" if os.path.exists(output_file_path): os.startfile(output_file_path) def on_generate_event(event): global running_flag if not (running_flag): NAI_generation(NAI_width, NAI_height, button_generate) def random_artist_management(): global top_100_keywords, top_100_counts, cached_rows if not cached_rows: return if not (top_100_keywords): if not os.path.exists("counting_result.txt"): keyword_counts = analyze_cached_rows(cached_rows) excluded_keywords = set(whitelist[:2974]) with open("counting_result.txt", "w") as file: for keyword, count in sorted(keyword_counts.items(), key=lambda item: item[1], reverse=True): if keyword not in excluded_keywords and keyword in afilter_30000: file.write(f"{keyword}: {count}\n") with open("counting_result.txt", "r") as file: lines = file.readlines() top_100_data = [ (line.split(":")[0].strip(), int(line.split(":")[1].strip())) for line in lines[:3000] if line.split(":")[0].strip() in afilter_30000[:8528] ] top_100_keywords, top_100_counts = zip(*top_100_data) if top_100_data else ([], []) elif (not top_100_keywords): with open("counting_result.txt", "r") as file: lines = file.readlines() top_100_data = [ (line.split(":")[0].strip(), int(line.split(":")[1].strip())) for line in lines[:3000] if line.split(":")[0].strip() in afilter_30000[:8528] ] top_100_keywords, top_100_counts = zip(*top_100_data) if top_100_data else ([], []) # top_100_keywords 내의 # 새 창 생성 random_artist_window = tk.Toplevel(window) # 첫 번째 Text Output Box 설정 text_output1 = tk.Text(random_artist_window, height=40, width=50) text_output1.pack(padx=10, pady=10) for keyword, count in zip(top_100_keywords, top_100_counts): text_output1.insert(tk.END, f"{keyword}: {count}\n") def update_lists(): # Text 위젯에서 내용을 가져옵니다. content = text_output1.get("1.0", tk.END) lines = content.strip().split("\n") # 줄바꿈으로 구분하여 리스트를 생성합니다. # 갱신된 데이터를 저장할 두 개의 리스트를 생성합니다. updated_keywords = [] updated_counts = [] # 각 줄을 처리하여 리스트를 업데이트합니다. for line in lines: if line: # 빈 줄이 아닌 경우에만 처리합니다. parts = line.split(":") if len(parts) == 2: # "키워드: 카운트" 형식이 맞는지 확인합니다. updated_keywords.append(parts[0].strip()) updated_counts.append(int(parts[1].strip())) # 전역 리스트를 갱신합니다. global top_100_keywords, top_100_counts top_100_keywords = updated_keywords top_100_counts = updated_counts with open("random_artist_keywords_"+str(len(top_100_keywords))+".txt", "w") as f: for keyword in updated_keywords: f.write('100: artist:'+keyword + "\n") # 버튼들을 담을 프레임 생성 buttons_frame = tk.Frame(random_artist_window) buttons_frame.pack(pady=10) # '저장' 버튼을 프레임의 왼쪽에 배치 save_button = tk.Button(buttons_frame, text="저장", command=update_lists) save_button.pack(side=tk.LEFT, padx=5) # '닫기' 버튼을 프레임의 오른쪽에 배치 close_button = tk.Button(buttons_frame, text="닫기", command=random_artist_window.destroy) close_button.pack(side=tk.RIGHT, padx=5) random_artist_label = tk.Label(random_artist_window, text='작가이름 총 '+str(len(top_100_keywords))+'개, 저장위치: exe파일 위치') random_artist_label.pack() def open_wildcard_setting(): global artist_wildcard_saved, character_wildcard_saved # 새 창 생성 wildcard_window = tk.Toplevel(window) wildcard_window.title("와일드 카드 설정") # 작가명 파일 불러오기 artist_label = tk.Label(wildcard_window, text="작가명 파일 불러오기 ->") artist_label.grid(row=0, column=0) artist_button = tk.Button(wildcard_window, text="열기", command=lambda: load_wildcard(artist_text, wildcard_window, 'artist')) artist_button.grid(row=0, column=1) artist_text = tk.Text(wildcard_window, height=15, width=60) artist_text.grid(row=1, column=0, columnspan=2) if artist_wildcard_saved: artist_text_content = '\n'.join(artist_wildcard_saved) artist_text.insert('1.0', artist_text_content) # 캐릭터 파일 불러오기 character_label = tk.Label(wildcard_window, text="캐릭터 파일 불러오기 ->") character_label.grid(row=2, column=0) character_button = tk.Button(wildcard_window, text="열기", command=lambda: load_wildcard(character_text, wildcard_window, 'character')) character_button.grid(row=2, column=1) character_text = tk.Text(wildcard_window, height=15, width=60) character_text.grid(row=3, column=0, columnspan=2) if character_wildcard_saved: character_text_content = '\n'.join(character_wildcard_saved) character_text.insert('1.0', character_text_content) # 버튼들 buttons_frame = tk.Frame(wildcard_window) buttons_frame.grid(row=4, column=0, columnspan=2) text_label = tk.Label(wildcard_window, text="wildcard 문법(작가):\noffset:wildcard1 150:null (helloworld)\noffset:wildcard2 100:none (goodbye)\n없으면 100: 으로 간주\noffset/offset 총합: wildcard 확률") text_label.grid(row=5, column=0,sticky='ew') text_label2 = tk.Label(wildcard_window, text="wildcard 문법(캐릭):\n200:{{arona (blue archive)}}, blue hair, blue eyes, hair over one eye, ...\n150:{{nahida (genshin impact)}}, green eyes, pointy ears, ...\n...") text_label2.grid(row=6, column=0,sticky='ew') load_button = tk.Button(buttons_frame, text="와일드카드 탑재", command=lambda: apply_wildcard(artist_text, character_text, wildcard_window)) load_button.pack(side=tk.LEFT, padx=10) close_button = tk.Button(buttons_frame, text="닫기", command=wildcard_window.destroy) close_button.pack(side=tk.RIGHT, padx=10) def apply_wildcard(artist_text, character_text, window): global artist_wildcard_saved artist_wildcard_check = artist_text.get("1.0", tk.END) if len(artist_wildcard_check) > 5: lines = artist_wildcard_check.strip().split("\n") artist_wildcard_saved = lines.copy() akeywords = [] current_index = 0 for line in lines: parts = line.strip().split(':', 1) if len(parts) == 2 and parts[0].strip().isdigit(): weight = int(parts[0].strip()) else: weight = 100 parts = ['100', line.strip()] # '100:'을 앞에 추가합니다. keyword = parts[1].strip() akeywords.append((current_index, current_index + weight - 1, keyword)) current_index += weight global artist_wildcard artist_wildcard = akeywords check_wildcard.config(state='normal') character_wildcard_check = character_text.get("1.0", tk.END) global character_wildcard_saved if len(character_wildcard_check) > 5: lines = character_wildcard_check.strip().split("\n") character_wildcard_saved = lines.copy() ckeywords = [] current_index = 0 for line in lines: parts = line.strip().split(':', 1) if len(parts) == 2 and parts[0].strip().isdigit(): weight = int(parts[0].strip()) else: weight = 100 parts = ['100', line.strip()] # '100:'을 앞에 추가합니다. keyword = parts[1].strip() ckeywords.append((current_index, current_index + weight - 1, keyword)) current_index += weight global character_wildcard character_wildcard = ckeywords check_wildcard2.config(state='normal') window.destroy() def load_wildcard(text_widget, window, _type): global artist_wildcard_saved, character_wildcard_saved window.iconify() filepath = filedialog.askopenfilename(filetypes=[("텍스트 파일", "*.txt")]) if filepath: with open(filepath, 'r', encoding='utf-8') as file: lines = file.readlines() updated_lines = [] for line in lines: line = line.strip() if ':' in line: prefix, _ = line.split(':', 1) if not prefix.strip().isdigit(): line = f"100: {line}" else: line = f"100: {line}" updated_lines.append(line) text_widget.delete('1.0', tk.END) text_widget.insert('1.0', '\n'.join(updated_lines)) if(_type == 'artist'): artist_wildcard_saved = updated_lines.copy() elif(_type == 'character'): character_wildcard_saved = updated_lines.copy() window.deiconify() def find_keyword(index, keywords): for start, end, keyword in keywords: if start <= index <= end: return keyword return None def get_random_keyword(req_keyword): #called from artist_wildcard, character_wildcard global artist_wildcard, character_wildcard if(req_keyword == 'artist'): keywords = artist_wildcard elif(req_keyword == 'character'): keywords = character_wildcard print('lenghth = ',len(keywords)) max_index = keywords[-1][1] random_index = random.randint(0, max_index) return find_keyword(random_index, keywords) def tprint(*args): ctext = "" color = None for t in args: if(type(t) is str and 'color=' in t): color = t[6:] else: ctext += str(t) if not color: running_state.config(text=ctext, fg='black') else: running_state.config(text=ctext, fg='red') def show_automation_option(): global auto_thread, stop_event top = tk.Toplevel(window) top.title("Automation Stop Condition") global var, timer_label, timer_entry, count_label, count_entry var = tk.StringVar(value="unlimited") def show_option(): if var.get() == 'timer': timer_label.grid(row=4, column=0) timer_entry.grid(row=4, column=1) count_label.grid_forget() count_entry.grid_forget() elif var.get() == 'count': count_label.grid(row=4, column=0) count_entry.grid(row=4, column=1) timer_label.grid_forget() timer_entry.grid_forget() else: timer_label.grid_forget() timer_entry.grid_forget() count_label.grid_forget() count_entry.grid_forget() label_time = tk.Label(top, text="이미지 생성당 지연시간 추가 (초)") label_time.grid(row=0, column=0, columnspan=3) delay_entry = tk.Entry(top) delay_entry.grid(row=1, column=0, columnspan=3) label = tk.Label(top, text="자동화 종료 조건") label.grid(row=2, column=0, columnspan=3) radio1 = tk.Radiobutton(top, text="무제한", variable=var, value="unlimited", command=show_option) radio1.grid(row=3, column=0) radio2 = tk.Radiobutton(top, text="타이머", variable=var, value="timer", command=show_option) radio2.grid(row=3, column=1) radio3 = tk.Radiobutton(top, text="생성 카운트", variable=var, value="count", command=show_option) radio3.grid(row=3, column=2) timer_label = tk.Label(top, text="자동화 동작 시간(분) :") timer_entry = tk.Entry(top) count_label = tk.Label(top, text="자동 생성 횟수:") count_entry = tk.Entry(top) # "A.설정" 창에서 버튼 클릭 이벤트 처리 def on_apply(top): global auto_time_left_flag, auto_time_left, auto_count_left_flag, auto_count_left, delay_offset, delay_offset_label selected_option = var.get() if delay_entry.get(): try: delay_offset = round(float(delay_entry.get()), 1) delay_offset_label.config(text="Dealy offset : "+str(delay_offset)) except ValueError as e: print(e) delay_offset = 0 else: delay_offset = 0 delay_offset_label.config(text="") if selected_option == "timer": auto_time_left_flag = True auto_count_left_flag = False try: # 엔트리에서 입력된 시간을 분 단위에서 초 단위로 변환 auto_time_left = int(timer_entry.get()) * 60 except ValueError: # 잘못된 입력 처리 print("Invalid input for timer. Please enter a number.") return elif selected_option == "count": auto_count_left_flag = True auto_time_left_flag = False try: auto_count_left = int(count_entry.get()) except ValueError: # 잘못된 입력 처리 print("Invalid input for count. Please enter a number.") return else: auto_time_left_flag = False auto_count_left_flag = False stop_auto_thread() # 이전 스레드 종료 start_auto_thread() # 새 스레드 시작 mac_var.set(1) top.destroy() apply_button = tk.Button(top, text="적용", command=lambda: on_apply(top)) stop_button = tk.Button(top, text="중단", command=on_stop) close_button = tk.Button(top, text="닫기", command=top.destroy) apply_button.grid(row=5, column=0, pady=5) stop_button.grid(row=5, column=1, pady=5) close_button.grid(row=5, column=2, pady=5) def auto_time_thread(stop_event): global auto_time_left, auto_time_left_flag def seconds_to_hms(seconds): h = seconds // 3600 m = (seconds % 3600) // 60 s = seconds % 60 return f"{h:02d}:{m:02d}:{s:02d}" def update_label_for_time_finished(): automation_state.config(text="지정한 시간이 모두 소진되어 자동화가 종료되었습니다.") auto_time_left_flag = False mac_var.set(0) while not stop_event.is_set() and auto_time_left > 0: time.sleep(1) auto_time_left -= 1 remaining_time = seconds_to_hms(auto_time_left) window.after(0, lambda: automation_state.config(text=f"자동화 남은시간 : {remaining_time}")) if auto_time_left <= 0: window.after(0, update_label_for_time_finished) if stop_event.is_set(): window.after(0, lambda: automation_state.config(text="Conditional automation not set (A.설정)")) auto_time_left_flag = False def auto_count_thread(stop_event): global auto_count_left, auto_count_left_flag def update_label_for_count_finished(): mac_var.set(0) # 자동화 종료 조건 충족 auto_count_left_flag = False automation_state.config(text="지정한 횟수가 모두 소진되어 자동화가 종료되었습니다.") while not stop_event.is_set(): time.sleep(1) # 주기적으로 확인 window.after(0, lambda: automation_state.config(text=f"자동화 남은횟수 : {auto_count_left}")) if auto_count_left <= 0: window.after(0, update_label_for_count_finished) break # 루프 탈출 if stop_event.is_set(): window.after(0, lambda: automation_state.config(text="Conditional automation not set (A.설정)")) mac_var.set(0) auto_count_left_flag = False # 스레드 시작 및 종료 함수 def start_auto_thread(): global auto_thread, stop_event stop_event = threading.Event() auto_thread = None if auto_time_left_flag: auto_thread = threading.Thread(target=auto_time_thread, args=(stop_event,)) elif auto_count_left_flag: auto_thread = threading.Thread(target=auto_count_thread, args=(stop_event,)) if auto_thread is not None: auto_thread.start() def stop_auto_thread(): global stop_event, auto_thread if stop_event is not None: stop_event.set() #if auto_thread is not None and auto_thread.is_alive(): # auto_thread.join() def on_stop(): global auto_thread, stop_event stop_auto_thread() # 스레드 종료 def on_hold_seed_button_click(): global last_generation_seed last_generation_seed = entry_seed_value.get() def update_fullscreen_image(new_window, new_image_label): last_updated_image = None while True: time.sleep(1) if not new_window.winfo_exists(): break try: current_image = getattr(image_label, 'original_image', None) if current_image and current_image != last_updated_image: resized_image = resize_image_to_fit(current_image, new_window.winfo_height()) tk_image = ImageTk.PhotoImage(resized_image) new_window.after(0, lambda img=tk_image: new_image_label.config(image=img)) new_image_label.image = tk_image last_updated_image = current_image except ValueError: print(ValueError) def show_fullscreen_image(): new_window = tk.Toplevel() new_window.attributes('-fullscreen', True) new_window.configure(bg='black') new_image_label = tk.Label(new_window, relief='solid', borderwidth=1, bg='black') new_image_label.pack(expand=True, fill='both') new_window.bind("", lambda e: new_window.destroy()) update_thread = threading.Thread(target=update_fullscreen_image, args=(new_window, new_image_label)) update_thread.daemon = True update_thread.start() def resize_image_to_fit(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 auto_time_left_flag = False auto_time_left = 0 auto_count_left_flag = False auto_count_left = 0 stop_event = None auto_thread = None delay_offset = 0 start_time = datetime.now().strftime('%Y%m%d_%H%M') thread_controller = False turbo_count = 0 on_press_flag = False window = tk.Tk() window.title("Prompt Selector for Danbooru tags") last_deep_search_keywords = None cached_rows = [] total_rows = 0 last_selected_row_keywords = ['1girl','nahida (genshin impact)', 'looking at viewer', 'garden'] top_100_keywords = [] top_100_counts = [] previous_artist = None listener = None current_keys = set() click_count = 0 access_token = None temp_clipboard_image = None NAI_width = 1024 NAI_height = 1024 current_sampler = "k_euler_ancestral" running_flag = False NAI_ID = None artist_wildcard = [] character_wildcard_saved = [] artist_wildcard_saved = [] character_wildcard =[] access_token_multi = None NAI_ID_multi = None image_queue = [] last_generation_seed = random.randint(0,9999999999) turbo_stop_bit = False task_finished = 0 error_count = 0 multi_token_enable = False whitelist = wlist.whitelist bag_of_tags = tagbag.bag_of_tags afilter_30000 = arti_list.afilter_30000 history = [] current_wildcard_artist = [] current_wildcard_character = [] previous_wildcard_artist = None previous_wildcard_character = None GENERATE_EVENT = "<>" window.bind(GENERATE_EVENT, on_generate_event) left_frame = tk.Frame(window) left_frame.grid(row=0, column=0, sticky="nsew") #left_frame.grid_columnconfigure(0, weight=1) right_frame = tk.Frame(window) right_frame.grid(row=0, column=1, sticky="nsew") window.grid_columnconfigure(0, weight=1) window.grid_columnconfigure(1, weight=1) #파일 경로 및 키워드 입력창 그리드 관리 frame_row0 = tk.Frame(left_frame) frame_row0.grid(row=0, column=0, padx=5, pady=5, sticky='w') # 파일 경로 입력창 label_file_path = tk.Label(frame_row0, text="CSV 파일 경로:") label_file_path.grid(row=0, column=0, sticky='w') entry_file_path = tk.Entry(frame_row0, width=55) entry_file_path.grid(row=0, column=1, columnspan=4, padx=5, pady=5, sticky='ew') button_open_file = tk.Button(frame_row0, text="파일 열기", command=open_file) button_open_file.grid(row=0, column=5, padx=5, pady=6, sticky='e') # 키워드 입력창 keyword_label = tk.Label(frame_row0, text="초기화 키워드: ") keyword_label.grid(row=1, column=0, sticky='w') entry_keyword = tk.Entry(frame_row0) entry_keyword.grid(row=1, column=1, columnspan=4, padx=5, pady=5, sticky='ew') button_export_csv = tk.Button(frame_row0, text=".csv 내보내기", command=export_csv) button_export_csv.grid(row=1, column=5, padx=5, pady=6, sticky='e') # 버튼 프레임 frame_buttons = tk.Frame(left_frame) frame_buttons.grid(row=1, column=0, sticky='ew') frame_buttons.columnconfigure(0, weight=1) # 왼쪽에 가중치 부여 frame_buttons.columnconfigure(1, weight=0) # 버튼 열은 가중치 없음 frame_buttons.columnconfigure(2, weight=0) # 버튼 열은 가중치 없음 frame_buttons.columnconfigure(3, weight=1) # 오른쪽에 가중치 부여 # 버튼들 button_search = tk.Button(frame_buttons, text="해당 키워드 검색", command=search) button_search.grid(row=0, column=1, padx=5, sticky='ew') # 변경: column=1 button_exclude = tk.Button(frame_buttons, text="해당 키워드 제외", command=exclude) button_exclude.grid(row=0, column=2, padx=5, sticky='ew') # 변경: column=2 total_rows_count_label = tk.Label(frame_buttons, text=".csv 내 프롬프트 행: 0") total_rows_count_label.grid(row=0, column=3, padx=5, sticky='ew') # 심층검색 키워드 입력창 deep_search_frame = tk.Frame(left_frame) deep_search_frame.grid(row=2, column=0, pady=10, sticky='w') deep_search_frame.columnconfigure(0, weight=2) deep_search_frame.columnconfigure(1, weight=2) deep_search_frame.columnconfigure(2, weight=1) label_deep_search = tk.Label(deep_search_frame, text="심층검색 키워드 입력: key,*key,~key,{key1|key2} ") label_deep_search.grid(row=0, column=0, sticky='w') # Tkinter UI 설정 부분에 레이블 추가 cached_rows_count_label = tk.Label(deep_search_frame, text="심층검색 프롬프트 행: 0") cached_rows_count_label.grid(row=0, column=1, padx=5, sticky='w') button_export_deep_csv = tk.Button(deep_search_frame, text=".csv 내보내기", command=export_csv_search) button_export_deep_csv.grid(row=0, column=2, padx=5, sticky='e') entry_deep_search = tk.Entry(deep_search_frame, width=82) entry_deep_search.grid(row=1, column=0, columnspan=3,padx=5, pady=5, sticky='ew') # 버튼들을 포함할 프레임 생성 button_frame = tk.Frame(left_frame) button_frame.grid(row=5, column=0, padx=5, sticky='w') # 버튼 프레임 내의 열 설정 button_frame.columnconfigure(0, weight=1) button_frame.columnconfigure(1, weight=1) button_frame.columnconfigure(2, weight=1) button_frame.columnconfigure(3, weight=1) button_frame.columnconfigure(4, weight=1) button_frame.columnconfigure(5, weight=1) button_frame.columnconfigure(6, weight=1) # "랜덤" 버튼 button_random = tk.Button(button_frame, text="랜덤", command=random_function) button_random.grid(row=0, column=0, sticky='ew') # "프롬프트 고정" 토글 버튼 button_frame_frame = tk.Frame(button_frame) button_frame_frame.grid(row=0, column=1, sticky='ew') toggle_prompt_var = tk.IntVar() button_toggle_prompt = tk.Checkbutton(button_frame_frame, text="프롬프트 고정", variable=toggle_prompt_var) button_toggle_prompt.grid(row=0, column=0, sticky='ew') safe_search_var = tk.IntVar() button_toggle_safe_search = tk.Checkbutton(button_frame_frame, text="세이프서치", variable=safe_search_var) button_toggle_safe_search.grid(row=1, column=0, sticky='ew') # "자동 복사" 체크박스 auto_copy_var = tk.IntVar() check_auto_copy = tk.Checkbutton(button_frame, text="자동 복사", variable=auto_copy_var) check_auto_copy.grid(row=0, column=2, sticky='ew') # "종료" 버튼 button_exit = tk.Button(button_frame, text="종료", command=exit_program) button_exit.grid(row=0, column=3, sticky='ew') # "생성" 버튼 button_generate = tk.Button(button_frame, text="NAI 요청", command=lambda: NAI_generation(NAI_width, NAI_height, button_generate)) button_generate.grid(row=0, column=4, sticky='ew') button_generate.config(state='disabled') # "중단"버튼 button_stop = tk.Button(button_frame, text="중단",command=lambda: turbo_stop(button_stop)) button_stop.grid(row=0, column=5, sticky='ew') button_stop.config(state='disabled') # "설정" 버튼 button_setting = tk.Button(button_frame, text="NAI 설정", command=lambda: NAI_setting(button_setting, button_generate)) button_setting.grid(row=0, column=6, sticky='ew') # '자동화'와 '터보on'을 포함할 서브 프레임 생성 sub_frame = tk.Frame(button_frame) sub_frame.grid(row=0, column=7, sticky='ns') # 서브 프레임을 열 8에 배치 # '자동화' 체크박스를 서브 프레임에 배치 mac_var = tk.IntVar() mac_button = tk.Checkbutton(sub_frame, text="자동화", variable=mac_var) mac_button.grid(row=0, column=0, sticky='ew') # 첫 번째 행에 배치 # '터보on' 체크박스를 서브 프레임에 배치 turbo_var = tk.IntVar() turbo_button = tk.Checkbutton(sub_frame, text="터보on", variable=turbo_var) turbo_button.grid(row=1, column=0, sticky='ew') # 두 번째 행에 배치 # "A.설정" 버튼 button_auto_setting = tk.Button(button_frame, text="A.설정", command=show_automation_option) button_auto_setting.grid(row=0, column=8, sticky='ew') # 텍스트 및 고정 프롬프트 네거티브 등 프롬프트 창 text_frame = tk.Frame(left_frame) text_frame.grid(row=6, column=0, padx=5, pady=5, sticky='w') text_frame.columnconfigure(0, weight=1) text_frame.columnconfigure(1, weight=7) text_frame2 = tk.Frame(left_frame) text_frame2.grid(row=8, column=0, padx=5, pady=5, sticky='w') text_frame2.columnconfigure(0, weight=1) text_frame2.columnconfigure(1, weight=7) _size = get_max_size() # 출력 텍스트 창 text_output_label = tk.Label(text_frame, text="프롬프트", borderwidth=1, relief="solid",height=6) text_output_label.grid(row=0, column=0, padx=5, pady=5, sticky='w') if _size<=768: text_output_width = 74 else: text_output_width = 65 text_output = tk.Text(text_frame, width=text_output_width, height=7) text_output.grid(row=0, column=1, sticky='ew') fixed_prompt_frame = tk.Frame(left_frame) fixed_prompt_frame.grid(row=7, column=0, padx=5, pady=5, sticky='w') fixed_prompt_frame.columnconfigure(0, weight=1) fixed_prompt_frame.columnconfigure(1, weight=1) fixed_prompt_label_left = tk.Label(fixed_prompt_frame, text="선행 고정 프롬프트 (프롬프트 앞)") fixed_prompt_label_left.grid(row=0, column=0, sticky='ew') fixed_prompt_label_right = tk.Label(fixed_prompt_frame, text="후행 고정 프롬프트 (프롬프트 뒤)") fixed_prompt_label_right.grid(row=0, column=1, sticky='ew') entry_fixed_prompt = tk.Entry(fixed_prompt_frame, width=40) entry_fixed_prompt.grid(row=1, column=0,padx=5, pady=5, sticky='ew') entry_fixed_prompt_after = tk.Entry(fixed_prompt_frame, width=40) entry_fixed_prompt_after.grid(row=1, column=1, padx=5, pady=5, sticky='ew') # 네거티브 프롬프트 NP_label = tk.Label(text_frame2, text="네거티브\n프롬프트", borderwidth=1, relief="solid",height=2) NP_label.grid(row=0,column=0, padx=5, pady=5, sticky='ew') negative_text = tk.Text(text_frame2, width=65, height=3) negative_text.grid(row=0, column=1, padx=5, pady=5, sticky='w') auto_hide_frame = tk.Frame(left_frame) auto_hide_frame.grid(row=9, column=0, padx=5, pady=5, sticky='w') auto_hide_frame.columnconfigure(0, weight=2) auto_hide_frame.columnconfigure(1, weight=2) auto_hide_frame.columnconfigure(2, weight=1) # 자동 숨김 키워드 라벨과 입력창 추가 auto_hide_label = tk.Label(auto_hide_frame, text="자동 숨김 키워드: keyword1, keyword2, ...") auto_hide_label.grid(row=0, column=0, sticky='w') entry_auto_hide = tk.Entry(auto_hide_frame, width=82) entry_auto_hide.grid(row=1, column=0, columnspan=3, padx=5, pady=5, sticky='ew') # 추천 프롬프트 조회 버튼 설정 character_search_button = tk.Button(auto_hide_frame, text="캐릭터 검색", command=character_search_thread) character_search_button.grid(row=0, column=1, padx=2, pady=5, sticky='e') recommend_prompt_button = tk.Button(auto_hide_frame, text="추천 프롬프트", command=open_prompt_window) recommend_prompt_button.grid(row=0, column=2, padx=2, pady=5, sticky='e') # sticky 옵션을 'e'로 변경하여 오른쪽 정렬 # 체크박스들을 포함할 프레임 생성 checkbox_frame = tk.Frame(left_frame) checkbox_frame.grid(row=10, column=0, pady=15, sticky='ew') checkbox_frame2 = tk.Frame(left_frame) checkbox_frame2.grid(row=10, column=1, padx=5, pady=15, sticky='ew') # 체크박스 프레임 내의 열 설정 checkbox_frame.columnconfigure(0, weight=1) checkbox_frame.columnconfigure(1, weight=1) checkbox_frame.columnconfigure(2, weight=1) checkbox_frame.columnconfigure(3, weight=1) checkbox_frame.columnconfigure(4, weight=1) checkbox_frame.columnconfigure(5, weight=1) # "작가명 제거" 체크박스 remove_artist_var = tk.IntVar() # 체크박스 상태를 저장할 변수 check_remove_artist = tk.Checkbutton(checkbox_frame, text="작가명 제거", variable=remove_artist_var) check_remove_artist.grid(row=0, column=0, sticky='ew') # "랜덤 작가 추가" 체크박스 random_artist_var = tk.IntVar() check_random_artist = tk.Checkbutton(checkbox_frame, text="랜덤 작가 추가", variable=random_artist_var) check_random_artist.grid(row=0, column=1, sticky='ew') # 랜덤작가 관리 random_artist_setting = tk.Button(checkbox_frame, text="랜덤 작가 관리", command=random_artist_management) #output_file_path random_artist_setting.grid(row=0, column=2, sticky='ew') # "캐릭터 특징 제거" 체크박스 rm_characteristic_var = tk.IntVar() rm_characteristic_label = tk.Checkbutton(checkbox_frame, text="캐릭터 특징 제거", variable=rm_characteristic_var) rm_characteristic_label.grid(row=0, column=3, sticky='ew') # 와일드 카드 wildcard_var = tk.IntVar(value= 0) check_wildcard = tk.Checkbutton(checkbox_frame, text="와일드카드(작가)", variable=wildcard_var, state='disabled') check_wildcard.grid(row=1, column=0, sticky='ew') # 와일드 카드2 wildcard_var2 = tk.IntVar(value= 0) check_wildcard2 = tk.Checkbutton(checkbox_frame, text="와일드카드(캐릭터)", variable=wildcard_var2, state='disabled') check_wildcard2.grid(row=1, column=1, sticky='ew') # 와일드 카드 설정 widldcard_button = tk.Button(checkbox_frame, text="와일드카드 설정", command=open_wildcard_setting) #output_file_path widldcard_button.grid(row=1, column=2, sticky='ew') #폴더 호출 버튼 folder_button = tk.Button(checkbox_frame, text="저장 폴더", command=open_file_explorer) #output_file_path folder_button.grid(row=1, column=3, padx=5, sticky='ew') # "작품명 제거" 체크박스 rm_copyright_var = tk.IntVar() rm_copyright_label = tk.Checkbutton(checkbox_frame, text="작품명 제거", variable=rm_copyright_var) rm_copyright_label.grid(row=2, column=0, pady=5, sticky='ew') # "NSFW Only 체크박스 NSFW_assert_var = tk.IntVar() NSFW_assert_label = tk.Checkbutton(checkbox_frame, text="NSFW Only", variable=NSFW_assert_var) NSFW_assert_label.grid(row=2, column=1, pady=5, sticky='ew') window.bind('', on_ctrl_enter) window.bind('', on_shift_enter) NAI_ID = load_settings() if not (entry_keyword.get()): entry_keyword.insert(0, "1girl") if not (entry_fixed_prompt.get()): entry_fixed_prompt.insert(0, '1girl,') if not (entry_fixed_prompt_after.get()): entry_fixed_prompt_after.insert(0, 'great quality, aesthetic, absurdres') if not (entry_auto_hide.get()): entry_auto_hide.insert(0, 'monochrome, doujin cover, bad source, censored, bar censor') resolution1_frame = tk.Frame(left_frame) resolution1_frame.grid(row=16, column=0, padx=5, pady=5, sticky='ew') resolution2_frame = tk.Frame(left_frame) resolution2_frame.grid(row=17, column=0, padx=5, pady=5, sticky='ew') selected_resolution = tk.StringVar(value="1024 x 1024") tk.Radiobutton(resolution1_frame, text="1024 x 1024", variable=selected_resolution, value="1024 x 1024").grid(row=0, column=0, sticky='w') tk.Radiobutton(resolution1_frame, text="960 x 1088", variable=selected_resolution, value="960 x 1088").grid(row=0, column=1, sticky='w') tk.Radiobutton(resolution1_frame, text="896 x 1152", variable=selected_resolution, value="896 x 1152").grid(row=0, column=2, sticky='w') tk.Radiobutton(resolution1_frame, text="832 x 1216", variable=selected_resolution, value="832 x 1216").grid(row=0, column=3, sticky='w') tk.Radiobutton(resolution2_frame, text="1088 x 960", variable=selected_resolution, value="1088 x 960").grid(row=0, column=0, sticky='w') tk.Radiobutton(resolution2_frame, text="1152 x 896", variable=selected_resolution, value="1152 x 896").grid(row=0, column=1, sticky='w') tk.Radiobutton(resolution2_frame, text="1216 x 832", variable=selected_resolution, value="1216 x 832").grid(row=0, column=2, sticky='w') selected_resolution.trace_add('write', on_resolution_change) sema_button_var = tk.IntVar(value=1) sema_button = tk.Checkbutton(resolution1_frame, text="SEMA", variable=sema_button_var) sema_button.grid(row=0, column=5, sticky='w') dyn_button_var = tk.IntVar() dyn_button = tk.Checkbutton(resolution1_frame, text="+DYN", variable=dyn_button_var) dyn_button.grid(row=0, column=6, sticky='w') rand_resolution_var = tk.IntVar() rand_resolution_button = tk.Checkbutton(resolution1_frame, text="랜덤 해상도", variable=rand_resolution_var) rand_resolution_button.grid(row=0, column=7, sticky='w') entry_CFG_value = tk.StringVar() entry_CFG_label = tk.Label(resolution2_frame, text=" CFG Scale : ", justify=tk.LEFT) entry_CFG_label.grid(row=0, column=4, sticky='w') entry_CFG_value.set("5.0") entry_CFG = tk.Entry(resolution2_frame, width=5, textvariable=entry_CFG_value) entry_CFG.grid(row=0, column=5, sticky='w') options = ["k_euler", "k_euler_ancestral", "k_dpmpp_2s_ancestral", "k_dpmpp_sde"] selected_option = tk.StringVar() selected_option.set(options[1]) option_menu = tk.OptionMenu(resolution2_frame, selected_option, *options, command=on_option_select) option_menu.grid(row=0, column=6, padx=5, sticky='ew') resolution3_frame = tk.Frame(left_frame) resolution3_frame.grid(row=18, column=0, padx=5, pady=5, sticky='ew') resolution3_frame.columnconfigure(0, weight=2) resolution3_frame.columnconfigure(1, weight=1) resolution3_frame.columnconfigure(2, weight=1) running_state = tk.Label(resolution3_frame, text="state : idle") running_state.grid(row = 0, column= 0, sticky='w') automation_state = tk.Label(resolution3_frame, text="Conditional automation not set (A.설정)") automation_state.grid(row = 1, column= 0, sticky='w') delay_offset_label = tk.Label(resolution3_frame, text="Dealy offset : "+str(delay_offset)) delay_offset_label.grid(row = 1, column= 1, sticky='e') entry_rescale_value = tk.StringVar() entry_rescale_label = tk.Label(resolution3_frame, text=" Prompt Guidance Rescale : ", justify=tk.LEFT) entry_rescale_label.grid(row=0, column=1, sticky='e') entry_rescale_value.set("0.0") entry_rescale = tk.Entry(resolution3_frame, width=5, textvariable=entry_rescale_value) entry_rescale.grid(row=0, column=2, sticky='w') image_label = tk.Label(right_frame, relief='solid', borderwidth=1) image_label.grid(row=0, column=0, rowspan=15, padx=10, pady=10, sticky="n") right_frame_area16 = tk.Frame(right_frame) right_frame_area16.grid(row=16, column=0, padx=5, pady=5, sticky='ew') entry_seed_value = tk.IntVar() entry_seed_label = tk.Label(right_frame_area16, text=" seed : ", justify=tk.LEFT) entry_seed_label.grid(row=0, column=0, sticky='ew') entry_seed_value.set(last_generation_seed) entry_seed = tk.Entry(right_frame_area16, width=15, textvariable=entry_seed_value) entry_seed.grid(row=0, column=1, sticky='ew') hold_seed_var = tk.IntVar(value=0) hold_seed_button = tk.Checkbutton(right_frame_area16, text="시드고정", variable=hold_seed_var, command=on_hold_seed_button_click) hold_seed_button.grid(row=0, column=2, sticky='ew') btn_add_low_freq = tk.Button(right_frame_area16, text="이미지를 클립보드에 복사", fg="blue", command=copy_image_to_clipboard) btn_add_low_freq.grid(row=0, column=3, padx=5, pady=5, sticky='ew') show_fullscreen_btn = tk.Button(right_frame_area16, text="전체화면 보기 (ESC로 닫기)", command=show_fullscreen_image) show_fullscreen_btn.grid(row=0, column=4, padx=5, pady=5, sticky='ew') show_history_btn = tk.Button(right_frame_area16, text="history", command=show_history) show_history_btn.grid(row=0, column=5, padx=5, pady=5, sticky='ew') # 하얀색 이미지 생성 white_image = Image.new('RGB', (_size, _size), 'white') white_photo = ImageTk.PhotoImage(white_image) # 라벨에 이미지 설정 image_label.config(image=white_photo) image_label.image = white_photo Auto_login_check() window.protocol("WM_DELETE_WINDOW", exit_program) window.mainloop()