2024-01-02 00:31:06 +08:00
import dataclasses
2023-01-23 14:24:43 +08:00
import json
import html
import os
import platform
import sys
import gradio as gr
2023-01-23 20:50:32 +08:00
import subprocess as sp
2023-01-23 14:24:43 +08:00
from modules import call_queue , shared
2024-01-01 22:25:30 +08:00
from modules . infotext_utils import image_from_url_text
2023-01-23 14:24:43 +08:00
import modules . images
2023-06-01 03:40:09 +08:00
from modules . ui_components import ToolButton
2024-01-01 22:25:30 +08:00
import modules . infotext_utils as parameters_copypaste
2023-01-23 14:24:43 +08:00
folder_symbol = ' \U0001f4c2 ' # 📂
2023-06-01 03:40:09 +08:00
refresh_symbol = ' \U0001f504 ' # 🔄
2023-01-23 14:24:43 +08:00
def update_generation_info ( generation_info , html_info , img_index ) :
try :
generation_info = json . loads ( generation_info )
if img_index < 0 or img_index > = len ( generation_info [ " infotexts " ] ) :
return html_info , gr . update ( )
return plaintext_to_html ( generation_info [ " infotexts " ] [ img_index ] ) , gr . update ( )
except Exception :
pass
# if the json parse or anything else fails, just return the old html_info
return html_info , gr . update ( )
2023-07-15 13:07:25 +08:00
def plaintext_to_html ( text , classname = None ) :
content = " <br> \n " . join ( html . escape ( x ) for x in text . split ( ' \n ' ) )
return f " <p class= ' { classname } ' > { content } </p> " if classname else f " <p> { content } </p> "
2023-01-23 14:24:43 +08:00
def save_files ( js_data , images , do_make_zip , index ) :
import csv
filenames = [ ]
fullfns = [ ]
#quick dictionary to class object conversion. Its necessary due apply_filename_pattern requiring it
class MyObject :
def __init__ ( self , d = None ) :
if d is not None :
for key , value in d . items ( ) :
setattr ( self , key , value )
data = json . loads ( js_data )
p = MyObject ( data )
path = shared . opts . outdir_save
save_to_dirs = shared . opts . use_save_to_dirs_for_ui
extension : str = shared . opts . samples_format
start_index = 0
2023-05-24 23:17:02 +08:00
only_one = False
2023-01-23 14:24:43 +08:00
if index > - 1 and shared . opts . save_selected_only and ( index > = data [ " index_of_first_image " ] ) : # ensures we are looking at a specific non-grid picture, and we have save_selected_only
2023-05-24 23:17:02 +08:00
only_one = True
2023-01-23 14:24:43 +08:00
images = [ images [ index ] ]
start_index = index
os . makedirs ( shared . opts . outdir_save , exist_ok = True )
with open ( os . path . join ( shared . opts . outdir_save , " log.csv " ) , " a " , encoding = " utf8 " , newline = ' ' ) as file :
at_start = file . tell ( ) == 0
writer = csv . writer ( file )
if at_start :
writer . writerow ( [ " prompt " , " seed " , " width " , " height " , " sampler " , " cfgs " , " steps " , " filename " , " negative_prompt " ] )
for image_index , filedata in enumerate ( images , start_index ) :
image = image_from_url_text ( filedata )
is_grid = image_index < p . index_of_first_image
i = 0 if is_grid else ( image_index - p . index_of_first_image )
2023-05-24 23:17:02 +08:00
p . batch_index = image_index - 1
2023-01-23 14:24:43 +08:00
fullfn , txt_fullfn = modules . images . save_image ( image , path , " " , seed = p . all_seeds [ i ] , prompt = p . all_prompts [ i ] , extension = extension , info = p . infotexts [ image_index ] , grid = is_grid , p = p , save_to_dirs = save_to_dirs )
filename = os . path . relpath ( fullfn , path )
filenames . append ( filename )
fullfns . append ( fullfn )
if txt_fullfn :
filenames . append ( os . path . basename ( txt_fullfn ) )
fullfns . append ( txt_fullfn )
writer . writerow ( [ data [ " prompt " ] , data [ " seed " ] , data [ " width " ] , data [ " height " ] , data [ " sampler_name " ] , data [ " cfg_scale " ] , data [ " steps " ] , filenames [ 0 ] , data [ " negative_prompt " ] ] )
# Make Zip
if do_make_zip :
2023-05-24 23:17:02 +08:00
zip_fileseed = p . all_seeds [ index - 1 ] if only_one else p . all_seeds [ 0 ]
namegen = modules . images . FilenameGenerator ( p , zip_fileseed , p . all_prompts [ 0 ] , image , True )
zip_filename = namegen . apply ( shared . opts . grid_zip_filename_pattern or " [datetime]_[[model_name]]_[seed]-[seed_last] " )
zip_filepath = os . path . join ( path , f " { zip_filename } .zip " )
2023-01-23 14:24:43 +08:00
from zipfile import ZipFile
with ZipFile ( zip_filepath , " w " ) as zip_file :
for i in range ( len ( fullfns ) ) :
with open ( fullfns [ i ] , mode = " rb " ) as f :
zip_file . writestr ( filenames [ i ] , f . read ( ) )
fullfns . insert ( 0 , zip_filepath )
return gr . File . update ( value = fullfns , visible = True ) , plaintext_to_html ( f " Saved: { filenames [ 0 ] } " )
2024-01-02 00:31:06 +08:00
@dataclasses.dataclass
class OutputPanel :
gallery = None
2024-01-05 00:47:00 +08:00
generation_info = None
2024-01-02 00:31:06 +08:00
infotext = None
html_log = None
button_upscale = None
2023-11-06 00:19:55 +08:00
def create_output_panel ( tabname , outdir , toprow = None ) :
2024-01-02 00:31:06 +08:00
res = OutputPanel ( )
2023-01-23 14:24:43 +08:00
def open_folder ( f ) :
if not os . path . exists ( f ) :
print ( f ' Folder " { f } " does not exist. After you create an image, the folder will be created. ' )
return
elif not os . path . isdir ( f ) :
print ( f """
WARNING
An open_folder request was made with an argument that is not a folder .
This could be an error or a malicious attempt to run code on your computer .
Requested path was : { f }
""" , file=sys.stderr)
return
if not shared . cmd_opts . hide_ui_dir_config :
path = os . path . normpath ( f )
if platform . system ( ) == " Windows " :
os . startfile ( path )
elif platform . system ( ) == " Darwin " :
sp . Popen ( [ " open " , path ] )
elif " microsoft-standard-WSL2 " in platform . uname ( ) . release :
sp . Popen ( [ " wsl-open " , path ] )
else :
sp . Popen ( [ " xdg-open " , path ] )
2023-11-06 00:19:55 +08:00
with gr . Column ( elem_id = f " { tabname } _results " ) :
if toprow :
toprow . create_inline_toprow_image ( )
2023-01-23 14:24:43 +08:00
2023-11-06 00:19:55 +08:00
with gr . Column ( variant = ' panel ' , elem_id = f " { tabname } _results_panel " ) :
with gr . Group ( elem_id = f " { tabname } _gallery_container " ) :
2024-01-02 00:31:06 +08:00
res . gallery = gr . Gallery ( label = ' Output ' , show_label = False , elem_id = f " { tabname } _gallery " , columns = 4 , preview = True , height = shared . opts . gallery_height or None )
2023-11-06 00:19:55 +08:00
2023-03-21 13:18:14 +08:00
with gr . Row ( elem_id = f " image_buttons_ { tabname } " , elem_classes = " image-buttons " ) :
2023-08-13 00:03:33 +08:00
open_folder_button = ToolButton ( folder_symbol , elem_id = f ' { tabname } _open_folder ' , visible = not shared . cmd_opts . hide_ui_dir_config , tooltip = " Open images output directory. " )
2023-01-23 14:24:43 +08:00
if tabname != " extras " :
2023-08-13 00:03:33 +08:00
save = ToolButton ( ' 💾 ' , elem_id = f ' save_ { tabname } ' , tooltip = f " Save the image to a dedicated directory ( { shared . opts . outdir_save } ). " )
save_zip = ToolButton ( ' 🗃️ ' , elem_id = f ' save_zip_ { tabname } ' , tooltip = f " Save zip archive with images to a dedicated directory ( { shared . opts . outdir_save } ) " )
buttons = {
' img2img ' : ToolButton ( ' 🖼️ ' , elem_id = f ' { tabname } _send_to_img2img ' , tooltip = " Send image and generation parameters to img2img tab. " ) ,
' inpaint ' : ToolButton ( ' 🎨️ ' , elem_id = f ' { tabname } _send_to_inpaint ' , tooltip = " Send image and generation parameters to img2img inpaint tab. " ) ,
' extras ' : ToolButton ( ' 📐 ' , elem_id = f ' { tabname } _send_to_extras ' , tooltip = " Send image and generation parameters to extras tab. " )
}
2023-01-23 14:24:43 +08:00
2024-01-02 00:31:06 +08:00
if tabname == ' txt2img ' :
res . button_upscale = ToolButton ( ' ✨ ' , elem_id = f ' { tabname } _upscale ' , tooltip = " Create an upscaled version of the current image using hires fix settings. " )
2023-01-23 14:24:43 +08:00
open_folder_button . click (
fn = lambda : open_folder ( shared . opts . outdir_samples or outdir ) ,
inputs = [ ] ,
outputs = [ ] ,
)
if tabname != " extras " :
2023-03-27 15:20:01 +08:00
download_files = gr . File ( None , file_count = " multiple " , interactive = False , show_label = False , visible = False , elem_id = f ' download_files_ { tabname } ' )
2023-01-23 14:24:43 +08:00
with gr . Group ( ) :
2024-01-05 00:47:00 +08:00
res . infotext = gr . HTML ( elem_id = f ' html_info_ { tabname } ' , elem_classes = " infotext " )
2024-01-02 00:31:06 +08:00
res . html_log = gr . HTML ( elem_id = f ' html_log_ { tabname } ' , elem_classes = " html-log " )
2023-01-23 14:24:43 +08:00
2024-01-05 00:47:00 +08:00
res . generation_info = gr . Textbox ( visible = False , elem_id = f ' generation_info_ { tabname } ' )
2023-01-23 14:24:43 +08:00
if tabname == ' txt2img ' or tabname == ' img2img ' :
generation_info_button = gr . Button ( visible = False , elem_id = f " { tabname } _generation_info_button " )
generation_info_button . click (
fn = update_generation_info ,
_js = " function(x, y, z) { return [x, y, selected_gallery_index()] } " ,
2024-01-05 00:47:00 +08:00
inputs = [ res . generation_info , res . infotext , res . infotext ] ,
outputs = [ res . infotext , res . infotext ] ,
2023-03-20 21:09:36 +08:00
show_progress = False ,
2023-01-23 14:24:43 +08:00
)
save . click (
fn = call_queue . wrap_gradio_call ( save_files ) ,
_js = " (x, y, z, w) => [x, y, false, selected_gallery_index()] " ,
inputs = [
2024-01-05 00:47:00 +08:00
res . generation_info ,
2024-01-02 00:31:06 +08:00
res . gallery ,
2024-01-05 00:47:00 +08:00
res . infotext ,
res . infotext ,
2023-01-23 14:24:43 +08:00
] ,
outputs = [
download_files ,
2024-01-02 00:31:06 +08:00
res . html_log ,
2023-01-23 14:24:43 +08:00
] ,
show_progress = False ,
)
save_zip . click (
fn = call_queue . wrap_gradio_call ( save_files ) ,
_js = " (x, y, z, w) => [x, y, true, selected_gallery_index()] " ,
inputs = [
2024-01-05 00:47:00 +08:00
res . generation_info ,
2024-01-02 00:31:06 +08:00
res . gallery ,
2024-01-05 00:47:00 +08:00
res . infotext ,
res . infotext ,
2023-01-23 14:24:43 +08:00
] ,
outputs = [
download_files ,
2024-01-02 00:31:06 +08:00
res . html_log ,
2023-01-23 14:24:43 +08:00
]
)
else :
2024-01-05 00:47:00 +08:00
res . generation_info = gr . HTML ( elem_id = f ' html_info_x_ { tabname } ' )
res . infotext = gr . HTML ( elem_id = f ' html_info_ { tabname } ' , elem_classes = " infotext " )
2024-01-02 00:31:06 +08:00
res . html_log = gr . HTML ( elem_id = f ' html_log_ { tabname } ' )
2023-01-23 14:24:43 +08:00
2023-02-14 19:55:42 +08:00
paste_field_names = [ ]
if tabname == " txt2img " :
paste_field_names = modules . scripts . scripts_txt2img . paste_field_names
elif tabname == " img2img " :
paste_field_names = modules . scripts . scripts_img2img . paste_field_names
2023-01-30 05:25:30 +08:00
for paste_tabname , paste_button in buttons . items ( ) :
parameters_copypaste . register_paste_params_button ( parameters_copypaste . ParamBinding (
2024-01-02 00:31:06 +08:00
paste_button = paste_button , tabname = paste_tabname , source_tabname = " txt2img " if tabname == " txt2img " else None , source_image_component = res . gallery ,
2023-02-14 19:55:42 +08:00
paste_field_names = paste_field_names
2023-01-30 05:25:30 +08:00
) )
2024-01-02 00:31:06 +08:00
return res
2023-06-01 03:40:09 +08:00
def create_refresh_button ( refresh_component , refresh_method , refreshed_args , elem_id ) :
2023-08-04 03:46:57 +08:00
refresh_components = refresh_component if isinstance ( refresh_component , list ) else [ refresh_component ]
label = None
for comp in refresh_components :
label = getattr ( comp , ' label ' , None )
if label is not None :
break
2023-06-01 03:40:09 +08:00
def refresh ( ) :
refresh_method ( )
args = refreshed_args ( ) if callable ( refreshed_args ) else refreshed_args
for k , v in args . items ( ) :
2023-08-04 03:46:57 +08:00
for comp in refresh_components :
setattr ( comp , k , v )
2023-06-01 03:40:09 +08:00
2023-08-05 14:17:36 +08:00
return [ gr . update ( * * ( args or { } ) ) for _ in refresh_components ] if len ( refresh_components ) > 1 else gr . update ( * * ( args or { } ) )
2023-06-01 03:40:09 +08:00
2023-08-04 03:46:57 +08:00
refresh_button = ToolButton ( value = refresh_symbol , elem_id = elem_id , tooltip = f " { label } : refresh " if label else " Refresh " )
2023-06-01 03:40:09 +08:00
refresh_button . click (
fn = refresh ,
inputs = [ ] ,
2023-08-04 19:56:27 +08:00
outputs = refresh_components
2023-06-01 03:40:09 +08:00
)
return refresh_button
2023-08-04 03:46:57 +08:00
def setup_dialog ( button_show , dialog , * , button_close = None ) :
""" Sets up the UI so that the dialog (gr.Box) is invisible, and is only shown when buttons_show is clicked, in a fullscreen modal window. """
dialog . visible = False
button_show . click (
fn = lambda : gr . update ( visible = True ) ,
inputs = [ ] ,
outputs = [ dialog ] ,
2023-08-27 14:39:37 +08:00
) . then ( fn = None , _js = " function() { popupId( ' " + dialog . elem_id + " ' ); } " )
2023-08-04 03:46:57 +08:00
if button_close :
button_close . click ( fn = None , _js = " closePopup " )