extra networks UI
rework of hypernets: rather than via settings, hypernets are added directly to prompt as <hypernet:name:weight>master
parent
e33cace2c2
commit
40ff6db532
Binary file not shown.
|
After Width: | Height: | Size: 82 KiB |
@ -0,0 +1,11 @@
|
|||||||
|
<div class='card' {preview_html} onclick='return cardClicked({prompt}, {allow_negative_prompt})'>
|
||||||
|
<div class='actions'>
|
||||||
|
<div class='additional'>
|
||||||
|
<ul>
|
||||||
|
<a href="#" title="replace preview image with currently selected in gallery" onclick='return saveCardPreview(event, {tabname}, {local_preview})'>replace preview</a>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<span class='name'>{name}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
<div class='nocards'>
|
||||||
|
<h1>Nothing here. Add some content to the following directories:</h1>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
{dirs}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
@ -0,0 +1,60 @@
|
|||||||
|
|
||||||
|
function setupExtraNetworksForTab(tabname){
|
||||||
|
gradioApp().querySelector('#'+tabname+'_extra_tabs').classList.add('extra-networks')
|
||||||
|
|
||||||
|
gradioApp().querySelector('#'+tabname+'_extra_tabs > div').appendChild(gradioApp().getElementById(tabname+'_extra_refresh'))
|
||||||
|
gradioApp().querySelector('#'+tabname+'_extra_tabs > div').appendChild(gradioApp().getElementById(tabname+'_extra_close'))
|
||||||
|
}
|
||||||
|
|
||||||
|
var activePromptTextarea = null;
|
||||||
|
var activePositivePromptTextarea = null;
|
||||||
|
|
||||||
|
function setupExtraNetworks(){
|
||||||
|
setupExtraNetworksForTab('txt2img')
|
||||||
|
setupExtraNetworksForTab('img2img')
|
||||||
|
|
||||||
|
function registerPrompt(id, isNegative){
|
||||||
|
var textarea = gradioApp().querySelector("#" + id + " > label > textarea");
|
||||||
|
|
||||||
|
if (activePromptTextarea == null){
|
||||||
|
activePromptTextarea = textarea
|
||||||
|
}
|
||||||
|
if (activePositivePromptTextarea == null && ! isNegative){
|
||||||
|
activePositivePromptTextarea = textarea
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea.addEventListener("focus", function(){
|
||||||
|
activePromptTextarea = textarea;
|
||||||
|
if(! isNegative) activePositivePromptTextarea = textarea;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
registerPrompt('txt2img_prompt')
|
||||||
|
registerPrompt('txt2img_neg_prompt', true)
|
||||||
|
registerPrompt('img2img_prompt')
|
||||||
|
registerPrompt('img2img_neg_prompt', true)
|
||||||
|
}
|
||||||
|
|
||||||
|
onUiLoaded(setupExtraNetworks)
|
||||||
|
|
||||||
|
function cardClicked(textToAdd, allowNegativePrompt){
|
||||||
|
textarea = allowNegativePrompt ? activePromptTextarea : activePositivePromptTextarea
|
||||||
|
|
||||||
|
textarea.value = textarea.value + " " + textToAdd
|
||||||
|
updateInput(textarea)
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveCardPreview(event, tabname, filename){
|
||||||
|
textarea = gradioApp().querySelector("#" + tabname + '_preview_filename > label > textarea')
|
||||||
|
button = gradioApp().getElementById(tabname + '_save_preview')
|
||||||
|
|
||||||
|
textarea.value = filename
|
||||||
|
updateInput(textarea)
|
||||||
|
|
||||||
|
button.click()
|
||||||
|
|
||||||
|
event.stopPropagation()
|
||||||
|
event.preventDefault()
|
||||||
|
}
|
||||||
@ -0,0 +1,147 @@
|
|||||||
|
import re
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
from modules import errors
|
||||||
|
|
||||||
|
extra_network_registry = {}
|
||||||
|
|
||||||
|
|
||||||
|
def initialize():
|
||||||
|
extra_network_registry.clear()
|
||||||
|
|
||||||
|
|
||||||
|
def register_extra_network(extra_network):
|
||||||
|
extra_network_registry[extra_network.name] = extra_network
|
||||||
|
|
||||||
|
|
||||||
|
class ExtraNetworkParams:
|
||||||
|
def __init__(self, items=None):
|
||||||
|
self.items = items or []
|
||||||
|
|
||||||
|
|
||||||
|
class ExtraNetwork:
|
||||||
|
def __init__(self, name):
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
def activate(self, p, params_list):
|
||||||
|
"""
|
||||||
|
Called by processing on every run. Whatever the extra network is meant to do should be activated here.
|
||||||
|
Passes arguments related to this extra network in params_list.
|
||||||
|
User passes arguments by specifying this in his prompt:
|
||||||
|
|
||||||
|
<name:arg1:arg2:arg3>
|
||||||
|
|
||||||
|
Where name matches the name of this ExtraNetwork object, and arg1:arg2:arg3 are any natural number of text arguments
|
||||||
|
separated by colon.
|
||||||
|
|
||||||
|
Even if the user does not mention this ExtraNetwork in his prompt, the call will stil be made, with empty params_list -
|
||||||
|
in this case, all effects of this extra networks should be disabled.
|
||||||
|
|
||||||
|
Can be called multiple times before deactivate() - each new call should override the previous call completely.
|
||||||
|
|
||||||
|
For example, if this ExtraNetwork's name is 'hypernet' and user's prompt is:
|
||||||
|
|
||||||
|
> "1girl, <hypernet:agm:1.1> <extrasupernet:master:12:13:14> <hypernet:ray>"
|
||||||
|
|
||||||
|
params_list will be:
|
||||||
|
|
||||||
|
[
|
||||||
|
ExtraNetworkParams(items=["agm", "1.1"]),
|
||||||
|
ExtraNetworkParams(items=["ray"])
|
||||||
|
]
|
||||||
|
|
||||||
|
"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def deactivate(self, p):
|
||||||
|
"""
|
||||||
|
Called at the end of processing for housekeeping. No need to do anything here.
|
||||||
|
"""
|
||||||
|
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
|
def activate(p, extra_network_data):
|
||||||
|
"""call activate for extra networks in extra_network_data in specified order, then call
|
||||||
|
activate for all remaining registered networks with an empty argument list"""
|
||||||
|
|
||||||
|
for extra_network_name, extra_network_args in extra_network_data.items():
|
||||||
|
extra_network = extra_network_registry.get(extra_network_name, None)
|
||||||
|
if extra_network is None:
|
||||||
|
print(f"Skipping unknown extra network: {extra_network_name}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
extra_network.activate(p, extra_network_args)
|
||||||
|
except Exception as e:
|
||||||
|
errors.display(e, f"activating extra network {extra_network_name} with arguments {extra_network_args}")
|
||||||
|
|
||||||
|
for extra_network_name, extra_network in extra_network_registry.items():
|
||||||
|
args = extra_network_data.get(extra_network_name, None)
|
||||||
|
if args is not None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
extra_network.activate(p, [])
|
||||||
|
except Exception as e:
|
||||||
|
errors.display(e, f"activating extra network {extra_network_name}")
|
||||||
|
|
||||||
|
|
||||||
|
def deactivate(p, extra_network_data):
|
||||||
|
"""call deactivate for extra networks in extra_network_data in specified order, then call
|
||||||
|
deactivate for all remaining registered networks"""
|
||||||
|
|
||||||
|
for extra_network_name, extra_network_args in extra_network_data.items():
|
||||||
|
extra_network = extra_network_registry.get(extra_network_name, None)
|
||||||
|
if extra_network is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
extra_network.deactivate(p)
|
||||||
|
except Exception as e:
|
||||||
|
errors.display(e, f"deactivating extra network {extra_network_name}")
|
||||||
|
|
||||||
|
for extra_network_name, extra_network in extra_network_registry.items():
|
||||||
|
args = extra_network_data.get(extra_network_name, None)
|
||||||
|
if args is not None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
extra_network.deactivate(p)
|
||||||
|
except Exception as e:
|
||||||
|
errors.display(e, f"deactivating unmentioned extra network {extra_network_name}")
|
||||||
|
|
||||||
|
|
||||||
|
re_extra_net = re.compile(r"<(\w+):([^>]+)>")
|
||||||
|
|
||||||
|
|
||||||
|
def parse_prompt(prompt):
|
||||||
|
res = defaultdict(list)
|
||||||
|
|
||||||
|
def found(m):
|
||||||
|
name = m.group(1)
|
||||||
|
args = m.group(2)
|
||||||
|
|
||||||
|
res[name].append(ExtraNetworkParams(items=args.split(":")))
|
||||||
|
|
||||||
|
return ""
|
||||||
|
|
||||||
|
prompt = re.sub(re_extra_net, found, prompt)
|
||||||
|
|
||||||
|
return prompt, res
|
||||||
|
|
||||||
|
|
||||||
|
def parse_prompts(prompts):
|
||||||
|
res = []
|
||||||
|
extra_data = None
|
||||||
|
|
||||||
|
for prompt in prompts:
|
||||||
|
updated_prompt, parsed_extra_data = parse_prompt(prompt)
|
||||||
|
|
||||||
|
if extra_data is None:
|
||||||
|
extra_data = parsed_extra_data
|
||||||
|
|
||||||
|
res.append(updated_prompt)
|
||||||
|
|
||||||
|
return res, extra_data
|
||||||
|
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
from modules import extra_networks
|
||||||
|
from modules.hypernetworks import hypernetwork
|
||||||
|
|
||||||
|
|
||||||
|
class ExtraNetworkHypernet(extra_networks.ExtraNetwork):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__('hypernet')
|
||||||
|
|
||||||
|
def activate(self, p, params_list):
|
||||||
|
names = []
|
||||||
|
multipliers = []
|
||||||
|
for params in params_list:
|
||||||
|
assert len(params.items) > 0
|
||||||
|
|
||||||
|
names.append(params.items[0])
|
||||||
|
multipliers.append(float(params.items[1]) if len(params.items) > 1 else 1.0)
|
||||||
|
|
||||||
|
hypernetwork.load_hypernetworks(names, multipliers)
|
||||||
|
|
||||||
|
def deactivate(p, self):
|
||||||
|
pass
|
||||||
@ -0,0 +1,149 @@
|
|||||||
|
import os.path
|
||||||
|
|
||||||
|
from modules import shared
|
||||||
|
import gradio as gr
|
||||||
|
import json
|
||||||
|
|
||||||
|
from modules.generation_parameters_copypaste import image_from_url_text
|
||||||
|
|
||||||
|
extra_pages = []
|
||||||
|
|
||||||
|
|
||||||
|
def register_page(page):
|
||||||
|
"""registers extra networks page for the UI; recommend doing it in on_app_started() callback for extensions"""
|
||||||
|
|
||||||
|
extra_pages.append(page)
|
||||||
|
|
||||||
|
|
||||||
|
class ExtraNetworksPage:
|
||||||
|
def __init__(self, title):
|
||||||
|
self.title = title
|
||||||
|
self.card_page = shared.html("extra-networks-card.html")
|
||||||
|
self.allow_negative_prompt = False
|
||||||
|
|
||||||
|
def refresh(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def create_html(self, tabname):
|
||||||
|
items_html = ''
|
||||||
|
|
||||||
|
for item in self.list_items():
|
||||||
|
items_html += self.create_html_for_item(item, tabname)
|
||||||
|
|
||||||
|
if items_html == '':
|
||||||
|
dirs = "".join([f"<li>{x}</li>" for x in self.allowed_directories_for_previews()])
|
||||||
|
items_html = shared.html("extra-networks-no-cards.html").format(dirs=dirs)
|
||||||
|
|
||||||
|
res = "<div class='extra-network-cards'>" + items_html + "</div>"
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
def list_items(self):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def allowed_directories_for_previews(self):
|
||||||
|
return []
|
||||||
|
|
||||||
|
def create_html_for_item(self, item, tabname):
|
||||||
|
preview = item.get("preview", None)
|
||||||
|
|
||||||
|
args = {
|
||||||
|
"preview_html": "style='background-image: url(" + json.dumps(preview) + ")'" if preview else '',
|
||||||
|
"prompt": json.dumps(item["prompt"]),
|
||||||
|
"tabname": json.dumps(tabname),
|
||||||
|
"local_preview": json.dumps(item["local_preview"]),
|
||||||
|
"name": item["name"],
|
||||||
|
"allow_negative_prompt": "true" if self.allow_negative_prompt else "false",
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.card_page.format(**args)
|
||||||
|
|
||||||
|
|
||||||
|
def intialize():
|
||||||
|
extra_pages.clear()
|
||||||
|
|
||||||
|
|
||||||
|
class ExtraNetworksUi:
|
||||||
|
def __init__(self):
|
||||||
|
self.pages = None
|
||||||
|
self.stored_extra_pages = None
|
||||||
|
|
||||||
|
self.button_save_preview = None
|
||||||
|
self.preview_target_filename = None
|
||||||
|
|
||||||
|
self.tabname = None
|
||||||
|
|
||||||
|
|
||||||
|
def create_ui(container, button, tabname):
|
||||||
|
ui = ExtraNetworksUi()
|
||||||
|
ui.pages = []
|
||||||
|
ui.stored_extra_pages = extra_pages.copy()
|
||||||
|
ui.tabname = tabname
|
||||||
|
|
||||||
|
with gr.Tabs(elem_id=tabname+"_extra_tabs") as tabs:
|
||||||
|
button_refresh = gr.Button('Refresh', elem_id=tabname+"_extra_refresh")
|
||||||
|
button_close = gr.Button('Close', elem_id=tabname+"_extra_close")
|
||||||
|
|
||||||
|
for page in ui.stored_extra_pages:
|
||||||
|
with gr.Tab(page.title):
|
||||||
|
page_elem = gr.HTML(page.create_html(ui.tabname))
|
||||||
|
ui.pages.append(page_elem)
|
||||||
|
|
||||||
|
ui.button_save_preview = gr.Button('Save preview', elem_id=tabname+"_save_preview", visible=False)
|
||||||
|
ui.preview_target_filename = gr.Textbox('Preview save filename', elem_id=tabname+"_preview_filename", visible=False)
|
||||||
|
|
||||||
|
button.click(fn=lambda: gr.update(visible=True), inputs=[], outputs=[container])
|
||||||
|
button_close.click(fn=lambda: gr.update(visible=False), inputs=[], outputs=[container])
|
||||||
|
|
||||||
|
def refresh():
|
||||||
|
res = []
|
||||||
|
|
||||||
|
for pg in ui.stored_extra_pages:
|
||||||
|
pg.refresh()
|
||||||
|
res.append(pg.create_html(ui.tabname))
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
button_refresh.click(fn=refresh, inputs=[], outputs=ui.pages)
|
||||||
|
|
||||||
|
return ui
|
||||||
|
|
||||||
|
|
||||||
|
def path_is_parent(parent_path, child_path):
|
||||||
|
parent_path = os.path.abspath(parent_path)
|
||||||
|
child_path = os.path.abspath(child_path)
|
||||||
|
|
||||||
|
return os.path.commonpath([parent_path]) == os.path.commonpath([parent_path, child_path])
|
||||||
|
|
||||||
|
|
||||||
|
def setup_ui(ui, gallery):
|
||||||
|
def save_preview(index, images, filename):
|
||||||
|
if len(images) == 0:
|
||||||
|
print("There is no image in gallery to save as a preview.")
|
||||||
|
return [page.create_html(ui.tabname) for page in ui.stored_extra_pages]
|
||||||
|
|
||||||
|
index = int(index)
|
||||||
|
index = 0 if index < 0 else index
|
||||||
|
index = len(images) - 1 if index >= len(images) else index
|
||||||
|
|
||||||
|
img_info = images[index if index >= 0 else 0]
|
||||||
|
image = image_from_url_text(img_info)
|
||||||
|
|
||||||
|
is_allowed = False
|
||||||
|
for extra_page in ui.stored_extra_pages:
|
||||||
|
if any([path_is_parent(x, filename) for x in extra_page.allowed_directories_for_previews()]):
|
||||||
|
is_allowed = True
|
||||||
|
break
|
||||||
|
|
||||||
|
assert is_allowed, f'writing to {filename} is not allowed'
|
||||||
|
|
||||||
|
image.save(filename)
|
||||||
|
|
||||||
|
return [page.create_html(ui.tabname) for page in ui.stored_extra_pages]
|
||||||
|
|
||||||
|
ui.button_save_preview.click(
|
||||||
|
fn=save_preview,
|
||||||
|
_js="function(x, y, z){console.log(x, y, z); return [selected_gallery_index(), y, z]}",
|
||||||
|
inputs=[ui.preview_target_filename, gallery, ui.preview_target_filename],
|
||||||
|
outputs=[*ui.pages]
|
||||||
|
)
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
from modules import shared, ui_extra_networks
|
||||||
|
|
||||||
|
|
||||||
|
class ExtraNetworksPageHypernetworks(ui_extra_networks.ExtraNetworksPage):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__('Hypernetworks')
|
||||||
|
|
||||||
|
def refresh(self):
|
||||||
|
shared.reload_hypernetworks()
|
||||||
|
|
||||||
|
def list_items(self):
|
||||||
|
for name, path in shared.hypernetworks.items():
|
||||||
|
path, ext = os.path.splitext(path)
|
||||||
|
previews = [path + ".png", path + ".preview.png"]
|
||||||
|
|
||||||
|
preview = None
|
||||||
|
for file in previews:
|
||||||
|
if os.path.isfile(file):
|
||||||
|
preview = "./file=" + file.replace('\\', '/') + "?mtime=" + str(os.path.getmtime(file))
|
||||||
|
break
|
||||||
|
|
||||||
|
yield {
|
||||||
|
"name": name,
|
||||||
|
"filename": path,
|
||||||
|
"preview": preview,
|
||||||
|
"prompt": f"<hypernet:{name}:1.0>",
|
||||||
|
"local_preview": path + ".png",
|
||||||
|
}
|
||||||
|
|
||||||
|
def allowed_directories_for_previews(self):
|
||||||
|
return [shared.cmd_opts.hypernetwork_dir]
|
||||||
|
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
from modules import ui_extra_networks, sd_hijack
|
||||||
|
|
||||||
|
|
||||||
|
class ExtraNetworksPageTextualInversion(ui_extra_networks.ExtraNetworksPage):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__('Textual Inversion')
|
||||||
|
self.allow_negative_prompt = True
|
||||||
|
|
||||||
|
def refresh(self):
|
||||||
|
sd_hijack.model_hijack.embedding_db.load_textual_inversion_embeddings(force_reload=True)
|
||||||
|
|
||||||
|
def list_items(self):
|
||||||
|
for embedding in sd_hijack.model_hijack.embedding_db.word_embeddings.values():
|
||||||
|
path, ext = os.path.splitext(embedding.filename)
|
||||||
|
preview_file = path + ".preview.png"
|
||||||
|
|
||||||
|
preview = None
|
||||||
|
if os.path.isfile(preview_file):
|
||||||
|
preview = "./file=" + preview_file.replace('\\', '/') + "?mtime=" + str(os.path.getmtime(preview_file))
|
||||||
|
|
||||||
|
yield {
|
||||||
|
"name": embedding.name,
|
||||||
|
"filename": embedding.filename,
|
||||||
|
"preview": preview,
|
||||||
|
"prompt": embedding.name,
|
||||||
|
"local_preview": path + ".preview.png",
|
||||||
|
}
|
||||||
|
|
||||||
|
def allowed_directories_for_previews(self):
|
||||||
|
return list(sd_hijack.model_hijack.embedding_db.embedding_dirs)
|
||||||
Loading…
Reference in New Issue