From eb94a0937fa89aaeae71a8cb28d82267afb4ef3a Mon Sep 17 00:00:00 2001 From: roamindev <121050401+roamindev@users.noreply.github.com> Date: Tue, 4 Apr 2023 11:06:50 -0400 Subject: [PATCH] Add JS based button titles Add button titles on UI load. Use comments in aspect_ratios.txt and resolutions.txt to for user configured buttons, and hard-coded titles in sd-webui-ar.js for built-in buttons (i.e. for calculator). --- javascript/button_titles.js | 2 + javascript/sd-webui-ar.js | 20 +++ scripts/sd-webui-ar.py | 345 +++++++++++++++++++----------------- 3 files changed, 204 insertions(+), 163 deletions(-) create mode 100644 javascript/button_titles.js create mode 100644 javascript/sd-webui-ar.js diff --git a/javascript/button_titles.js b/javascript/button_titles.js new file mode 100644 index 0000000..ff732fe --- /dev/null +++ b/javascript/button_titles.js @@ -0,0 +1,2 @@ +// Do not put custom titles here. This file is overwritten each time the WebUI is started. +ar_button_titles = {} \ No newline at end of file diff --git a/javascript/sd-webui-ar.js b/javascript/sd-webui-ar.js new file mode 100644 index 0000000..97bd5e9 --- /dev/null +++ b/javascript/sd-webui-ar.js @@ -0,0 +1,20 @@ +if ("undefined" === typeof ar_button_titles) { + ar_button_titles = {}; +} + +ar_button_titles["Calc"] = "Show or hide the aspect ratio calculator"; +ar_button_titles["\u{21c5}"] = "Swap width and height values"; +ar_button_titles["\u2B07\ufe0f"] = "Get dimensions from txt2img/img2img sliders"; +ar_button_titles["\u{1f5bc}"] = "Get dimensions from image on current img2img tab"; +ar_button_titles["Calculate Height"] = "Calculate new height based on source aspect ratio"; +ar_button_titles["Calculate Width"] = "Calculate new width based on source aspect ratio"; +ar_button_titles["Apply"] = "Apply calculated width and height to txt2img/img2img sliders"; + +onUiUpdate(function(){ + gradioApp().querySelectorAll('#txt2img_container_aspect_ratio button, #img2img_container_aspect_ratio button').forEach(function(elem){ + tooltip = ar_button_titles[elem.textContent]; + if(tooltip){ + elem.title = tooltip; + } + }) +}) diff --git a/scripts/sd-webui-ar.py b/scripts/sd-webui-ar.py index a3ab038..040d823 100644 --- a/scripts/sd-webui-ar.py +++ b/scripts/sd-webui-ar.py @@ -12,10 +12,9 @@ aspect_ratios_dir = scripts.basedir() calculator_symbol = '\U0001F5A9' switch_values_symbol = '\U000021C5' -get_dimensions_symbol = '\U00002B07' +get_dimensions_symbol = '\u2B07\ufe0f' get_image_dimensions_symbol = '\U0001F5BC' - class ResButton(ToolButton): def __init__(self, res=(512, 512), **kwargs): super().__init__(**kwargs) @@ -110,10 +109,10 @@ def parse_resolutions_file(filename): # TODO: write a generic function handling both cases def write_aspect_ratios_file(filename): aspect_ratios = [ - "1:1, 1.0\n", - "3:2, 3/2\n", - "4:3, 4/3\n", - "16:9, 16/9", + "1:1, 1.0 # 1:1 ratio based on minimum dimension\n", + "3:2, 3/2 # Set width based on 3:2 ratio to height\n", + "4:3, 4/3 # Set width based on 4:3 ratio to height\n", + "16:9, 16/9 # Set width based on 16:9 ratio to height", ] with open(filename, "w", encoding="utf-8") as f: f.writelines(aspect_ratios) @@ -129,6 +128,20 @@ def write_resolutions_file(filename): f.writelines(resolutions) +def write_js_titles_file(button_titles): + filename = Path(aspect_ratios_dir + "\\javascript\\", "button_titles.js") + content = ["// Do not put custom titles here. This file is overwritten each time the WebUI is started.\n"] + content.append("ar_button_titles = {\n") + counter = 0 + while counter < len(button_titles[0]): + content.append(f" \"{button_titles[0][counter]}\" : \"{button_titles[1][counter]}\",\n") + counter = counter + 1 + content.append("}") + + with open(filename, "w", encoding="utf-8") as f: + f.writelines(content) + + def get_reduced_ratio(n, d): n, d = list(map(int, (n, d))) @@ -196,169 +209,175 @@ class AspectRatioScript(scripts.Script): return scripts.AlwaysVisible def ui(self, is_img2img): - self.read_aspect_ratios() - with gr.Row(elem_id=f'{"img" if is_img2img else "txt"}2img_row_aspect_ratio'): - # Toggle calculator display button - arc_show_calculator = gr.Button(value="Calc", visible=True, variant="secondary", elem_id="arc_show_calculator_button") - arc_hide_calculator = gr.Button(value="Calc", visible=False, variant="primary", elem_id="arc_hide_calculator_button") + with gr.Column(elem_id=f'{"img" if is_img2img else "txt"}2img_container_aspect_ratio'): + self.read_aspect_ratios() + with gr.Row(elem_id=f'{"img" if is_img2img else "txt"}2img_row_aspect_ratio'): + # Toggle calculator display button + arc_show_calculator = gr.Button(value="Calc", visible=True, variant="secondary", elem_id="arc_show_calculator_button") + arc_hide_calculator = gr.Button(value="Calc", visible=False, variant="primary", elem_id="arc_hide_calculator_button") - # Aspect Ratio buttons - btns = [ - ARButton(ar=ar, value=label) - for ar, label in zip( - self.aspect_ratios, - self.aspect_ratio_labels, - ) - ] - - with contextlib.suppress(AttributeError): - for b in btns: - if is_img2img: - resolution = [self.i2i_w, self.i2i_h] - else: - resolution = [self.t2i_w, self.t2i_h] - - b.click( - b.apply, - inputs=resolution, - outputs=resolution, + # Aspect Ratio buttons + btns = [ + ARButton(ar=ar, value=label) + for ar, label in zip( + self.aspect_ratios, + self.aspect_ratio_labels, ) + ] - self.read_resolutions() - with gr.Row(elem_id=f'{"img" if is_img2img else "txt"}2img_row_resolutions'): - btns = [ - ResButton(res=res, value=label) - for res, label in zip(self.res, self.res_labels) - ] - - with contextlib.suppress(AttributeError): - for b in btns: - if is_img2img: - resolution = [self.i2i_w, self.i2i_h] - else: - resolution = [self.t2i_w, self.t2i_h] - - b.click( - b.reset, - outputs=resolution, - ) - - # dummy components needed for JS function - dummy_text1 = gr.Text(visible=False) - dummy_text2 = gr.Text(visible=False) - dummy_text3 = gr.Text(visible=False) - dummy_text4 = gr.Text(visible=False) - - # Aspect Ratio Calculator - with gr.Column(visible=False, variant="panel", elem_id="arc_panel") as arc_panel: - arc_title_heading = gr.Markdown(value="#### Aspect Ratio Calculator") - with gr.Row(): - with gr.Column(min_width=150): - arc_width1 = gr.Number(label="Width 1") - arc_height1 = gr.Number(label="Height 1") - - with gr.Column(min_width=150): - arc_desired_width = gr.Number(label="Width 2") - arc_desired_height = gr.Number(label="Height 2") - - with gr.Column(min_width=150): - arc_ar_display = gr.Markdown(value="Aspect Ratio:") - with gr.Row(elem_id=f'{"img" if is_img2img else "txt"}2img_arc_tool_buttons'): - # Switch resolution values button - arc_swap = ToolButton(value=switch_values_symbol) - arc_swap.click(lambda w, h, w2, h2: (h, w, h2, w2), inputs=[arc_width1, arc_height1, arc_desired_width, arc_desired_height], outputs=[arc_width1, arc_height1, arc_desired_width, arc_desired_height]) - - with contextlib.suppress(AttributeError): - # For img2img tab - if is_img2img: - # Get slider dimensions button - resolution = [self.i2i_w, self.i2i_h] - arc_get_img2img_dim = ToolButton(value=get_dimensions_symbol) - arc_get_img2img_dim.click( - lambda w, h: (w, h), - inputs=resolution, - outputs=[arc_width1, arc_height1], - ) - - # Javascript function to select image element from current img2img tab - current_tab_image = """ - function current_tab_image(...args) { - const tab_index = get_img2img_tab_index(); - // Get current tab's image (on Batch tab, use image from img2img tab) - if (tab_index == 5) { - image = args[0]; - } else { - image = args[tab_index]; - } - // On Inpaint tab, select just the image and drop the mask - if (tab_index == 2 && image !== null) { - image = image["image"]; - } - return [image, null, null, null, null]; - } - - """ - # Get image dimensions - def get_dims(img: list, dummy_text1, dummy_text2, dummy_text3, dummy_text4): - if img: - width = img.size[0] - height = img.size[1] - return width, height - else: - return 0, 0 - - # Get image dimensions button - arc_get_image_dim = ToolButton(value=get_image_dimensions_symbol) - arc_get_image_dim.click(fn=get_dims,inputs=self.image,outputs=[arc_width1, arc_height1],_js=current_tab_image) - - else: - # For txt2img tab - # Get slider dimensions button - resolution = [self.t2i_w, self.t2i_h] - arc_get_txt2img_dim = ToolButton(value=get_dimensions_symbol) - arc_get_txt2img_dim.click( - lambda w, h: (w, h), - inputs=resolution, - outputs=[arc_width1, arc_height1], - ) - - # Update aspect ratio display on change - arc_width1.change(lambda w, h: (f"Aspect Ratio: **{get_reduced_ratio(w,h)}**"), inputs=[arc_width1, arc_height1], outputs=[arc_ar_display]) - arc_height1.change(lambda w, h: (f"Aspect Ratio: **{get_reduced_ratio(w,h)}**"), inputs=[arc_width1, arc_height1], outputs=[arc_ar_display]) - - with gr.Row(): - # Calculate and Apply buttons - arc_calc_height = gr.Button(value='Calculate Height').style(full_width=False) - arc_calc_height.click(lambda w2, w1, h1: (solve_aspect_ratio(w2,0,w1,h1)), inputs=[arc_desired_width,arc_width1,arc_height1], outputs=[arc_desired_height]) - arc_calc_width = gr.Button(value='Calculate Width').style(full_width=False) - arc_calc_width.click(lambda h2, w1, h1: (solve_aspect_ratio(0,h2,w1,h1)), inputs=[arc_desired_height,arc_width1,arc_height1], outputs=[arc_desired_width]) - arc_apply_params = gr.Button(value='Apply') with contextlib.suppress(AttributeError): - if is_img2img: - resolution = [self.i2i_w, self.i2i_h] - else: - resolution = [self.t2i_w, self.t2i_h] + for b in btns: + if is_img2img: + resolution = [self.i2i_w, self.i2i_h] + else: + resolution = [self.t2i_w, self.t2i_h] - arc_apply_params.click( - lambda w2, h2: (w2, h2), - inputs=[arc_desired_width, arc_desired_height], - outputs=resolution, - ) + b.click( + b.apply, + inputs=resolution, + outputs=resolution, + ) + + self.read_resolutions() + with gr.Row(elem_id=f'{"img" if is_img2img else "txt"}2img_row_resolutions'): + btns = [ + ResButton(res=res, value=label) + for res, label in zip(self.res, self.res_labels) + ] + with contextlib.suppress(AttributeError): + for b in btns: + if is_img2img: + resolution = [self.i2i_w, self.i2i_h] + else: + resolution = [self.t2i_w, self.t2i_h] + + b.click( + b.reset, + outputs=resolution, + ) + + # Write button_titles.js with labels and comments read from aspect ratios and resolutions files + button_titles = [self.aspect_ratio_labels + self.res_labels] + button_titles.append(self.aspect_ratio_comments + self.res_comments) + write_js_titles_file(button_titles) + + # dummy components needed for JS function + dummy_text1 = gr.Text(visible=False) + dummy_text2 = gr.Text(visible=False) + dummy_text3 = gr.Text(visible=False) + dummy_text4 = gr.Text(visible=False) + + # Aspect Ratio Calculator + with gr.Column(visible=False, variant="panel", elem_id="arc_panel") as arc_panel: + arc_title_heading = gr.Markdown(value="#### Aspect Ratio Calculator") + with gr.Row(): + with gr.Column(min_width=150): + arc_width1 = gr.Number(label="Width 1") + arc_height1 = gr.Number(label="Height 1") + + with gr.Column(min_width=150): + arc_desired_width = gr.Number(label="Width 2") + arc_desired_height = gr.Number(label="Height 2") + + with gr.Column(min_width=150): + arc_ar_display = gr.Markdown(value="Aspect Ratio:") + with gr.Row(elem_id=f'{"img" if is_img2img else "txt"}2img_arc_tool_buttons'): + # Switch resolution values button + arc_swap = ToolButton(value=switch_values_symbol) + arc_swap.click(lambda w, h, w2, h2: (h, w, h2, w2), inputs=[arc_width1, arc_height1, arc_desired_width, arc_desired_height], outputs=[arc_width1, arc_height1, arc_desired_width, arc_desired_height]) + + with contextlib.suppress(AttributeError): + # For img2img tab + if is_img2img: + # Get slider dimensions button + resolution = [self.i2i_w, self.i2i_h] + arc_get_img2img_dim = ToolButton(value=get_dimensions_symbol) + arc_get_img2img_dim.click( + lambda w, h: (w, h), + inputs=resolution, + outputs=[arc_width1, arc_height1], + ) + + # Javascript function to select image element from current img2img tab + current_tab_image = """ + function current_tab_image(...args) { + const tab_index = get_img2img_tab_index(); + // Get current tab's image (on Batch tab, use image from img2img tab) + if (tab_index == 5) { + image = args[0]; + } else { + image = args[tab_index]; + } + // On Inpaint tab, select just the image and drop the mask + if (tab_index == 2 && image !== null) { + image = image["image"]; + } + return [image, null, null, null, null]; + } + + """ + # Get image dimensions + def get_dims(img: list, dummy_text1, dummy_text2, dummy_text3, dummy_text4): + if img: + width = img.size[0] + height = img.size[1] + return width, height + else: + return 0, 0 + + # Get image dimensions button + arc_get_image_dim = ToolButton(value=get_image_dimensions_symbol) + arc_get_image_dim.click(fn=get_dims,inputs=self.image,outputs=[arc_width1, arc_height1],_js=current_tab_image) + + else: + # For txt2img tab + # Get slider dimensions button + resolution = [self.t2i_w, self.t2i_h] + arc_get_txt2img_dim = ToolButton(value=get_dimensions_symbol) + arc_get_txt2img_dim.click( + lambda w, h: (w, h), + inputs=resolution, + outputs=[arc_width1, arc_height1], + ) + + # Update aspect ratio display on change + arc_width1.change(lambda w, h: (f"Aspect Ratio: **{get_reduced_ratio(w,h)}**"), inputs=[arc_width1, arc_height1], outputs=[arc_ar_display]) + arc_height1.change(lambda w, h: (f"Aspect Ratio: **{get_reduced_ratio(w,h)}**"), inputs=[arc_width1, arc_height1], outputs=[arc_ar_display]) + + with gr.Row(): + # Calculate and Apply buttons + arc_calc_height = gr.Button(value='Calculate Height').style(full_width=False) + arc_calc_height.click(lambda w2, w1, h1: (solve_aspect_ratio(w2,0,w1,h1)), inputs=[arc_desired_width,arc_width1,arc_height1], outputs=[arc_desired_height]) + arc_calc_width = gr.Button(value='Calculate Width').style(full_width=False) + arc_calc_width.click(lambda h2, w1, h1: (solve_aspect_ratio(0,h2,w1,h1)), inputs=[arc_desired_height,arc_width1,arc_height1], outputs=[arc_desired_width]) + arc_apply_params = gr.Button(value='Apply') + with contextlib.suppress(AttributeError): + if is_img2img: + resolution = [self.i2i_w, self.i2i_h] + else: + resolution = [self.t2i_w, self.t2i_h] + + arc_apply_params.click( + lambda w2, h2: (w2, h2), + inputs=[arc_desired_width, arc_desired_height], + outputs=resolution, + ) + + # Show calculator pane (and reset number input values) + arc_show_calculator.click( + lambda: [gr.update(visible=True), gr.update(visible=False), gr.update(visible=True), + gr.update(value=512), gr.update(value=512), gr.update(value=0), gr.update(value=0), + gr.update(value="Aspect Ratio: **1:1**")], + None, + [arc_panel, arc_show_calculator, arc_hide_calculator, + arc_width1, arc_height1, arc_desired_width, arc_desired_height, + arc_ar_display] + ) + # Hide calculator pane + arc_hide_calculator.click( + lambda: [gr.update(visible=False), gr.update(visible=True), gr.update(visible=False)], + None, [arc_panel, arc_show_calculator, arc_hide_calculator]) - # Show calculator pane (and reset number input values) - arc_show_calculator.click( - lambda: [gr.update(visible=True), gr.update(visible=False), gr.update(visible=True), - gr.update(value=512), gr.update(value=512), gr.update(value=0), gr.update(value=0), - gr.update(value="Aspect Ratio: **1:1**")], - None, - [arc_panel, arc_show_calculator, arc_hide_calculator, - arc_width1, arc_height1, arc_desired_width, arc_desired_height, - arc_ar_display] - ) - # Hide calculator pane - arc_hide_calculator.click( - lambda: [gr.update(visible=False), gr.update(visible=True), gr.update(visible=False)], - None, [arc_panel, arc_show_calculator, arc_hide_calculator]) # https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/7456#issuecomment-1414465888 def after_component(self, component, **kwargs):