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, ImageFilter,ImageTk from IPython.display import display import win32clipboard from io import BytesIO from datetime import datetime import arti_list, tagbag, wlist from ttkthemes import ThemedTk 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 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) 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": 0.0, "noise_schedule": "native", } 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 try: zipped_bytes = generate_image(access_token, positive, "nai-diffusion-3", "generate", params) d = Path("output_NAI") d.mkdir(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) 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(3.0, 9.0)) button.config(state=tk.DISABLED) window.event_generate(GENERATE_EVENT, when="tail") time.sleep(random.uniform(1.0, 2.5)) button.config(state=tk.NORMAL) running_flag = False 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") 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 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() print(whatthefuck) 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 print(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 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')) 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: random_row_keywords.remove(keyword) temp_rm.append(keyword) for keyword in random_row_keywords: if "(" in keyword: random_row_keywords.remove(keyword) temp_rm.append(keyword) print('removed : ',temp_rm) 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 = 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 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): 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 simulate_shortcuts(): global listener, on_press_flag on_press_flag = True random_function() on_press_flag = False try: current_keys.clear() current_keys.add(keyboard.Key.ctrl_l) except KeyError: pass def on_activate_simulate_shortcuts(): simulate_shortcuts() def for_canonical(f): return lambda k: f(listener.canonical(k)) def start_keyboard_listener(): global listener listener = keyboard.Listener(on_press=on_press, on_release=on_release) listener.start() def on_press(key): global on_press_flag current_keys.add(key) # Ctrl + ` 조합 감지 if keyboard.Key.ctrl_l in current_keys: if any(k.vk == 192 for k in current_keys if hasattr(k, 'vk')): on_press_flag = True simulate_shortcuts() def on_release(key): try: current_keys.remove(key) except KeyError: pass 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): 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_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() NAI_ID_label = tk.Label(NAI_setting_window, text="NAI ID: ") NAI_ID_label.grid(row=0, column=0, columnspan=2, sticky='w') entry_NAI_ID = tk.Entry(NAI_setting_window, width=50) entry_NAI_ID.grid(row=0, column=1, columnspan=2, padx=100, pady=5, sticky='e') if(NAI_ID): entry_NAI_ID.insert(tk.END,NAI_ID) NAI_PW_label = tk.Label(NAI_setting_window, text="NAI PW: ") NAI_PW_label.grid(row=1, column=0, columnspan=2, sticky='w') entry_NAI_PW = tk.Entry(NAI_setting_window, 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, 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 except Exception as e: print(e) NAI_close() connect_button = tk.Button(NAI_setting_window, 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, 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, text="해당 접속기능은 정상적인 접속 패턴이 아닌점 참고 부탁드립니다.", 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) running_flag = True 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(): # 새 창 생성 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_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) # 캐릭터 파일 불러오기 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_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) # 버튼들 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): artist_wildcard_check = artist_text.get("1.0", tk.END) if len(artist_wildcard_check) > 5: lines = artist_wildcard_check.strip().split("\n") 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) if len(character_wildcard_check) > 5: lines = character_wildcard_check.strip().split("\n") 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): 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)) window.deiconify() def find_keyword(index, keywords): for start, end, keyword in keywords: if start <= index <= end: print(keyword) 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] print(len(keywords)) random_index = random.randint(0, max_index) print(random_index) return find_keyword(random_index, keywords) 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 =[] 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, pady=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_copy = tk.Button(button_frame, text="복사", command=copy_to_clipboard) button_copy.grid(row=0, column=3, sticky='ew') # "종료" 버튼 button_exit = tk.Button(button_frame, text="종료", command=exit_program) button_exit.grid(row=0, column=4, 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=5, sticky='ew') button_generate.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') mac_var = tk.IntVar() mac_button = tk.Checkbutton(button_frame, text="자동생성", variable=mac_var) mac_button.grid(row=0, column=7, 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') # 추천 프롬프트 조회 버튼 설정 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') 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') 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') 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") # 저빈도 키워드 자동추가 버튼 추가 btn_add_low_freq = tk.Button(right_frame, text="이미지를 클립보드에 복사", fg="blue", command=copy_image_to_clipboard) btn_add_low_freq.grid(row=16, column=0, 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 # 리스너 시작 listener = keyboard.Listener(on_press=on_press, on_release=on_release) listener.start() # 키보드 리스너 스레드 시작 listener_thread = threading.Thread(target=listener.join, daemon=True) listener_thread.start() window.mainloop()