diff options
author | AUTOMATIC1111 <16777216c@gmail.com> | 2023-07-15 21:56:53 +0000 |
---|---|---|
committer | AUTOMATIC1111 <16777216c@gmail.com> | 2023-07-15 21:57:45 +0000 |
commit | 11f339733de860b0b51adebe15dc945df7189edf (patch) | |
tree | 02d7036c9488c802ddbdaf6819e6006654ca208f | |
parent | 5decbf184b185026d5da9e2c7be02d06fd640f12 (diff) | |
download | stable-diffusion-webui-gfx803-11f339733de860b0b51adebe15dc945df7189edf.tar.gz stable-diffusion-webui-gfx803-11f339733de860b0b51adebe15dc945df7189edf.tar.bz2 stable-diffusion-webui-gfx803-11f339733de860b0b51adebe15dc945df7189edf.zip |
add lora user metadata editor dialog inspired by MrKuenning's mockup from #7458
-rw-r--r-- | extensions-builtin/Lora/ui_edit_user_metadata.py | 187 | ||||
-rw-r--r-- | extensions-builtin/Lora/ui_extra_networks_lora.py | 17 | ||||
-rw-r--r-- | javascript/extraNetworks.js | 18 | ||||
-rw-r--r-- | modules/ui_extra_networks_user_metadata.py | 23 | ||||
-rw-r--r-- | style.css | 9 |
5 files changed, 241 insertions, 13 deletions
diff --git a/extensions-builtin/Lora/ui_edit_user_metadata.py b/extensions-builtin/Lora/ui_edit_user_metadata.py new file mode 100644 index 00000000..c7dbd1c1 --- /dev/null +++ b/extensions-builtin/Lora/ui_edit_user_metadata.py @@ -0,0 +1,187 @@ +import html
+import json
+import random
+
+import gradio as gr
+import re
+
+from modules import ui_extra_networks_user_metadata
+
+
+def is_non_comma_tagset(tags):
+ average_tag_length = sum(len(x) for x in tags.keys()) / len(tags)
+
+ return average_tag_length >= 16
+
+
+re_word = re.compile(r"[-_\w']+")
+re_comma = re.compile(r" *, *")
+
+
+def build_tags(metadata):
+ tags = {}
+
+ for _, tags_dict in metadata.get("ss_tag_frequency", {}).items():
+ for tag, tag_count in tags_dict.items():
+ tag = tag.strip()
+ tags[tag] = tags.get(tag, 0) + int(tag_count)
+
+ if tags and is_non_comma_tagset(tags):
+ new_tags = {}
+
+ for text, text_count in tags.items():
+ for word in re.findall(re_word, text):
+ if len(word) < 3:
+ continue
+
+ new_tags[word] = new_tags.get(word, 0) + text_count
+
+ tags = new_tags
+
+ ordered_tags = sorted(tags.keys(), key=tags.get, reverse=True)
+
+ return [(tag, tags[tag]) for tag in ordered_tags]
+
+
+class LoraUserMetadataEditor(ui_extra_networks_user_metadata.UserMetadataEditor):
+ def __init__(self, ui, tabname, page):
+ super().__init__(ui, tabname, page)
+
+ self.taginfo = None
+ self.edit_activation_text = None
+ self.slider_preferred_weight = None
+ self.edit_notes = None
+
+ def save_lora_user_metadata(self, name, desc, activation_text, preferred_weight, notes):
+ user_metadata = self.get_user_metadata(name)
+ user_metadata["description"] = desc
+ user_metadata["activation text"] = activation_text
+ user_metadata["preferred weight"] = preferred_weight
+ user_metadata["notes"] = notes
+
+ self.write_user_metadata(name, user_metadata)
+
+ def get_metadata_table(self, name):
+ table = super().get_metadata_table(name)
+ item = self.page.items.get(name, {})
+ metadata = json.loads(item.get("metadata") or '{}')
+
+ keys = [
+ ('ss_sd_model_name', "Model:"),
+ ('ss_resolution', "Resolution:"),
+ ('ss_clip_skip', "Clip skip:"),
+ ]
+
+ for key, label in keys:
+ value = metadata.get(key, None)
+ if value is not None and str(value) != "None":
+ table.append((label, html.escape(value)))
+
+ image_count = 0
+ for _, params in metadata.get("ss_dataset_dirs", {}).items():
+ image_count += int(params.get("img_count", 0))
+
+ if image_count:
+ table.append(("Dataset size:", image_count))
+
+ return table
+
+ def put_values_into_components(self, name):
+ user_metadata = self.get_user_metadata(name)
+ values = super().put_values_into_components(name)
+
+ item = self.page.items.get(name, {})
+ metadata = json.loads(item.get("metadata") or '{}')
+
+ tags = build_tags(metadata)
+ gradio_tags = [(tag, str(count)) for tag, count in tags[0:24]]
+
+ return [
+ *values[0:4],
+ gr.HighlightedText.update(value=gradio_tags, visible=True if tags else False),
+ user_metadata.get('activation text', ''),
+ float(user_metadata.get('preferred weight', 0.0)),
+ user_metadata.get('notes', ''),
+ gr.update(visible=True if tags else False),
+ gr.update(value=self.generate_random_prompt_from_tags(tags), visible=True if tags else False),
+ ]
+
+ def generate_random_prompt(self, name):
+ item = self.page.items.get(name, {})
+ metadata = json.loads(item.get("metadata") or '{}')
+ tags = build_tags(metadata)
+
+ return self.generate_random_prompt_from_tags(tags)
+
+ def generate_random_prompt_from_tags(self, tags):
+ max_count = None
+ res = []
+ for tag, count in tags:
+ if not max_count:
+ max_count = count
+
+ v = random.random() * max_count
+ if count > v:
+ res.append(tag)
+
+ return ", ".join(sorted(res))
+
+ def create_editor(self):
+ self.create_default_editor_elems()
+
+ self.taginfo = gr.HighlightedText(label="Tags")
+ self.edit_activation_text = gr.Text(label='Activation text', info="Will be added to prompt along with Lora")
+ self.slider_preferred_weight = gr.Slider(label='Preferred weight', info="Set to 0 to disable", minimum=0.0, maximum=2.0, step=0.01)
+
+ with gr.Row() as row_random_prompt:
+ with gr.Column(scale=8):
+ random_prompt = gr.Textbox(label='Random prompt', lines=4, max_lines=4, interactive=False)
+
+ with gr.Column(scale=1, min_width=120):
+ generate_random_prompt = gr.Button('Generate').style(full_width=True, size="lg")
+
+ self.edit_notes = gr.TextArea(label='Notes', lines=4)
+
+ generate_random_prompt.click(fn=self.generate_random_prompt, inputs=[self.edit_name_input], outputs=[random_prompt])
+
+ def select_tag(activation_text, evt: gr.SelectData):
+ tag = evt.value[0]
+
+ words = re.split(re_comma, activation_text)
+ if tag in words:
+ words = [x for x in words if x != tag and x.strip()]
+ return ", ".join(words)
+
+ return activation_text + ", " + tag if activation_text else tag
+
+ self.taginfo.select(fn=select_tag, inputs=[self.edit_activation_text], outputs=[self.edit_activation_text], show_progress=False)
+
+ self.create_default_buttons()
+
+ viewed_components = [
+ self.edit_name,
+ self.edit_description,
+ self.html_filedata,
+ self.html_preview,
+ self.taginfo,
+ self.edit_activation_text,
+ self.slider_preferred_weight,
+ self.edit_notes,
+ row_random_prompt,
+ random_prompt,
+ ]
+
+ self.button_edit\
+ .click(fn=self.put_values_into_components, inputs=[self.edit_name_input], outputs=viewed_components)\
+ .then(fn=lambda: gr.update(visible=True), inputs=[], outputs=[self.box])
+
+ edited_components = [
+ self.edit_description,
+ self.edit_activation_text,
+ self.slider_preferred_weight,
+ self.edit_notes,
+ ]
+
+ self.button_save\
+ .click(fn=self.save_lora_user_metadata, inputs=[self.edit_name_input, *edited_components], outputs=[]) \
+ .then(fn=None, _js="extraNetworksReloadAll")
diff --git a/extensions-builtin/Lora/ui_extra_networks_lora.py b/extensions-builtin/Lora/ui_extra_networks_lora.py index 29b16c1c..95296275 100644 --- a/extensions-builtin/Lora/ui_extra_networks_lora.py +++ b/extensions-builtin/Lora/ui_extra_networks_lora.py @@ -3,6 +3,7 @@ import os import lora
from modules import shared, ui_extra_networks
+from ui_edit_user_metadata import LoraUserMetadataEditor
class ExtraNetworksPageLora(ui_extra_networks.ExtraNetworksPage):
@@ -18,19 +19,29 @@ class ExtraNetworksPageLora(ui_extra_networks.ExtraNetworksPage): alias = lora_on_disk.get_alias()
- yield {
+ item = {
"name": name,
"filename": lora_on_disk.filename,
"preview": self.find_preview(path),
"description": self.find_description(path),
"search_term": self.search_terms_from_path(lora_on_disk.filename),
- "prompt": json.dumps(f"<lora:{alias}:") + " + opts.extra_networks_default_multiplier + " + json.dumps(">"),
"local_preview": f"{path}.{shared.opts.samples_format}",
"metadata": json.dumps(lora_on_disk.metadata, indent=4) if lora_on_disk.metadata else None,
"sort_keys": {'default': index, **self.get_sort_keys(lora_on_disk.filename)},
-
}
+ self.read_user_metadata(item)
+ activation_text = item["user_metadata"].get("activation text")
+ preferred_weight = item["user_metadata"].get("preferred weight", 0.0)
+ item["prompt"] = json.dumps(f"<lora:{alias}:") + " + " + (str(preferred_weight) if preferred_weight else "opts.extra_networks_default_multiplier") + " + " + json.dumps(">")
+
+ if activation_text:
+ item["prompt"] += " + " + json.dumps(" " + activation_text)
+
+ yield item
+
def allowed_directories_for_previews(self):
return [shared.cmd_opts.lora_dir]
+ def create_user_metadata_editor(self, ui, tabname):
+ return LoraUserMetadataEditor(ui, tabname, self)
diff --git a/javascript/extraNetworks.js b/javascript/extraNetworks.js index 7007b353..8b67bf2b 100644 --- a/javascript/extraNetworks.js +++ b/javascript/extraNetworks.js @@ -113,7 +113,7 @@ function setupExtraNetworks() { onUiLoaded(setupExtraNetworks); -var re_extranet = /<([^:]+:[^:]+):[\d.]+>/; +var re_extranet = /<([^:]+:[^:]+):[\d.]+>(.*)/; var re_extranet_g = /\s+<([^:]+:[^:]+):[\d.]+>/g; function tryToRemoveExtraNetworkFromPrompt(textarea, text) { @@ -121,15 +121,22 @@ function tryToRemoveExtraNetworkFromPrompt(textarea, text) { var replaced = false; var newTextareaText; if (m) { + var extraTextAfterNet = m[2]; var partToSearch = m[1]; - newTextareaText = textarea.value.replaceAll(re_extranet_g, function(found) { + var foundAtPosition = -1; + newTextareaText = textarea.value.replaceAll(re_extranet_g, function(found, net, pos) { m = found.match(re_extranet); if (m[1] == partToSearch) { replaced = true; + foundAtPosition = pos; return ""; } return found; }); + + if (foundAtPosition >= 0 && newTextareaText.substr(foundAtPosition, extraTextAfterNet.length) == extraTextAfterNet) { + newTextareaText = newTextareaText.substr(0, foundAtPosition) + newTextareaText.substr(foundAtPosition + extraTextAfterNet.length); + } } else { newTextareaText = textarea.value.replaceAll(new RegExp(text, "g"), function(found) { if (found == text) { @@ -288,3 +295,10 @@ function extraNetworksEditUserMetadata(event, tabname, extraPage, cardName) { event.stopPropagation(); } + +function extraNetworksReloadAll() { + closePopup(); + + gradioApp().getElementById('txt2img_extra_refresh').click(); + gradioApp().getElementById('img2img_extra_refresh').click(); +} diff --git a/modules/ui_extra_networks_user_metadata.py b/modules/ui_extra_networks_user_metadata.py index 8d20d026..0dbd7419 100644 --- a/modules/ui_extra_networks_user_metadata.py +++ b/modules/ui_extra_networks_user_metadata.py @@ -52,7 +52,7 @@ class UserMetadataEditor: def create_default_buttons(self):
- with gr.Row():
+ with gr.Row(elem_classes="edit-user-metadata-buttons"):
self.button_cancel = gr.Button('Cancel')
self.button_replace_preview = gr.Button('Replace preview', variant='primary')
self.button_save = gr.Button('Save', variant='primary')
@@ -88,8 +88,7 @@ class UserMetadataEditor: stats = os.stat(filename)
params = [
('File size: ', sysinfo.pretty_bytes(stats.st_size)),
- ('Created: ', datetime.datetime.fromtimestamp(stats.st_ctime).strftime('%Y-%m-%d %H:%M')),
- ('Last modified: ', datetime.datetime.fromtimestamp(stats.st_mtime).strftime('%Y-%m-%d %H:%M')),
+ ('Modified: ', datetime.datetime.fromtimestamp(stats.st_mtime).strftime('%Y-%m-%d %H:%M')),
]
return params
@@ -100,7 +99,12 @@ class UserMetadataEditor: def put_values_into_components(self, name):
user_metadata = self.get_user_metadata(name)
- params = self.get_metadata_table(name)
+ try:
+ params = self.get_metadata_table(name)
+ except Exception as e:
+ errors.display(e, f"reading metadata info for {name}")
+ params = []
+
table = '<table class="file-metadata">' + "".join(f"<tr><th>{name}</th><td>{value}</td></tr>" for name, value in params) + '</table>'
return html.escape(name), user_metadata.get('description', ''), table, self.get_card_html(name)
@@ -128,7 +132,9 @@ class UserMetadataEditor: .click(fn=self.put_values_into_components, inputs=[self.edit_name_input], outputs=[self.edit_name, self.edit_description, self.html_filedata, self.html_preview])\
.then(fn=lambda: gr.update(visible=True), inputs=[], outputs=[self.box])
- self.button_save.click(fn=self.save_user_metadata, inputs=[self.edit_name_input, self.edit_description], outputs=[]).then(fn=None, _js="closePopup")
+ self.button_save\
+ .click(fn=self.save_user_metadata, inputs=[self.edit_name_input, self.edit_description], outputs=[])\
+ .then(fn=None, _js="extraNetworksReloadAll")
def create_ui(self):
with gr.Box(visible=False, elem_id=self.id_part, elem_classes="edit-user-metadata") as box:
@@ -142,7 +148,7 @@ class UserMetadataEditor: def save_preview(self, index, gallery, name):
if len(gallery) == 0:
print("There is no image in gallery to save as a preview.")
- return [self.get_card_html(name)] + [page.create_html(self.ui.tabname) for page in self.ui.stored_extra_pages]
+ return [self.get_card_html(name)] + self.regenerate_ui_pages()
item = self.page.items.get(name, {})
@@ -156,7 +162,10 @@ class UserMetadataEditor: images.save_image_with_geninfo(image, geninfo, item["local_preview"])
- return [self.get_card_html(name)] + [page.create_html(self.tabname) for page in self.ui.stored_extra_pages]
+ return [self.get_card_html(name)] + self.regenerate_ui_pages()
+
+ def regenerate_ui_pages(self):
+ return [page.create_html(self.tabname) for page in self.ui.stored_extra_pages]
def setup_ui(self, gallery):
self.button_replace_preview.click(
@@ -1004,7 +1004,7 @@ footer { }
div.block.gradio-box.edit-user-metadata {
- min-width: 56em;
+ width: 56em;
background: var(--body-background-fill);
padding: 2em !important;
}
@@ -1021,3 +1021,10 @@ div.block.gradio-box.edit-user-metadata { .edit-user-metadata .wrap.translucent{
background: var(--body-background-fill);
}
+.edit-user-metadata .gradio-highlightedtext span{
+ word-break: break-word;
+}
+
+.edit-user-metadata-buttons{
+ margin-top: 1.5em;
+}
|