alemelis / sd-webui-ar

Select img aspect ratio from presets in sd-webui
217 stars 35 forks source link

Move extension to the top of the list #4

Open alemelis opened 1 year ago

alemelis commented 1 year ago

The extensions are ordered in alphabetical order.

Is the sorting done on title method? If so we can hack the UI by giving this extension a name like AAAspect ratio selector or similar?

Gerschel commented 1 year ago

Do you want it at the top of the list, or do you want it to not display in the list?

Gerschel commented 1 year ago

Try this. In the root of your application, the folder that has your readme and script folder, create a style.css file.

sd-webui-ar
├── .gitignore
├── README.md
├── aspect_ratios.txt
├── resolutions.txt
├── style.css
└── scripts
    └── sd-webui-ar.py

In style.css, paste this:

#script_list option[value^="Aspect Ratio picker"] {
  display: none;
}
alemelis commented 1 year ago

Do you want it at the top of the list, or do you want it to not display in the list?

I'd like the buttons to be as close as possible to the ui section where we set the image resolution. See here for example

the extension is in between others and quite far from where it should be. Ideally this shouldn't be an extension but a core part of the ui

Gerschel commented 1 year ago

There are several ways to handle this that I have done.

Fist we will stick with the process that you are trying to use, and that is "after_component", but if you want finer control, like injecting it inside the "dimensions" row instead of after/before it, then we can explore those options.

Gradio components have a flag called 'render'.
You can declare your row with a name using the 'as' keyword, and set the render to False.

with gr.Row(elem_id=f'{"img" if is_img2img else "txt"}2img_row_aspect_ratio', render=False) as self.ratio_row:

Then in the after_components or 'before_components' method, you can tell the row to render:

def after_component(self, component, **kwargs):
        if kwargs.get("elem_id") == "txt2img_width":
            self.ratio_row.render()

But, 'after_components' and 'before_components' is called before the 'ui' method.

So your choices become, define these rows somewhere like in the 'init' method, then use the 'with' keyword in the 'ui' method:

class MyScript():
    def __init__(self):
        self.ratio_row = gr.Row(elem_id=f'{"img" if is_img2img else "txt"}2img_row_aspect_ratio', render=False)

    def ui(self):
        with self.ratio_row:
            #define your components here

    def after_component(self, *args, **kwargs):
        #your logic to find target
            self.ratio_row.render()

Or move everything from the ui method to the after_component method:

class MyScript():
    def ui(self):
        pass

    def after_component(self, *args, **kwargs):
        if whatever_ matches:
            self.my_ui_stuff()
Xyem commented 1 year ago

On my machine, this extension is installed at extensions/20-aspect-ratio which makes it load very early, showing directly under the built-in controls. I've not had any issues with this extension following this directory naming convention, but I have with some others in case other users want to follow a similar naming convention.

I'm not suggesting the repo be named that though, I just handle my extensions differently and slightly more manually (as git submodules).

Very related: https://github.com/AUTOMATIC1111/stable-diffusion-webui/issues/8011

roamindev commented 1 year ago

@Gerschel What about waiting until the UI has loaded and moving the element using Javascript and OnUiUpdate()? Would that be bad practice? I'm new to Python, gradio, and A1111 SD webui (and its extensions)...and it seems this would be simple with JS and I wouldn't have to worry about what rendered where/when. But, I also know that just because I can run JS, doesn't mean I should. :]

Gerschel commented 1 year ago

@roamindev You would use a mutationObserver, this way it will trigger once the elements exist. I'm not up to date on whether the core app is in a shadowroot, I don't believe it is anymore, but the "addEventListener('DOMContentLoaded', ...)" fired too soon.

Then you could use "insertAdjacentElement" on the target.

Something like:

target = gradioApp().getElementById("some_element_id")
        /* SOME POSITIONING NOTES
        'beforebegin'
            <ele>
            'afterbegin'
                <otherele>
            'beforeend'
            </ele>
        'afterend'
        */
target.insertAdjacentElement("afterend", gradioApp().querySelector("my_row"))
schoenid commented 7 months ago

Very complicated. What about updates? Are the settings kept after updates?

For the 08:15 user there should be a possibility to define, where it should stand in the accordion list. I also would prefer it as the first item.

Like now, it is ugly: image

schoenid commented 7 months ago

Adding the following to user.css

#txt2img_script_container .gr-group.gradio-group.svelte-iyf88w:has(#txt2img_container_aspect_ratio) {
    order: 1;
    margin-top:0.5em;
}

#txt2img_script_container .gr-group.gradio-group.svelte-iyf88w {
    order: 2;
}

#txt2img_script_container .form.svelte-sfqy0y { /* Script tab */
    order: 3;
}

gives the following result: image

This is stable against updates, as long the extension ID in CSS is not altered by the maintainers.