diff --git "a/Danbooru Prompt Selector/TEST2024/prompt_selector_auto_1212_testv8.py" "b/Danbooru Prompt Selector/TEST2024/prompt_selector_auto_1212_testv8.py" new file mode 100644--- /dev/null +++ "b/Danbooru Prompt Selector/TEST2024/prompt_selector_auto_1212_testv8.py" @@ -0,0 +1,2186 @@ +import tkinter as tk +import tkinter.ttk as ttk +from tkinter import filedialog +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 +import character_dictionary as cd +import pandas as pd + +BASE_URL="https://api.novelai.net" + +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 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, error_count + while (running_flag): + time.sleep(1) + try: + running_flag = True + tprint("Running : NAI request received") + zipped_bytes = generate_image(access_token, 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.image = tk_image # 참조 유지 + output_file_path = "output_image.jpg" + i.save(output_file_path) + error_count = 0 + except Exception as e: + #text_output.insert(tk.END, f"Error: {e}", fg='red') + log_error(e, "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) + + 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 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 + 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: + entry_auto_hide.delete(0, tk.END) + entry_auto_hide.insert(tk.END, 'monochrome, doujin cover, bad source, censored, bar censor, photoshop (medium)') + 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(exclude in 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 + + 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 = 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: + 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()) + if(wildcard_var2.get() == 1): + temp_len = len(girl_keywords) + girl_keywords.append(get_random_keyword('character')) + if(wildcard_var.get() == 1): + if(wildcard_var2.get() == 1): + girl_keywords.insert(temp_len+1,get_random_keyword('artist')) + else: + girl_keywords.append(get_random_keyword('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 "(" in keyword or "horns" in keyword: + temp_rm.append(keyword) + for keyword in temp_rm: + 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) + + #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 = [r'\b{}\b'.format(re.escape(keyword)) for keyword in auto_hide_keywords] + + for pattern in patterns: + random_row_keywords = [re.sub(pattern + '(, )?', '', keyword) for keyword in random_row_keywords] + random_row_keywords = [re.sub('(?<=, )' + pattern, '', keyword) for keyword in random_row_keywords] + + if entry_fixed_prompt_after.get(): + entry_after_text = entry_fixed_prompt_after.get().strip().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) + + random_row_keywords = [keyword.strip(', ') for keyword in random_row_keywords] + random_row_keywords = [keyword for keyword in random_row_keywords if keyword] + #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 ... ") + +def exit_program(): + 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): + #NAI_generation(NAI_width, NAI_height, button_generate) + pass + +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 on_release(key): + try: + current_keys.remove(key) + except KeyError: + pass + +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_count * 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}") + + # 메인 윈도우 생성 + 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) + + # right_frame 내부 구성 + prompt_label = tk.Label(right_frame, text="캐릭터 프롬프트:") + prompt_label.grid(row=0, column=0, 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" + cosplay_label = tk.Label(right_frame, text="캐릭터 (Cosplay):") + cosplay_label.grid(row=2, column=0, 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 + +def v_automatic(save_position, prompt_position): + # 프롬프트 창 클릭 + copy_to_clipboard() + pyautogui.click(prompt_position) + time.sleep(0.5) + random_function() + pyautogui.hotkey('ctrl', 'a', 'v','Right','Right','Enter') + time.sleep(0.5) + pyautogui.click(save_position) + +def start_automation(save_position, prompt_position, label, window_to_close, automation_event): + def automation_task(): + while automation_event.is_set(): + random_delay = random.uniform(17.5, 23.5) + next_click_time = time.time() + random_delay + while time.time() < next_click_time and automation_event.is_set(): + time_remaining = max(next_click_time - time.time(), 0) + label.config(text=f"다음 클릭까지: {time_remaining:.2f}초") + time.sleep(0.1) + if automation_event.is_set(): + v_automatic(save_position, prompt_position) + + automation_thread = threading.Thread(target=automation_task, daemon=True) + automation_thread.start() + random_function() + + def stop_automation(): + automation_event.clear() + window_to_close.destroy() + + window_to_close.protocol("WM_DELETE_WINDOW", stop_automation) + +def stop_automation(window_to_close, automation_event, start_button, stop_button): + automation_event.clear() # Stop the automation task + start_button['state'] = 'normal' # Enable the start button + stop_button['state'] = 'disabled' # Disable the stop button + window_to_close.destroy() # Close the automation window + window.deiconify() + +# 라벨을 업데이트하는 함수 +def update_label(label, text): + def task(): + label.config(text=text) + label.after(1000, task) # 메인 스레드에서 UI 업데이트 + +def open_automation_window(): + window.iconify() + automation_event = threading.Event() + automation_event.set() # Enable the automation event + auto_window = tk.Toplevel(window) + auto_window.title("자동화 설정") + auto_window.geometry("300x200") # 창 크기 조정 + auto_window.attributes('-topmost', True) # 창을 항상 맨 위에 위치 + auto_window.focus_force() # 창 열릴 때 포커스 강제 이동 + + save_position = None + prompt_position = None + automation_running = threading.Event() + + def get_mouse_position(event=None): + nonlocal save_position, prompt_position + x, y = pyautogui.position() + if not save_position: + save_position = (x, y) + position_label.config(text=f"저장 버튼의 좌표: {x}, {y}\n프롬프트 창의 좌표: ") + elif not prompt_position: + prompt_position = (x, y) + position_label.config(text=f"저장 버튼의 좌표: {save_position[0]}, {save_position[1]}\n프롬프트 창의 좌표: {x}, {y}") + start_button.config(state="normal") + if save_position and prompt_position: + start_button.config(state="normal") + stop_button.config(state="normal") + + + position_label = tk.Label(auto_window, text="엔터를 눌러 마우스 좌표를 저장하세요\n(클릭 X, 1.저장->2.프롬프트창 순서)", justify=tk.LEFT) + position_label.pack(pady=10) + + start_button = tk.Button(auto_window, text="시작", state="disabled", + command=lambda: start_automation(save_position, prompt_position, countdown_label, auto_window, automation_event)) + start_button.pack(side=tk.LEFT, fill='x', expand=True) + + stop_button = tk.Button(auto_window, text="중지", state="disabled", + command=lambda: stop_automation(auto_window, automation_event, start_button, stop_button)) + stop_button.pack(side=tk.RIGHT, fill='x', expand=True) + + countdown_label = tk.Label(auto_window, text="") + countdown_label.pack(pady=10) + + auto_window.bind('', get_mouse_position) + +def NAI_generation(width, height, button): + global access_token_multi, NAI_width, NAI_height + global button_stop, running_flag + + pretest = text_output.get("1.0", tk.END).split(', ') + if (turbo_var.get() == 1): + if ( 'sex' not in pretest or '1girl' not in pretest or '1boy' not in pretest): + tprint('현재 부스트 기능은 1girl, 1boy, sex 세가지 키워드가 있어야 동작합니다.') + 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() + if(access_token): button_setting.config(state='disabled') + if(access_token): button_generate.config(state='normal') + +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() + 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[:25] + 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 + + if(result["boys"]): + #group sex + if(result["girls"]): + pass + #gangbang + elif(result["1girl"]): + pass + + 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"]): + if(result["girls"]): + pass + elif(result["1girl"]): + if('sex,' in positive): + sex_pos_keywords = ['stomach bulge','insertion', 'fucked silly', 'x-ray', 'orgasm', 'cross-section', 'uterus', 'overflow', 'rape'] + 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' + key_index = filtered_keywords.index('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('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') + 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) + + 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): + 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.image = tk_image # 참조 유지 + 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: + 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.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 :",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) + tprint("image inserted, pipe number : ",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.image = tk_image # 참조 유지 + except Exception as e: + #text_output.insert(tk.END, f"Error: {e}", fg='red') + log_error(e, "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) + + 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) + + 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 :",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 = "" + for t in args: + ctext += str(t) + running_state.config(text=ctext) + +def on_hold_seed_button_click(): + global last_generation_seed + last_generation_seed = entry_seed_value.get() # entry_seed_value에서 값을 가져와 last_generation_seed에 저장 + +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 + +whitelist = wlist.whitelist +bag_of_tags = tagbag.bag_of_tags +afilter_30000 = arti_list.afilter_30000 + +current_wildcard_artist = [] +current_wildcard_character = [] + +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') + +# "프롬프트 고정" 토글 버튼 +toggle_prompt_var = tk.IntVar() +button_toggle_prompt = tk.Checkbutton(button_frame, text="프롬프트 고정", variable=toggle_prompt_var) +button_toggle_prompt.grid(row=0, column=1, 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') + +# "터보" 버튼 +#button_turbo = tk.Button(button_frame, text="터보", command=lambda: NAI_setting_multi(button_turbo)) +#button_turbo.grid(row=0, column=7, sticky='ew') + +# '자동화'와 '터보on'을 포함할 서브 프레임 생성 +sub_frame = tk.Frame(button_frame) +sub_frame.grid(row=0, column=8, 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') # 두 번째 행에 배치 + +# 텍스트 및 고정 프롬프트 네거티브 등 프롬프트 창 +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'로 변경하여 오른쪽 정렬 + +#character_search_button + +# 체크박스들을 포함할 프레임 생성 +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') + + +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=15, column=0, padx=5, pady=5, sticky='ew') +resolution2_frame = tk.Frame(left_frame) +resolution2_frame.grid(row=16, 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=17, 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') +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') + +# 하얀색 이미지 생성 +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.mainloop() \ No newline at end of file