refactor: python js interface (#381)

* refactor js interface

* rename call_SD to call_JS

* add invocation example to call_JS

* serialize args before passing them to js

* allow override of x
This commit is contained in:
Thomas Mello 2022-09-01 13:11:22 +03:00 committed by GitHub
parent 8212d8e5de
commit 773aa60e8a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 79 additions and 58 deletions

View File

@ -1,4 +1,6 @@
from os import path
import json
def readTextFile(*args):
dir = path.dirname(__file__)
@ -7,31 +9,25 @@ def readTextFile(*args):
data = f.read()
return data
def css(opt):
styling = readTextFile("css", "styles.css")
if not opt.no_progressbar_hiding:
styling += readTextFile("css", "no_progress_bar.css")
return styling
def js(opt):
data = readTextFile("js", "index.js")
data = "(z) => {" + data + "; return z ?? [] }"
return data
# Wrap the typical SD method call into async closure for ease of use
# If you call frontend method without wrapping
# DONT FORGET to bind input argument if you need it: SD.with(x)
def w(sd_method_call):
return f"async (x) => {{ return await SD.with(x).{sd_method_call} ?? x ?? []; }}"
def js_move_image(from_id, to_id):
return w(f"moveImageFromGallery('{from_id}', '{to_id}')")
def js_copy_to_clipboard(from_id):
return w(f"copyImageFromGalleryToClipboard('{from_id}')")
def js_painterro_launch(to_id):
return w(f"Painterro.init('{to_id}')")
def js_img2img_submit(prompt_row_id):
return w(f"clickFirstVisibleButton('{prompt_row_id}')")
# Supplies the js function with a params object
# That includes all the passed arguments and input from Gradio: x
# Example call in Gradio component's event handler (pass the result to _js arg):
# _js=call_JS("myJsMethod", arg1="string", arg2=100, arg3=[])
def call_JS(sd_method, **kwargs):
param_str = json.dumps(kwargs)
return f"async (x) => {{ return await SD.{sd_method}({{ x, ...{param_str} }}) ?? []; }}"

View File

@ -1,6 +1,5 @@
import gradio as gr
from frontend.css_and_js import *
from frontend.css_and_js import css
from frontend.css_and_js import css, js, call_JS
import frontend.ui_functions as uifn
def draw_gradio_ui(opt, img2img=lambda x: x, txt2img=lambda x: x, txt2img_defaults={}, RealESRGAN=True, GFPGAN=True,
@ -47,10 +46,13 @@ def draw_gradio_ui(opt, img2img=lambda x: x, txt2img=lambda x: x, txt2img_defaul
gr.Markdown(
'Select an image from the gallery, then click one of the buttons below to perform an action.')
with gr.Row():
output_txt2img_copy_clipboard = gr.Button("Copy to clipboard").click(fn=None,
inputs=output_txt2img_gallery,
outputs=[],
_js=js_copy_to_clipboard('txt2img_gallery_output'))
output_txt2img_copy_clipboard = gr.Button("Copy to clipboard")\
.click(fn=None,
inputs=output_txt2img_gallery,
outputs=[],
_js=call_JS("copyImageFromGalleryToClipboard",
fromId="txt2img_gallery_output")
)
output_txt2img_copy_to_input_btn = gr.Button("Push to img2img")
if RealESRGAN is not None:
output_txt2img_to_upscale_esrgan = gr.Button("Upscale w/ ESRGAN")
@ -60,11 +62,13 @@ def draw_gradio_ui(opt, img2img=lambda x: x, txt2img=lambda x: x, txt2img_defaul
with gr.Row():
output_txt2img_copy_params = gr.Button("Copy full parameters").click(
inputs=output_txt2img_params, outputs=[],
_js='(x) => navigator.clipboard.writeText(x)', fn=None, show_progress=False)
fn=None, show_progress=False,
_js=call_JS("gradioInputToClipboard")
)
output_txt2img_seed = gr.Number(label='Seed', interactive=False, visible=False)
output_txt2img_copy_seed = gr.Button("Copy only seed").click(
inputs=output_txt2img_seed, outputs=[],
_js='(x) => navigator.clipboard.writeText(x)', fn=None, show_progress=False)
_js=call_JS("gradioInputToClipboard"), fn=None, show_progress=False)
output_txt2img_stats = gr.HTML(label='Stats')
with gr.Column():
@ -153,7 +157,7 @@ def draw_gradio_ui(opt, img2img=lambda x: x, txt2img=lambda x: x, txt2img_defaul
value=3, visible=False)
img2img_resize = gr.Radio(label="Resize mode",
choices=["Just resize", "Crop and resize", "Resize and fill"],
choices=["Just resize"],
type="index",
value=img2img_resize_modes[img2img_defaults['resize_mode']])
@ -172,17 +176,18 @@ def draw_gradio_ui(opt, img2img=lambda x: x, txt2img=lambda x: x, txt2img_defaul
output_img2img_copy_to_clipboard_btn = gr.Button("Copy to clipboard")
output_img2img_copy_to_input_btn = gr.Button("Push to img2img input")
output_img2img_copy_to_mask_btn = gr.Button("Push to img2img input mask")
gr.Markdown("Warning: This will clear your current image and mask settings!")
with gr.TabItem("Output info", id="img2img_output_info_tab"):
output_img2img_params = gr.Textbox(label="Generation parameters")
with gr.Row():
output_img2img_copy_params = gr.Button("Copy full parameters").click(
inputs=output_img2img_params, outputs=[],
_js='(x) => navigator.clipboard.writeText(x)', fn=None, show_progress=False)
_js=call_JS("gradioInputToClipboard"), fn=None, show_progress=False)
output_img2img_seed = gr.Number(label='Seed', interactive=False, visible=False)
output_img2img_copy_seed = gr.Button("Copy only seed").click(
inputs=output_img2img_seed, outputs=[],
_js='(x) => navigator.clipboard.writeText(x)', fn=None, show_progress=False)
_js=call_JS("gradioInputToClipboard"), fn=None, show_progress=False)
output_img2img_stats = gr.HTML(label='Stats')
gr.Markdown('# img2img settings')
@ -247,24 +252,32 @@ def draw_gradio_ui(opt, img2img=lambda x: x, txt2img=lambda x: x, txt2img_defaul
uifn.copy_img_to_input,
[output_txt2img_gallery],
[img2img_image_editor, img2img_image_mask, tabs],
_js=js_move_image('txt2img_gallery_output', 'img2img_editor')
_js=call_JS("moveImageFromGallery",
fromId="txt2img_gallery_output",
toId="img2img_editor")
)
output_img2img_copy_to_input_btn.click(
uifn.copy_img_to_edit,
[output_img2img_gallery],
[img2img_image_editor, tabs, img2img_image_editor_mode],
_js=js_move_image('img2img_gallery_output', 'img2img_editor')
_js=call_JS("moveImageFromGallery",
fromId="img2img_gallery_output",
toId="img2img_editor")
)
output_img2img_copy_to_mask_btn.click(
uifn.copy_img_to_mask,
[output_img2img_gallery],
[img2img_image_mask, tabs, img2img_image_editor_mode],
_js=js_move_image('img2img_gallery_output', 'img2img_editor')
_js=call_JS("moveImageFromGallery",
fromId="img2img_gallery_output",
toId="img2img_editor")
)
output_img2img_copy_to_clipboard_btn.click(fn=None, inputs=output_img2img_gallery, outputs=[],
_js=js_copy_to_clipboard('img2img_gallery_output'))
_js=call_JS("copyImageFromGalleryToClipboard",
fromId="img2img_gallery_output")
)
img2img_btn_mask.click(
img2img,
@ -287,9 +300,14 @@ def draw_gradio_ui(opt, img2img=lambda x: x, txt2img=lambda x: x, txt2img_defaul
# GENERATE ON ENTER
img2img_prompt.submit(None, None, None,
_js=js_img2img_submit("prompt_row"))
_js=call_JS("clickFirstVisibleButton",
rowId="prompt_row"))
img2img_painterro_btn.click(None, [img2img_image_editor], [img2img_image_editor, img2img_image_mask], _js=js_painterro_launch('img2img_editor'))
img2img_painterro_btn.click(None,
[img2img_image_editor],
[img2img_image_editor, img2img_image_mask],
_js=call_JS("Painterro.init", toId="img2img_editor")
)
img2img_width.change(fn=uifn.update_dimensions_info, inputs=[img2img_width, img2img_height], outputs=img2img_dimensions_info_text_box)
img2img_height.change(fn=uifn.update_dimensions_info, inputs=[img2img_width, img2img_height], outputs=img2img_dimensions_info_text_box)
@ -338,7 +356,10 @@ def draw_gradio_ui(opt, img2img=lambda x: x, txt2img=lambda x: x, txt2img_defaul
uifn.copy_img_to_upscale_esrgan,
output_txt2img_gallery,
[realesrgan_source, tabs],
_js=js_move_image('txt2img_gallery_output', 'img2img_editor'))
_js=call_JS("moveImageFromGallery",
fromId="txt2img_gallery_output",
toId="img2img_editor")
)
gr.HTML("""
<div id="90" style="max-width: 100%; font-size: 14px; text-align: center;" class="output-markdown gr-prose border-solid border border-gray-200 rounded gr-panel">

View File

@ -5,8 +5,8 @@ window.SD = (() => {
*/
class PainterroClass {
static isOpen = false;
static async init (toId) {
const img = SD.x;
static async init ({ x, toId }) {
const img = x;
const originalImage = Array.isArray(img) ? img[0] : img;
if (window.Painterro === undefined) {
@ -110,42 +110,28 @@ window.SD = (() => {
*/
class SDClass {
el = new ElementCache();
x;
Painterro = PainterroClass;
with (x) {
this.x = x;
return this;
}
moveImageFromGallery (fromId, toId) {
if (!Array.isArray(this.x) || this.x.length === 0) return;
moveImageFromGallery ({ x, fromId, toId }) {
if (!Array.isArray(x) || x.length === 0) return;
this.clearImageInput(this.el.get(`#${toId}`));
const i = this.#getGallerySelectedIndex(this.el.get(`#${fromId}`));
return [this.x[i].replace('data:;','data:image/png;')];
return [x[i].replace('data:;','data:image/png;')];
}
async copyImageFromGalleryToClipboard (fromId) {
if (!Array.isArray(this.x) || this.x.length === 0) return;
async copyImageFromGalleryToClipboard ({ x, fromId }) {
if (!Array.isArray(x) || x.length === 0) return;
const i = this.#getGallerySelectedIndex(this.el.get(`#${fromId}`));
const data = this.x[i];
const data = x[i];
const blob = await (await fetch(data.replace('data:;','data:image/png;'))).blob();
const item = new ClipboardItem({'image/png': blob});
try {
navigator.clipboard.write([item]);
} catch (e) {
SDClass.error(e);
}
return this.x;
await this.copyToClipboard([item]);
}
clearImageInput (imageEditor) {
imageEditor?.querySelector('.modify-upload button:last-child')?.click();
}
clickFirstVisibleButton(rowId) {
clickFirstVisibleButton({ rowId }) {
const generateButtons = this.el.get(`#${rowId}`).querySelectorAll('.gr-button-primary');
if (!generateButtons) return;
@ -161,6 +147,21 @@ window.SD = (() => {
}
}
}
async gradioInputToClipboard ({ x }) { return this.copyToClipboard(x); }
async copyToClipboard (value) {
if (!value || typeof value === 'boolean') return;
try {
if (Array.isArray(value) &&
value.length &&
value[0] instanceof ClipboardItem) {
await navigator.clipboard.write(value);
} else {
await navigator.clipboard.writeText(value);
}
} catch (e) {
SDClass.error(e);
}
}
static error (e) {
console.error(e);
if (typeof e === 'string') {
@ -169,6 +170,9 @@ window.SD = (() => {
alert(e.message);
}
}
clearImageInput (imageEditor) {
imageEditor?.querySelector('.modify-upload button:last-child')?.click();
}
#getGallerySelectedIndex (gallery) {
const selected = gallery.querySelector(`.\\!ring-2`);
return selected ? [...selected.parentNode.children].indexOf(selected) : 0;