Merge pull request #26 from lllyasviel/acc

Put unit in accordions
This commit is contained in:
Chenlei Hu
2024-02-02 22:10:08 +00:00
committed by GitHub
6 changed files with 88 additions and 103 deletions

View File

@@ -66,30 +66,39 @@
constructor(tab, accordion) {
this.tab = tab;
this.accordion = accordion;
this.isImg2Img = tab.querySelector('.cnet-unit-enabled').id.includes('img2img');
this.isImg2Img = tab.querySelector('.cnet-mask-upload').id.includes('img2img');
this.enabledCheckbox = tab.querySelector('.cnet-unit-enabled input');
this.enabledCheckbox = tab.querySelector('.input-accordion-checkbox');
this.inputImage = tab.querySelector('.cnet-input-image-group .cnet-image input[type="file"]');
this.inputImageContainer = tab.querySelector('.cnet-input-image-group .cnet-image');
this.controlTypeRadios = tab.querySelectorAll('.controlnet_control_type_filter_group input[type="radio"]');
this.resizeModeRadios = tab.querySelectorAll('.controlnet_resize_mode_radio input[type="radio"]');
this.runPreprocessorButton = tab.querySelector('.cnet-run-preprocessor');
const tabs = tab.parentNode;
this.tabNav = tabs.querySelector('.tab-nav');
this.tabIndex = childIndex(tab) - 1; // -1 because tab-nav is also at the same level.
this.tabs = tab.parentNode;
this.tabIndex = childIndex(tab);
// By default the InputAccordion checkbox is linked with the state
// of accordion's open/close state. To disable this link, we can
// simulate click to check the checkbox and uncheck it.
this.enabledCheckbox.click();
this.enabledCheckbox.click();
this.attachEnabledButtonListener();
this.attachControlTypeRadioListener();
this.attachTabNavChangeObserver();
this.attachImageUploadListener();
this.attachImageStateChangeObserver();
this.attachA1111SendInfoObserver();
this.attachPresetDropdownObserver();
}
getTabNavButton() {
return this.tabNav.querySelector(`:nth-child(${this.tabIndex + 1})`);
/**
* Get the span that has text "Unit {X}".
*/
getUnitHeaderTextElement() {
return this.tab.querySelector(
`:nth-child(${this.tabIndex + 1}) span.svelte-s1r2yt`
);
}
getActiveControlType() {
@@ -102,13 +111,13 @@
}
updateActiveState() {
const tabNavButton = this.getTabNavButton();
if (!tabNavButton) return;
const unitHeader = this.getUnitHeaderTextElement();
if (!unitHeader) return;
if (this.enabledCheckbox.checked) {
tabNavButton.classList.add('cnet-unit-active');
unitHeader.classList.add('cnet-unit-active');
} else {
tabNavButton.classList.remove('cnet-unit-active');
unitHeader.classList.remove('cnet-unit-active');
}
}
@@ -144,11 +153,11 @@
* Add the active control type to tab displayed text.
*/
updateActiveControlType() {
const tabNavButton = this.getTabNavButton();
if (!tabNavButton) return;
const unitHeader = this.getUnitHeaderTextElement();
if (!unitHeader) return;
// Remove the control if exists
const controlTypeSuffix = tabNavButton.querySelector('.control-type-suffix');
const controlTypeSuffix = unitHeader.querySelector('.control-type-suffix');
if (controlTypeSuffix) controlTypeSuffix.remove();
// Add new suffix.
@@ -158,31 +167,7 @@
const span = document.createElement('span');
span.innerHTML = `[${controlType}]`;
span.classList.add('control-type-suffix');
tabNavButton.appendChild(span);
}
/**
* When 'Inpaint' control type is selected in img2img:
* - Make image input disabled
* - Clear existing image input
*/
updateImageInputState() {
if (!this.isImg2Img) return;
const tabNavButton = this.getTabNavButton();
if (!tabNavButton) return;
const controlType = this.getActiveControlType();
if (controlType.toLowerCase() === 'inpaint') {
this.inputImage.disabled = true;
this.inputImage.parentNode.addEventListener('click', imageInputDisabledAlert);
const removeButton = this.tab.querySelector(
'.cnet-input-image-group .cnet-image button[aria-label="Remove Image"]');
if (removeButton) removeButton.click();
} else {
this.inputImage.disabled = false;
this.inputImage.parentNode.removeEventListener('click', imageInputDisabledAlert);
}
unitHeader.appendChild(span);
}
attachEnabledButtonListener() {
@@ -200,22 +185,6 @@
}
}
/**
* Each time the active tab change, all tab nav buttons are cleared and
* regenerated by gradio. So we need to reapply the active states on
* them.
*/
attachTabNavChangeObserver() {
new MutationObserver((mutationsList) => {
for (const mutation of mutationsList) {
if (mutation.type === 'childList') {
this.updateActiveState();
this.updateActiveControlType();
}
}
}).observe(this.tabNav, { childList: true });
}
attachImageUploadListener() {
// Automatically check `enable` checkbox when image is uploaded.
this.inputImage.addEventListener('change', (event) => {
@@ -303,7 +272,7 @@
gradioApp().querySelectorAll('#controlnet').forEach(accordion => {
if (cnetAllAccordions.has(accordion)) return;
accordion.querySelectorAll('.cnet-unit-tab')
accordion.querySelectorAll('.input-accordion')
.forEach(tab => new ControlNetUnitTab(tab, accordion));
cnetAllAccordions.add(accordion);
});

View File

@@ -56,7 +56,7 @@
}
});
}
const tabs = gradioApp().querySelectorAll('.cnet-unit-tab');
const tabs = gradioApp().querySelectorAll('#controlnet .input-accordion');
tabs.forEach(tab => {
if (cnetOpenposeEditorRegisteredElements.has(tab)) return;
cnetOpenposeEditorRegisteredElements.add(tab);

View File

@@ -304,7 +304,7 @@
*/
async fetchFromControlNet(tabs) {
if (tabs.length === 0) return;
const isImg2Img = tabs[0].querySelector('.cnet-unit-enabled').id.includes('img2img');
const isImg2Img = tabs[0].querySelector('.cnet-mask-upload').id.includes('img2img');
const generationType = isImg2Img ? 'img2img' : 'txt2img';
const width = gradioApp().querySelector(`#${generationType}_width input[type=number]`).value;
const height = gradioApp().querySelector(`#${generationType}_height input[type=number]`).value;
@@ -401,7 +401,7 @@
}
const closeModalButton = accordion.querySelector('.cnet-photopea-edit .cnet-modal-close');
const tabs = accordion.querySelectorAll('.cnet-unit-tab');
const tabs = accordion.querySelectorAll('.controlnet .input-accordion');
const photopeaIframe = accordion.querySelector('.photopea-iframe');
const photopeaContext = new PhotopeaContext(photopeaIframe, tabs);

View File

@@ -59,7 +59,7 @@ class A1111Context:
)
@property
def img2img_non_inpaint_tabs(self) -> List[gr.components.IOComponent]:
def img2img_non_inpaint_tabs(self) -> Tuple[gr.components.IOComponent]:
return (
self.img2img_img2img_tab,
self.img2img_img2img_sketch_tab,
@@ -151,7 +151,8 @@ class ControlNetUiGroup(object):
self,
is_img2img: bool,
default_unit: external_code.ControlNetUnit,
photopea: Optional[Photopea],
unit_enabled: gr.Checkbox,
photopea: Optional[Photopea] = None,
):
# Whether callbacks have been registered.
self.callbacks_registered: bool = False
@@ -164,6 +165,8 @@ class ControlNetUiGroup(object):
self.webcam_enabled = False
self.webcam_mirrored = False
# Now the enabled checkbox is moved to display on InputAccordion.
self.enabled = unit_enabled
# Note: All gradio elements declared in `render` will be defined as member variable.
# Update counter to trigger a force update of UiControlNetUnit.
# This is useful when a field with no event subscriber available changes.
@@ -190,7 +193,6 @@ class ControlNetUiGroup(object):
self.webcam_enable = None
self.webcam_mirror = None
self.send_dimen_button = None
self.enabled = None
self.pixel_perfect = None
self.preprocessor_preview = None
self.mask_upload = None
@@ -393,18 +395,6 @@ class ControlNetUiGroup(object):
)
with FormRow(elem_classes=["controlnet_main_options"]):
self.enabled = gr.Checkbox(
label="Enable",
value=self.default_unit.enabled,
elem_id=f"{elem_id_tabname}_{tabname}_controlnet_enable_checkbox",
elem_classes=["cnet-unit-enabled"],
)
# self.low_vram = gr.Checkbox(
# label="Low VRAM",
# value=self.default_unit.low_vram,
# elem_id=f"{elem_id_tabname}_{tabname}_controlnet_low_vram_checkbox",
# visible=False, # Not needed now
# )
self.pixel_perfect = gr.Checkbox(
label="Pixel Perfect",
value=self.default_unit.pixel_perfect,

View File

@@ -5,7 +5,8 @@ import cv2
import torch
import modules.scripts as scripts
from modules import shared, script_callbacks, processing, masking, images
from modules import shared, script_callbacks, masking, images
from modules.ui_components import InputAccordion
from modules.api.api import decode_base64_to_image
import gradio as gr
@@ -58,33 +59,32 @@ class ControlNetForForgeOfficial(scripts.Script):
def show(self, is_img2img):
return scripts.AlwaysVisible
def uigroup(self, tabname: str, is_img2img: bool, elem_id_tabname: str, photopea: Optional[Photopea]) -> Tuple[ControlNetUiGroup, gr.State]:
default_unit = UiControlNetUnit(enabled=False, module="None", model="None")
group = ControlNetUiGroup(is_img2img, default_unit, photopea)
return group, group.render(tabname, elem_id_tabname)
def ui(self, is_img2img):
infotext = Infotext()
ui_groups = []
controls = []
max_models = shared.opts.data.get("control_net_unit_count", 3)
elem_id_tabname = ("img2img" if is_img2img else "txt2img") + "_controlnet"
gen_type = "img2img" if is_img2img else "txt2img"
elem_id_tabname = gen_type + "_controlnet"
default_unit = UiControlNetUnit(enabled=False, module="None", model="None")
with gr.Group(elem_id=elem_id_tabname):
with gr.Accordion(f"ControlNet Integrated", open=False, elem_id="controlnet"):
photopea = Photopea() if not shared.opts.data.get("controlnet_disable_photopea_edit", False) else None
if max_models > 1:
with gr.Tabs(elem_id=f"{elem_id_tabname}_tabs"):
for i in range(max_models):
with gr.Tab(f"ControlNet Unit {i}",
elem_classes=['cnet-unit-tab']):
group, state = self.uigroup(f"ControlNet-{i}", is_img2img, elem_id_tabname, photopea)
ui_groups.append(group)
controls.append(state)
else:
with gr.Column():
group, state = self.uigroup(f"ControlNet", is_img2img, elem_id_tabname, photopea)
ui_groups.append(group)
controls.append(state)
with gr.Accordion(f"ControlNet Integrated", open=False, elem_id="controlnet",
elem_classes=["controlnet"]):
photopea = (
Photopea()
if not shared.opts.data.get("controlnet_disable_photopea_edit", False)
else None
)
with gr.Row(elem_id=elem_id_tabname + "_accordions", elem_classes="accordions"):
for i in range(max_models):
with InputAccordion(
value=False,
label=f"ControlNet Unit {i}",
elem_classes=["cnet-unit-enabled"],
) as enable_unit:
group = ControlNetUiGroup(is_img2img, default_unit, enable_unit, photopea)
ui_groups.append(group)
controls.append(group.render(f"ControlNet-{i}", elem_id_tabname))
for i, ui_group in enumerate(ui_groups):
infotext.register_unit(i, ui_group)

View File

@@ -1,3 +1,29 @@
/* InputAccordion alignment */
/* Flex container */
.controlnet .svelte-vt1mxs {
display: flex;
flex-wrap: wrap;
flex-direction: row;
gap: 10px;
/* Adjusts the space between items */
}
.controlnet .input-accordion {
flex: 1 1 calc(50% - 10px);
/* Adjusts for the gap, default 2 columns */
/* Additional styling for items */
}
/* Media query for screens smaller than a specific width */
@media (max-width: 600px) {
/* Adjust the threshold as needed */
.controlnet .input-accordion {
flex: 1 1 100%;
/* Changes to 1 column when window width is ≤ 600px */
}
}
.cnet-modal {
display: none;
/* Hidden by default */