Abstract UI components, add UI ordering/sorting (#13)
* Abstract UI components - Created an ABC for our UI components - Extract components to its own module - Add concept of sorting including option and defaults - Update test for _safe_opt when attr is None - Change import structure to avoid circular importspull/15/head
parent
2d3343c911
commit
18d4199854
|
|
@ -0,0 +1,79 @@
|
|||
from abc import ABC
|
||||
from abc import abstractmethod
|
||||
from functools import partial
|
||||
|
||||
import gradio as gr
|
||||
|
||||
import aspect_ratio_helper._constants as _constants
|
||||
import aspect_ratio_helper._settings as _settings
|
||||
import aspect_ratio_helper._util as _util
|
||||
|
||||
|
||||
class ArhUIComponent(ABC):
|
||||
|
||||
def __init__(self, script):
|
||||
self.script = script
|
||||
|
||||
@abstractmethod
|
||||
def render(self): ...
|
||||
|
||||
@staticmethod
|
||||
@abstractmethod
|
||||
def should_show() -> bool: ...
|
||||
|
||||
|
||||
class MaxDimensionScaler(ArhUIComponent):
|
||||
def render(self):
|
||||
inputs = outputs = [self.script.wc, self.script.hc]
|
||||
with gr.Row():
|
||||
max_dimension = gr.inputs.Slider(
|
||||
minimum=_constants.MIN_DIMENSION,
|
||||
maximum=_constants.MAX_DIMENSION,
|
||||
step=1,
|
||||
default=_settings.safe_opt(
|
||||
_constants.ARH_MAX_WIDTH_OR_HEIGHT_KEY,
|
||||
),
|
||||
label='Maximum width or height (whichever is higher)',
|
||||
)
|
||||
gr.Button(value='Scale to maximum width or height').click(
|
||||
fn=_util.scale_dimensions_to,
|
||||
inputs=[*inputs, max_dimension],
|
||||
outputs=outputs,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def should_show() -> bool:
|
||||
return _settings.safe_opt(_constants.ARH_SHOW_MAX_WIDTH_OR_HEIGHT_KEY)
|
||||
|
||||
|
||||
class PredefinedPercentageButtons(ArhUIComponent):
|
||||
|
||||
def render(self):
|
||||
inputs = outputs = [self.script.wc, self.script.hc]
|
||||
with gr.Column(variant='panel'), gr.Row(variant='compact'):
|
||||
pps = _settings.safe_opt(_constants.ARH_PREDEFINED_PERCENTAGES_KEY)
|
||||
percentages = [abs(int(x)) for x in pps.split(',')]
|
||||
for percentage in percentages:
|
||||
display = self.display_func(percentage)
|
||||
gr.Button(value=display).click(
|
||||
fn=partial(
|
||||
_util.scale_by_percentage,
|
||||
pct=percentage / 100,
|
||||
),
|
||||
inputs=inputs,
|
||||
outputs=outputs,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def should_show() -> bool:
|
||||
return _settings.safe_opt(
|
||||
_constants.ARH_SHOW_PREDEFINED_PERCENTAGES_KEY,
|
||||
)
|
||||
|
||||
@property
|
||||
def display_func(self) -> callable:
|
||||
return _settings.PREDEFINED_PERCENTAGES_DISPLAY_MAP.get(
|
||||
_settings.safe_opt(
|
||||
_constants.ARH_PREDEFINED_PERCENTAGES_DISPLAY_KEY,
|
||||
),
|
||||
)
|
||||
|
|
@ -1,23 +1,15 @@
|
|||
_EXTENSION_NAME = 'Aspect Ratio Helper'
|
||||
EXTENSION_NAME = 'Aspect Ratio Helper'
|
||||
|
||||
_MAX_DIMENSION = 2048
|
||||
_MIN_DIMENSION = 64
|
||||
MAX_DIMENSION = 2048
|
||||
MIN_DIMENSION = 64
|
||||
|
||||
_ARH_EXPAND_BY_DEFAULT_KEY = 'arh_expand_by_default'
|
||||
_ARH_SHOW_MAX_WIDTH_OR_HEIGHT_KEY = 'arh_show_max_width_or_height'
|
||||
_ARH_MAX_WIDTH_OR_HEIGHT_KEY = 'arh_max_width_or_height'
|
||||
_ARH_SHOW_PREDEFINED_PERCENTAGES_KEY = 'arh_show_predefined_percentages'
|
||||
_ARH_PREDEFINED_PERCENTAGES_KEY = 'arh_predefined_percentages'
|
||||
_ARH_PREDEFINED_PERCENTAGES_DISPLAY_KEY \
|
||||
ARH_EXPAND_BY_DEFAULT_KEY = 'arh_expand_by_default'
|
||||
ARH_UI_COMPONENT_ORDER_KEY = 'arh_ui_component_order_key'
|
||||
ARH_SHOW_MAX_WIDTH_OR_HEIGHT_KEY = 'arh_show_max_width_or_height'
|
||||
ARH_MAX_WIDTH_OR_HEIGHT_KEY = 'arh_max_width_or_height'
|
||||
ARH_SHOW_PREDEFINED_PERCENTAGES_KEY = 'arh_show_predefined_percentages'
|
||||
ARH_PREDEFINED_PERCENTAGES_KEY = 'arh_predefined_percentages'
|
||||
ARH_PREDEFINED_PERCENTAGES_DISPLAY_KEY \
|
||||
= 'arh_predefined_percentages_display_key'
|
||||
|
||||
_DEFAULT_PERCENTAGES_DISPLAY_KEY \
|
||||
DEFAULT_PERCENTAGES_DISPLAY_KEY \
|
||||
= 'Incremental/decremental percentage (-50%, +50%)'
|
||||
_OPT_KEY_TO_DEFAULT_MAP = {
|
||||
_ARH_EXPAND_BY_DEFAULT_KEY: False,
|
||||
_ARH_SHOW_MAX_WIDTH_OR_HEIGHT_KEY: True,
|
||||
_ARH_MAX_WIDTH_OR_HEIGHT_KEY: _MAX_DIMENSION / 2,
|
||||
_ARH_SHOW_PREDEFINED_PERCENTAGES_KEY: True,
|
||||
_ARH_PREDEFINED_PERCENTAGES_KEY: '25, 50, 75, 125, 150, 175, 200',
|
||||
_ARH_PREDEFINED_PERCENTAGES_DISPLAY_KEY: _DEFAULT_PERCENTAGES_DISPLAY_KEY,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,95 +1,163 @@
|
|||
import gradio as gr
|
||||
from modules import shared
|
||||
|
||||
from aspect_ratio_helper._constants import _ARH_EXPAND_BY_DEFAULT_KEY
|
||||
from aspect_ratio_helper._constants import _ARH_MAX_WIDTH_OR_HEIGHT_KEY
|
||||
from aspect_ratio_helper._constants import \
|
||||
_ARH_PREDEFINED_PERCENTAGES_DISPLAY_KEY
|
||||
from aspect_ratio_helper._constants import _ARH_PREDEFINED_PERCENTAGES_KEY
|
||||
from aspect_ratio_helper._constants import _ARH_SHOW_MAX_WIDTH_OR_HEIGHT_KEY
|
||||
from aspect_ratio_helper._constants import _ARH_SHOW_PREDEFINED_PERCENTAGES_KEY
|
||||
from aspect_ratio_helper._constants import _DEFAULT_PERCENTAGES_DISPLAY_KEY
|
||||
from aspect_ratio_helper._constants import _EXTENSION_NAME
|
||||
from aspect_ratio_helper._constants import _MAX_DIMENSION
|
||||
from aspect_ratio_helper._constants import _MIN_DIMENSION
|
||||
from aspect_ratio_helper._constants import _OPT_KEY_TO_DEFAULT_MAP
|
||||
from aspect_ratio_helper._util import _display_minus_and_plus
|
||||
from aspect_ratio_helper._util import _display_multiplication
|
||||
from aspect_ratio_helper._util import _display_raw_percentage
|
||||
from aspect_ratio_helper._util import _safe_opt_util
|
||||
import aspect_ratio_helper._components as _components
|
||||
import aspect_ratio_helper._constants as _constants
|
||||
import aspect_ratio_helper._util as _util
|
||||
|
||||
_PREDEFINED_PERCENTAGES_DISPLAY_MAP = {
|
||||
_DEFAULT_PERCENTAGES_DISPLAY_KEY: _display_minus_and_plus,
|
||||
'Raw percentage (50%, 150%)': _display_raw_percentage,
|
||||
'Multiplication (x0.5, x1.5)': _display_multiplication,
|
||||
PREDEFINED_PERCENTAGES_DISPLAY_MAP = {
|
||||
_constants.DEFAULT_PERCENTAGES_DISPLAY_KEY: _util.display_minus_and_plus,
|
||||
'Raw percentage (50%, 150%)': _util.display_raw_percentage,
|
||||
'Multiplication (x0.5, x1.5)': _util.display_multiplication,
|
||||
}
|
||||
|
||||
ELEMENTS = (
|
||||
_components.MaxDimensionScaler,
|
||||
_components.PredefinedPercentageButtons,
|
||||
)
|
||||
|
||||
DEFAULT_UI_COMPONENT_ORDER_KEY = ', '.join(
|
||||
[e.__name__ for e in ELEMENTS], # noqa
|
||||
)
|
||||
OPT_KEY_TO_DEFAULT_MAP = {
|
||||
_constants.ARH_EXPAND_BY_DEFAULT_KEY: False,
|
||||
_constants.ARH_UI_COMPONENT_ORDER_KEY:
|
||||
DEFAULT_UI_COMPONENT_ORDER_KEY,
|
||||
_constants.ARH_SHOW_MAX_WIDTH_OR_HEIGHT_KEY: True,
|
||||
_constants.ARH_MAX_WIDTH_OR_HEIGHT_KEY:
|
||||
_constants.MAX_DIMENSION / 2,
|
||||
_constants.ARH_SHOW_PREDEFINED_PERCENTAGES_KEY: True,
|
||||
_constants.ARH_PREDEFINED_PERCENTAGES_KEY:
|
||||
'25, 50, 75, 125, 150, 175, 200',
|
||||
_constants.ARH_PREDEFINED_PERCENTAGES_DISPLAY_KEY:
|
||||
_constants.DEFAULT_PERCENTAGES_DISPLAY_KEY,
|
||||
}
|
||||
|
||||
|
||||
def _safe_opt(key):
|
||||
return _safe_opt_util(shared.opts, key)
|
||||
def safe_opt(key):
|
||||
return _util.safe_opt_util(shared.opts, key, OPT_KEY_TO_DEFAULT_MAP)
|
||||
|
||||
|
||||
def sort_elements_by_keys(
|
||||
elements: list[_components.ArhUIComponent],
|
||||
) -> list[_components.ArhUIComponent]:
|
||||
ordered_component_keys = safe_opt(
|
||||
_constants.ARH_UI_COMPONENT_ORDER_KEY,
|
||||
).split(',')
|
||||
try:
|
||||
component_key_to_order_dict = {
|
||||
key: order for order, key in enumerate(
|
||||
[k.strip() for k in ordered_component_keys],
|
||||
)
|
||||
}
|
||||
return sorted(
|
||||
elements,
|
||||
key=lambda c: component_key_to_order_dict.get(
|
||||
c.__class__.__name__,
|
||||
),
|
||||
)
|
||||
except ValueError:
|
||||
print(
|
||||
f'{_constants.EXTENSION_NAME} UI component order is erroneous. '
|
||||
f'Defaulting to regular order, to fix this, please use'
|
||||
f'the intended syntax for the setting, i.e '
|
||||
f'"{DEFAULT_UI_COMPONENT_ORDER_KEY}"',
|
||||
)
|
||||
return elements
|
||||
|
||||
|
||||
def on_ui_settings():
|
||||
section = 'aspect_ratio_helper', _EXTENSION_NAME
|
||||
section = 'aspect_ratio_helper', _constants.EXTENSION_NAME
|
||||
shared.opts.add_option(
|
||||
key=_ARH_EXPAND_BY_DEFAULT_KEY,
|
||||
key=_constants.ARH_EXPAND_BY_DEFAULT_KEY,
|
||||
info=shared.OptionInfo(
|
||||
default=_OPT_KEY_TO_DEFAULT_MAP[_ARH_EXPAND_BY_DEFAULT_KEY],
|
||||
default=OPT_KEY_TO_DEFAULT_MAP.get(
|
||||
_constants.ARH_EXPAND_BY_DEFAULT_KEY,
|
||||
),
|
||||
label='Expand by default',
|
||||
section=section,
|
||||
),
|
||||
)
|
||||
shared.opts.add_option(
|
||||
key=_ARH_SHOW_MAX_WIDTH_OR_HEIGHT_KEY,
|
||||
key=_constants.ARH_UI_COMPONENT_ORDER_KEY,
|
||||
info=shared.OptionInfo(
|
||||
default=_OPT_KEY_TO_DEFAULT_MAP[_ARH_SHOW_MAX_WIDTH_OR_HEIGHT_KEY],
|
||||
default=OPT_KEY_TO_DEFAULT_MAP.get(
|
||||
_constants.ARH_UI_COMPONENT_ORDER_KEY,
|
||||
),
|
||||
label='UI Component order',
|
||||
# todo: temporary drop-down to avoid user error!
|
||||
# we only have two components so 2 possible orders.
|
||||
# however, this will exponentially grow with more components.
|
||||
# if that happens, permutations is impractical, revisit then.
|
||||
component=gr.Dropdown,
|
||||
component_args=lambda: {
|
||||
'choices': (
|
||||
DEFAULT_UI_COMPONENT_ORDER_KEY,
|
||||
', '.join(
|
||||
DEFAULT_UI_COMPONENT_ORDER_KEY.split(',')[::-1],
|
||||
),
|
||||
),
|
||||
},
|
||||
section=section,
|
||||
),
|
||||
)
|
||||
shared.opts.add_option(
|
||||
key=_constants.ARH_SHOW_MAX_WIDTH_OR_HEIGHT_KEY,
|
||||
info=shared.OptionInfo(
|
||||
default=OPT_KEY_TO_DEFAULT_MAP.get(
|
||||
_constants.ARH_SHOW_MAX_WIDTH_OR_HEIGHT_KEY,
|
||||
),
|
||||
label='Show maximum width or height button',
|
||||
section=section,
|
||||
),
|
||||
)
|
||||
shared.opts.add_option(
|
||||
key=_ARH_MAX_WIDTH_OR_HEIGHT_KEY,
|
||||
key=_constants.ARH_MAX_WIDTH_OR_HEIGHT_KEY,
|
||||
info=shared.OptionInfo(
|
||||
default=_OPT_KEY_TO_DEFAULT_MAP[_ARH_MAX_WIDTH_OR_HEIGHT_KEY],
|
||||
default=OPT_KEY_TO_DEFAULT_MAP.get(
|
||||
_constants.ARH_MAX_WIDTH_OR_HEIGHT_KEY,
|
||||
),
|
||||
label='Maximum width or height default',
|
||||
component=gr.Slider,
|
||||
component_args={
|
||||
'minimum': _MIN_DIMENSION,
|
||||
'maximum': _MAX_DIMENSION,
|
||||
'minimum': _constants.MIN_DIMENSION,
|
||||
'maximum': _constants.MAX_DIMENSION,
|
||||
'step': 1,
|
||||
},
|
||||
section=section,
|
||||
),
|
||||
)
|
||||
shared.opts.add_option(
|
||||
key=_ARH_SHOW_PREDEFINED_PERCENTAGES_KEY,
|
||||
key=_constants.ARH_SHOW_PREDEFINED_PERCENTAGES_KEY,
|
||||
info=shared.OptionInfo(
|
||||
default=_OPT_KEY_TO_DEFAULT_MAP[
|
||||
_ARH_SHOW_PREDEFINED_PERCENTAGES_KEY
|
||||
],
|
||||
default=OPT_KEY_TO_DEFAULT_MAP.get(
|
||||
_constants.ARH_SHOW_PREDEFINED_PERCENTAGES_KEY,
|
||||
),
|
||||
label='Show predefined percentage buttons',
|
||||
section=section,
|
||||
),
|
||||
)
|
||||
shared.opts.add_option(
|
||||
key=_ARH_PREDEFINED_PERCENTAGES_KEY,
|
||||
key=_constants.ARH_PREDEFINED_PERCENTAGES_KEY,
|
||||
info=shared.OptionInfo(
|
||||
default=_OPT_KEY_TO_DEFAULT_MAP[_ARH_PREDEFINED_PERCENTAGES_KEY],
|
||||
default=OPT_KEY_TO_DEFAULT_MAP.get(
|
||||
_constants.ARH_PREDEFINED_PERCENTAGES_KEY,
|
||||
),
|
||||
label='Predefined percentage buttons, applied to dimensions (75, '
|
||||
'125, 150)',
|
||||
section=section,
|
||||
),
|
||||
)
|
||||
shared.opts.add_option(
|
||||
key=_ARH_PREDEFINED_PERCENTAGES_DISPLAY_KEY,
|
||||
key=_constants.ARH_PREDEFINED_PERCENTAGES_DISPLAY_KEY,
|
||||
info=shared.OptionInfo(
|
||||
default=_OPT_KEY_TO_DEFAULT_MAP[
|
||||
_ARH_PREDEFINED_PERCENTAGES_DISPLAY_KEY
|
||||
],
|
||||
default=OPT_KEY_TO_DEFAULT_MAP.get(
|
||||
_constants.ARH_PREDEFINED_PERCENTAGES_DISPLAY_KEY,
|
||||
),
|
||||
label='Predefined percentage display format',
|
||||
component=gr.Dropdown,
|
||||
component_args=lambda: {
|
||||
'choices': tuple(_PREDEFINED_PERCENTAGES_DISPLAY_MAP.keys()),
|
||||
'choices': tuple(PREDEFINED_PERCENTAGES_DISPLAY_MAP.keys()),
|
||||
},
|
||||
section=section,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,64 +1,66 @@
|
|||
import contextlib
|
||||
|
||||
from aspect_ratio_helper._constants import _MAX_DIMENSION
|
||||
from aspect_ratio_helper._constants import _MIN_DIMENSION
|
||||
from aspect_ratio_helper._constants import _OPT_KEY_TO_DEFAULT_MAP
|
||||
import aspect_ratio_helper._constants as _const
|
||||
|
||||
|
||||
def _safe_opt_util(shared_opts, key):
|
||||
def safe_opt_util(shared_opts, key, default_key_map: dict[str, object]):
|
||||
# attempt to retrieve key from shared options
|
||||
with contextlib.suppress(AttributeError):
|
||||
return shared_opts.__getattr__(key)
|
||||
value = shared_opts.__getattr__(key)
|
||||
if value is not None:
|
||||
return value
|
||||
|
||||
# attempt to retrieve default, and last resort the constant default
|
||||
return shared_opts.get_default(key) or _OPT_KEY_TO_DEFAULT_MAP.get(key)
|
||||
return shared_opts.get_default(key) or default_key_map.get(key)
|
||||
|
||||
|
||||
def _display_multiplication(num):
|
||||
def display_multiplication(num) -> str:
|
||||
return f'x{round(num / 100, 3)}'
|
||||
|
||||
|
||||
def _display_raw_percentage(num):
|
||||
def display_raw_percentage(num) -> str:
|
||||
return f'{num}%'
|
||||
|
||||
|
||||
def _display_minus_and_plus(num):
|
||||
def display_minus_and_plus(num) -> str:
|
||||
num -= 100
|
||||
if num > 0:
|
||||
return f'+{num}%'
|
||||
return f'{num}%'
|
||||
|
||||
|
||||
def _scale_by_percentage(width, height, pct):
|
||||
def scale_by_percentage(width, height, pct) -> tuple[int, int]:
|
||||
aspect_ratio = float(width) / float(height)
|
||||
step = (pct - 1.0)
|
||||
new_width = int(round(width * (1.0 + step)))
|
||||
new_height = int(round(new_width / aspect_ratio))
|
||||
return _clamp_to_boundaries(new_width, new_height, aspect_ratio)
|
||||
return clamp_to_boundaries(new_width, new_height, aspect_ratio)
|
||||
|
||||
|
||||
def _scale_dimensions_to_max_dimension(width, height, max_dim):
|
||||
def scale_dimensions_to(
|
||||
width, height, max_dim,
|
||||
) -> tuple[int, int]:
|
||||
aspect_ratio = float(width) / float(height)
|
||||
if width > height:
|
||||
new_width = max_dim
|
||||
new_height = max(int(round(max_dim / aspect_ratio)), 1)
|
||||
new_height = int(round(max_dim / aspect_ratio))
|
||||
else:
|
||||
new_height = max_dim
|
||||
new_width = max(int(round(max_dim * aspect_ratio)), 1)
|
||||
return _clamp_to_boundaries(new_width, new_height, aspect_ratio)
|
||||
new_width = int(round(max_dim * aspect_ratio))
|
||||
return clamp_to_boundaries(new_width, new_height, aspect_ratio)
|
||||
|
||||
|
||||
def _clamp_to_boundaries(width, height, aspect_ratio):
|
||||
if width > _MAX_DIMENSION:
|
||||
width = _MAX_DIMENSION
|
||||
def clamp_to_boundaries(width, height, aspect_ratio) -> tuple[int, int]:
|
||||
if width > _const.MAX_DIMENSION:
|
||||
width = _const.MAX_DIMENSION
|
||||
height = int(round(width / aspect_ratio))
|
||||
if height > _MAX_DIMENSION:
|
||||
height = _MAX_DIMENSION
|
||||
if height > _const.MAX_DIMENSION:
|
||||
height = _const.MAX_DIMENSION
|
||||
width = int(round(height * aspect_ratio))
|
||||
if width < _MIN_DIMENSION:
|
||||
width = _MIN_DIMENSION
|
||||
if width < _const.MIN_DIMENSION:
|
||||
width = _const.MIN_DIMENSION
|
||||
height = int(round(width / aspect_ratio))
|
||||
if height < _MIN_DIMENSION:
|
||||
height = _MIN_DIMENSION
|
||||
if height < _const.MIN_DIMENSION:
|
||||
height = _const.MIN_DIMENSION
|
||||
width = int(round(height * aspect_ratio))
|
||||
return width, height
|
||||
|
|
|
|||
|
|
@ -1,96 +1,63 @@
|
|||
from functools import partial
|
||||
|
||||
import gradio as gr
|
||||
from modules import script_callbacks
|
||||
from modules import scripts
|
||||
|
||||
from aspect_ratio_helper._constants import _ARH_EXPAND_BY_DEFAULT_KEY
|
||||
from aspect_ratio_helper._constants import _ARH_MAX_WIDTH_OR_HEIGHT_KEY
|
||||
from aspect_ratio_helper._constants import \
|
||||
_ARH_PREDEFINED_PERCENTAGES_DISPLAY_KEY
|
||||
from aspect_ratio_helper._constants import _ARH_PREDEFINED_PERCENTAGES_KEY
|
||||
from aspect_ratio_helper._constants import _ARH_SHOW_MAX_WIDTH_OR_HEIGHT_KEY
|
||||
from aspect_ratio_helper._constants import _ARH_SHOW_PREDEFINED_PERCENTAGES_KEY
|
||||
from aspect_ratio_helper._constants import _EXTENSION_NAME
|
||||
from aspect_ratio_helper._constants import _MAX_DIMENSION
|
||||
from aspect_ratio_helper._constants import _MIN_DIMENSION
|
||||
from aspect_ratio_helper._settings import _PREDEFINED_PERCENTAGES_DISPLAY_MAP
|
||||
from aspect_ratio_helper._settings import _safe_opt
|
||||
from aspect_ratio_helper._settings import on_ui_settings
|
||||
from aspect_ratio_helper._util import _scale_by_percentage
|
||||
from aspect_ratio_helper._util import _scale_dimensions_to_max_dimension
|
||||
import aspect_ratio_helper._constants as _constants
|
||||
import aspect_ratio_helper._settings as _settings
|
||||
|
||||
|
||||
class AspectRatioStepScript(scripts.Script):
|
||||
|
||||
def title(self):
|
||||
return _EXTENSION_NAME
|
||||
def __init__(self):
|
||||
self.t2i_w = None
|
||||
self.t2i_h = None
|
||||
self.i2i_w = None
|
||||
self.i2i_h = None
|
||||
self.wc = None
|
||||
self.hc = None
|
||||
|
||||
def show(self, is_img2img):
|
||||
def title(self) -> str:
|
||||
return _constants.EXTENSION_NAME
|
||||
|
||||
def show(self, _) -> scripts.AlwaysVisible:
|
||||
return scripts.AlwaysVisible
|
||||
|
||||
def ui(self, is_img2img):
|
||||
if not any([
|
||||
_safe_opt(_ARH_SHOW_MAX_WIDTH_OR_HEIGHT_KEY),
|
||||
_safe_opt(_ARH_SHOW_PREDEFINED_PERCENTAGES_KEY),
|
||||
]):
|
||||
return # return early as no 'show' options enabled
|
||||
with (
|
||||
gr.Group(),
|
||||
gr.Accordion(
|
||||
_EXTENSION_NAME,
|
||||
open=_safe_opt(_ARH_EXPAND_BY_DEFAULT_KEY),
|
||||
),
|
||||
if is_img2img:
|
||||
self.wc, self.hc = self.i2i_w, self.i2i_h
|
||||
else:
|
||||
self.wc, self.hc = self.t2i_h, self.t2i_w
|
||||
|
||||
elements = _settings.sort_elements_by_keys(
|
||||
[element(self) for element in _settings.ELEMENTS],
|
||||
)
|
||||
|
||||
if not any(element.should_show() for element in elements):
|
||||
return # no elements should render, so just return.
|
||||
|
||||
start_expanded: bool = _settings.safe_opt(
|
||||
_constants.ARH_EXPAND_BY_DEFAULT_KEY,
|
||||
)
|
||||
with gr.Group(), gr.Accordion(
|
||||
_constants.EXTENSION_NAME,
|
||||
open=start_expanded,
|
||||
):
|
||||
if is_img2img:
|
||||
inputs = outputs = [self.i2i_w, self.i2i_h]
|
||||
else:
|
||||
inputs = outputs = [self.t2i_w, self.t2i_h]
|
||||
for element in elements:
|
||||
if element.should_show():
|
||||
element.render()
|
||||
|
||||
if _safe_opt(_ARH_SHOW_MAX_WIDTH_OR_HEIGHT_KEY):
|
||||
with gr.Row():
|
||||
max_dimension = gr.inputs.Slider(
|
||||
minimum=_MIN_DIMENSION,
|
||||
maximum=_MAX_DIMENSION,
|
||||
step=1,
|
||||
default=_safe_opt(_ARH_MAX_WIDTH_OR_HEIGHT_KEY),
|
||||
label='Maximum width or height (whichever is higher)',
|
||||
)
|
||||
gr.Button(value='Scale to maximum width or height').click(
|
||||
fn=_scale_dimensions_to_max_dimension,
|
||||
inputs=[*inputs, max_dimension],
|
||||
outputs=outputs,
|
||||
)
|
||||
|
||||
if _safe_opt(_ARH_SHOW_PREDEFINED_PERCENTAGES_KEY):
|
||||
display_func = _PREDEFINED_PERCENTAGES_DISPLAY_MAP.get(
|
||||
_safe_opt(_ARH_PREDEFINED_PERCENTAGES_DISPLAY_KEY),
|
||||
)
|
||||
with gr.Column(variant='panel'), gr.Row(variant='compact'):
|
||||
pps = _safe_opt(_ARH_PREDEFINED_PERCENTAGES_KEY)
|
||||
percentages = [
|
||||
abs(int(x)) for x in pps.split(',')
|
||||
]
|
||||
for percentage in percentages:
|
||||
gr.Button(value=display_func(percentage)).click(
|
||||
fn=partial(
|
||||
_scale_by_percentage, pct=percentage / 100,
|
||||
),
|
||||
inputs=inputs,
|
||||
outputs=outputs,
|
||||
)
|
||||
|
||||
def after_component(self, component, **kwargs):
|
||||
def after_component(self, component: gr.components.Component, **kwargs):
|
||||
element_id = kwargs.get('elem_id')
|
||||
|
||||
if element_id == 'txt2img_width':
|
||||
self.t2i_w = component
|
||||
elif element_id == 'txt2img_height':
|
||||
self.t2i_h = component
|
||||
elif element_id == 'img2img_width':
|
||||
self.i2i_w = component
|
||||
elif element_id == 'img2img_height':
|
||||
self.i2i_h = component
|
||||
if isinstance(component, gr.components.Slider):
|
||||
if element_id == 'txt2img_width':
|
||||
self.t2i_w: gr.components.Slider = component
|
||||
elif element_id == 'txt2img_height':
|
||||
self.t2i_h: gr.components.Slider = component
|
||||
elif element_id == 'img2img_width':
|
||||
self.i2i_w: gr.components.Slider = component
|
||||
elif element_id == 'img2img_height':
|
||||
self.i2i_h: gr.components.Slider = component
|
||||
|
||||
|
||||
script_callbacks.on_ui_settings(on_ui_settings)
|
||||
script_callbacks.on_ui_settings(_settings.on_ui_settings)
|
||||
|
|
|
|||
|
|
@ -1,13 +1,7 @@
|
|||
import pytest
|
||||
|
||||
from aspect_ratio_helper._util import _display_minus_and_plus
|
||||
from aspect_ratio_helper._util import _display_multiplication
|
||||
from aspect_ratio_helper._util import _display_raw_percentage
|
||||
from aspect_ratio_helper._util import _MAX_DIMENSION
|
||||
from aspect_ratio_helper._util import _MIN_DIMENSION
|
||||
from aspect_ratio_helper._util import _safe_opt_util
|
||||
from aspect_ratio_helper._util import _scale_by_percentage
|
||||
from aspect_ratio_helper._util import _scale_dimensions_to_max_dimension
|
||||
import aspect_ratio_helper._constants as _constants
|
||||
import aspect_ratio_helper._util as _util
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
|
@ -20,7 +14,7 @@ from aspect_ratio_helper._util import _scale_dimensions_to_max_dimension
|
|||
],
|
||||
)
|
||||
def test_display_multiplication(num, expected):
|
||||
assert _display_multiplication(num) == expected
|
||||
assert _util.display_multiplication(num) == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
|
@ -34,7 +28,7 @@ def test_display_multiplication(num, expected):
|
|||
],
|
||||
)
|
||||
def test_display_raw_percentage(num, expected):
|
||||
assert _display_raw_percentage(num) == expected
|
||||
assert _util.display_raw_percentage(num) == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
|
@ -48,7 +42,7 @@ def test_display_raw_percentage(num, expected):
|
|||
],
|
||||
)
|
||||
def test_display_minus_and_plus(num, expected_output):
|
||||
assert _display_minus_and_plus(num) == expected_output
|
||||
assert _util.display_minus_and_plus(num) == expected_output
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
|
@ -60,25 +54,25 @@ def test_display_minus_and_plus(num, expected_output):
|
|||
pytest.param(100, 200, 0.9, (90, 180), id='10_percent_scale_down'),
|
||||
pytest.param(100, 200, 0.0, (64, 128), id='scale_full_down'),
|
||||
pytest.param(
|
||||
_MIN_DIMENSION - 1,
|
||||
_MIN_DIMENSION - 1,
|
||||
_constants.MIN_DIMENSION - 1,
|
||||
_constants.MIN_DIMENSION - 1,
|
||||
0.5,
|
||||
(_MIN_DIMENSION, _MIN_DIMENSION),
|
||||
id='scale_below_min_dimension',
|
||||
(_constants.MIN_DIMENSION, _constants.MIN_DIMENSION),
|
||||
id='scale_belowMIN_DIMENSION',
|
||||
),
|
||||
pytest.param(
|
||||
_MAX_DIMENSION + 1,
|
||||
_MAX_DIMENSION + 1,
|
||||
_constants.MAX_DIMENSION + 1,
|
||||
_constants.MAX_DIMENSION + 1,
|
||||
2.0,
|
||||
(_MAX_DIMENSION, _MAX_DIMENSION),
|
||||
id='scale_above_max_dimension',
|
||||
(_constants.MAX_DIMENSION, _constants.MAX_DIMENSION),
|
||||
id='scale_aboveMAX_DIMENSION',
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_scale_by_percentage(
|
||||
width, height, pct, expected,
|
||||
):
|
||||
assert _scale_by_percentage(
|
||||
assert _util.scale_by_percentage(
|
||||
width, height, pct,
|
||||
) == expected
|
||||
|
||||
|
|
@ -88,66 +82,70 @@ def test_scale_by_percentage(
|
|||
[
|
||||
pytest.param(
|
||||
100, 200, 400, (200, 400),
|
||||
id='scale_up_to_max_dimension_horizontally',
|
||||
id='scale_up_toMAX_DIMENSION_horizontally',
|
||||
),
|
||||
pytest.param(
|
||||
200, 100, 400, (400, 200),
|
||||
id='scale_up_to_max_dimension_vertically',
|
||||
id='scale_up_toMAX_DIMENSION_vertically',
|
||||
),
|
||||
pytest.param(
|
||||
400, 64, 400, (400, 64),
|
||||
id='no_scale_up_needed_with_max_dimension_width',
|
||||
id='no_scale_up_needed_withMAX_DIMENSION_width',
|
||||
),
|
||||
pytest.param(
|
||||
64, 400, 400, (64, 400),
|
||||
id='no_scale_up_needed_with_max_dimension_height',
|
||||
id='no_scale_up_needed_withMAX_DIMENSION_height',
|
||||
),
|
||||
pytest.param(
|
||||
_MIN_DIMENSION, _MIN_DIMENSION, _MAX_DIMENSION,
|
||||
(_MAX_DIMENSION, _MAX_DIMENSION),
|
||||
_constants.MIN_DIMENSION,
|
||||
_constants.MIN_DIMENSION,
|
||||
_constants.MAX_DIMENSION,
|
||||
(_constants.MAX_DIMENSION, _constants.MAX_DIMENSION),
|
||||
id='scale_from_min_to_max',
|
||||
),
|
||||
pytest.param(
|
||||
_MAX_DIMENSION, _MAX_DIMENSION, _MIN_DIMENSION,
|
||||
(_MIN_DIMENSION, _MIN_DIMENSION),
|
||||
_constants.MAX_DIMENSION,
|
||||
_constants.MAX_DIMENSION,
|
||||
_constants.MIN_DIMENSION,
|
||||
(_constants.MIN_DIMENSION, _constants.MIN_DIMENSION),
|
||||
id='scale_from_max_to_min',
|
||||
),
|
||||
pytest.param(
|
||||
_MIN_DIMENSION, 32, _MIN_DIMENSION,
|
||||
(128, _MIN_DIMENSION),
|
||||
_constants.MIN_DIMENSION, 32, _constants.MIN_DIMENSION,
|
||||
(128, _constants.MIN_DIMENSION),
|
||||
id='scale_below_min_height_dimension_clamps_retains_ar',
|
||||
),
|
||||
pytest.param(
|
||||
32, _MIN_DIMENSION, _MIN_DIMENSION,
|
||||
(_MIN_DIMENSION, 128),
|
||||
32, _constants.MIN_DIMENSION, _constants.MIN_DIMENSION,
|
||||
(_constants.MIN_DIMENSION, 128),
|
||||
id='scale_below_min_width_dimension_clamps_retains_ar',
|
||||
),
|
||||
pytest.param(
|
||||
_MAX_DIMENSION, 4096, _MAX_DIMENSION,
|
||||
(1024, _MAX_DIMENSION),
|
||||
_constants.MAX_DIMENSION, 4096, _constants.MAX_DIMENSION,
|
||||
(1024, _constants.MAX_DIMENSION),
|
||||
id='scale_above_max_height_dimension_clamps_retains_ar',
|
||||
),
|
||||
pytest.param(
|
||||
4096, _MAX_DIMENSION, _MAX_DIMENSION,
|
||||
(_MAX_DIMENSION, 1024),
|
||||
4096, _constants.MAX_DIMENSION, _constants.MAX_DIMENSION,
|
||||
(_constants.MAX_DIMENSION, 1024),
|
||||
id='scale_above_max_width_dimension_clamps_retains_ar',
|
||||
),
|
||||
pytest.param(
|
||||
64, 64, _MIN_DIMENSION - 1,
|
||||
(_MIN_DIMENSION, _MIN_DIMENSION),
|
||||
id='scale_dimension_below_min_dimension_clamps_retains_ar',
|
||||
64, 64, _constants.MIN_DIMENSION - 1,
|
||||
(_constants.MIN_DIMENSION, _constants.MIN_DIMENSION),
|
||||
id='scale_dimension_belowMIN_DIMENSION_clamps_retains_ar',
|
||||
),
|
||||
pytest.param(
|
||||
64, 64, _MAX_DIMENSION + 1,
|
||||
(_MAX_DIMENSION, _MAX_DIMENSION),
|
||||
id='scale_dimension_above_max_dimension_clamps_retains_ar',
|
||||
64, 64, _constants.MAX_DIMENSION + 1,
|
||||
(_constants.MAX_DIMENSION, _constants.MAX_DIMENSION),
|
||||
id='scale_dimension_aboveMAX_DIMENSION_clamps_retains_ar',
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_scale_dimensions_to_max_dimension(
|
||||
def test_scale_dimensions_to(
|
||||
width, height, max_dim, expected,
|
||||
):
|
||||
assert _scale_dimensions_to_max_dimension(
|
||||
assert _util.scale_dimensions_to(
|
||||
width, height, max_dim,
|
||||
) == expected
|
||||
|
||||
|
|
@ -169,36 +167,33 @@ class SharedOpts:
|
|||
|
||||
def test_safe_opt_util():
|
||||
shared_opts = SharedOpts(options={'key': 'value'})
|
||||
assert _safe_opt_util(shared_opts, 'key') == 'value'
|
||||
assert _util.safe_opt_util(shared_opts, 'key', {}) == 'value'
|
||||
|
||||
|
||||
def test_safe_opt_util_default_a(monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
'aspect_ratio_helper._util._OPT_KEY_TO_DEFAULT_MAP',
|
||||
{'key': 'default_b'},
|
||||
)
|
||||
def test_safe_opt_util_none():
|
||||
shared_opts = SharedOpts(options={'key': None}, defaults={'key': 'value'})
|
||||
assert _util.safe_opt_util(shared_opts, 'key', {}) == 'value'
|
||||
|
||||
|
||||
def testsafe_opt_util_default_a():
|
||||
shared_opts = SharedOpts(
|
||||
defaults={'key': 'default_a'},
|
||||
)
|
||||
assert _safe_opt_util(shared_opts, 'key') == 'default_a'
|
||||
assert _util.safe_opt_util(
|
||||
shared_opts, 'key', {'key': 'default_b'},
|
||||
) == 'default_a'
|
||||
|
||||
|
||||
def test_safe_opt_util_default_b(monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
'aspect_ratio_helper._util._OPT_KEY_TO_DEFAULT_MAP',
|
||||
{'key': 'default_b'},
|
||||
)
|
||||
def test_safe_opt_util_default_b():
|
||||
shared_opts = SharedOpts(defaults={'key': None})
|
||||
assert _safe_opt_util(shared_opts, 'key') == 'default_b'
|
||||
assert _util.safe_opt_util(
|
||||
shared_opts, 'key', {'key': 'default_b'},
|
||||
) == 'default_b'
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'options', ({'key': None}, {}),
|
||||
)
|
||||
def test_safe_opt_safe_return_no_defaults_b(monkeypatch, options):
|
||||
monkeypatch.setattr(
|
||||
'aspect_ratio_helper._util._OPT_KEY_TO_DEFAULT_MAP',
|
||||
{},
|
||||
)
|
||||
def test_safe_opt_safe_return_no_defaults_b(options):
|
||||
shared_opts = SharedOpts(options=options)
|
||||
assert _safe_opt_util(shared_opts, 'unknown_key') is None
|
||||
assert _util.safe_opt_util(shared_opts, 'unknown_key', {}) is None
|
||||
|
|
|
|||
Loading…
Reference in New Issue