+ add support for interactive report message (at the bottom of the node)

This commit is contained in:
Lex Darlog (DRL)
2025-07-05 18:17:22 +05:00
parent abef3f2cc0
commit a6400e9d2b
3 changed files with 55 additions and 32 deletions
+23 -7
View File
@@ -1,9 +1,15 @@
# encoding: utf-8
"""
Internal utility functions. They're intended to be used only by the methods within nodes,
so they might expect the input arguments to already be pre-validated.
"""
import typing as _t
from math import sqrt as _sqrt
from server import PromptServer as _PromptServer
_t_number = _t.Union[int, float]
@@ -62,25 +68,35 @@ def round_width_and_height_closest_to_the_ratio(width_f: _t_number, height_f: _t
return width, n_steps_x, height, n_steps_y
def simple_result_from_approx_wh(width_f: float, height_f: _t_number, step: int):
def simple_result_from_approx_wh(width_f: float, height_f: _t_number, step: int, unique_id: str = None):
"""Final part of the main func for simple (non-upscale) nodes - when desired float/height are already calculated"""
step = number_to_int(step)
width, n_steps_x, height, n_steps_y = round_width_and_height_closest_to_the_ratio(width_f, height_f, step)
square_side_f = _sqrt(float(width * height))
square_side = int(square_side_f + 0.5)
square_side_text = (
f"={square_side}x{square_side}"
if (square_side * square_side == width * height)
else f"~{square_side}x{square_side}"
)
ar_desired = width_f / height_f
ar_real = float(width) / height
ar_text = (
"AR: perfect match"
if abs(ar_desired - ar_real) < 0.0005
else f"AR desired/real: {ar_desired:.3f}/{ar_real:.3f}"
else f"AR goal/real: {ar_desired:.3f}/{ar_real:.3f}"
)
text = (
f"{width} x {height}\n"
f"{width} x {height} ({square_side_text})\n"
f"{n_steps_x} * {step} x {n_steps_y} * {step}\n"
f"{ar_text}"
)
return {
# "ui": {"text": [text]}, # TODO: for some reason, it isn't displayed
"result": (width, height, text)
}
if unique_id:
# Snatched from: https://github.com/comfyanonymous/ComfyUI/blob/27870ec3c30e56be9707d89a120eb7f0e2836be1/comfy_extras/nodes_images.py#L581-L582
_PromptServer.instance.send_progress_text(text, unique_id)
return width, height
+1
View File
@@ -1,5 +1,6 @@
# encoding: utf-8
"""
Pre-defined dictionaries for nice input types.
"""
import typing as _t
+31 -25
View File
@@ -1,5 +1,6 @@
# encoding: utf-8
"""
The actual nodes defined by the package.
"""
import typing as _t
@@ -20,31 +21,32 @@ from ._funcs import (
)
_48_tooltip = (
"The default 48 is (8 * 3 * 2), so it's a safe choice because:\n"
"- it's compatible with SD1.5/XL step (divisible by 8),\n"
"- it can be upscaled by x1.5 or x1.333 at the first iteration, which is optimal for latent-upscale,\n"
"- after x1.5 upscale, if you only do x2 later (it's OK for already high resolutions) - it will be divisible "
"by 3 AND 9, which might become handy at that point, where you'll probably use UltimateSDUpscale.\n\n"
"Other values worth trying first: 64, 96, 128."
)
# Tiny optimization by reusing the same immutable dict:
_input_types_simple = deepfreeze({
"required": {
"width": (_IO.INT, dict(type_dict_res, **{"tooltip": "Approximate width"})),
"height": (_IO.INT, dict(type_dict_res, **{"tooltip": "Approximate height"})),
"step": (_IO.INT, dict(type_dict_step_init, **{"tooltip": (
f"Both width and height will be divisible by this value - by rounding them "
f"to the closest appropriate resolution.\n\n{_48_tooltip}"
"Both width and height will be divisible by this value - by rounding them "
"to the closest appropriate resolution.\n\n"
"The default 48 is (8 * 3 * 2), so it's a safe choice because:\n"
"- it's compatible with SD1.5/XL downsampling factor (divisible by 8),\n"
"- it can be upscaled by x1.5 or x1.333 at the first iteration, which is optimal for latent-upscale,\n"
"- after x1.5 upscale, if you only do x2 later (it's OK for already high resolutions) - it will be divisible "
"by 3 AND 9, which might become handy at that point, where you'll probably use UltimateSDUpscale.\n\n"
"Other values worth trying first: 64, 96, 128."
)})),
},
# "hidden": {},
"hidden": {
"unique_id": "UNIQUE_ID", # used for text display at the bottom of the node
},
# "optional": {},
})
_return_types_simple = (_IO.INT, _IO.INT, _IO.STRING)
_return_names_simple = ("width", "height", "report")
# _return_types_simple = (_IO.INT, _IO.INT, _IO.STRING)
# _return_names_simple = ("width", "height", "report")
_return_types_simple = (_IO.INT, _IO.INT)
_return_names_simple = ("width", "height")
class BestResolutionSimple:
@@ -56,7 +58,7 @@ class BestResolutionSimple:
CATEGORY = "utils/resolution"
DESCRIPTION = _cleandoc(__doc__)
# OUTPUT_NODE = True # TODO
OUTPUT_NODE = True
FUNCTION = "main"
RETURN_TYPES = _return_types_simple
@@ -67,8 +69,8 @@ class BestResolutionSimple:
def INPUT_TYPES(cls):
return _input_types_simple
def main(self, width: int, height: int, step: int):
return _simple_result_from_approx_wh(float(width), height, step)
def main(self, width: int, height: int, step: int, unique_id: str = None):
return _simple_result_from_approx_wh(float(width), height, step, unique_id)
_tooltip_aspect = (
@@ -104,7 +106,9 @@ _input_types_orient = deepfreeze({
"tooltip": _tooltip_aspect
}),
},
# "hidden": {},
"hidden": {
"unique_id": "UNIQUE_ID",
},
# "optional": {},
})
@@ -118,7 +122,7 @@ class BestResolutionFromAspectRatio:
CATEGORY = "utils/resolution"
DESCRIPTION = _cleandoc(__doc__)
# OUTPUT_NODE = True # TODO
OUTPUT_NODE = True
FUNCTION = "main"
RETURN_TYPES = _return_types_simple
@@ -128,7 +132,7 @@ class BestResolutionFromAspectRatio:
def INPUT_TYPES(cls):
return _input_types_orient
def main(self, size: int, step: int, size_is_big: bool, landscape: bool, aspect_a: float, aspect_b: float):
def main(self, size: int, step: int, size_is_big: bool, landscape: bool, aspect_a: float, aspect_b: float, unique_id: str = None):
aspect_big, aspect_small = _aspect_ratios_sorted(aspect_a, aspect_b)
side_main_f = float(size)
@@ -141,7 +145,7 @@ class BestResolutionFromAspectRatio:
# The opposite: height is bigger
width_f, height_f = height_f, width_f
return _simple_result_from_approx_wh(width_f, height_f, step)
return _simple_result_from_approx_wh(width_f, height_f, step, unique_id)
_input_types_area = deepfreeze({
@@ -158,7 +162,9 @@ _input_types_area = deepfreeze({
"aspect_a": _input_types_orient["required"]["aspect_a"],
"aspect_b": _input_types_orient["required"]["aspect_b"],
},
# "hidden": {},
"hidden": {
"unique_id": "UNIQUE_ID",
},
# "optional": {},
})
@@ -181,7 +187,7 @@ class BestResolutionFromArea:
CATEGORY = "utils/resolution"
DESCRIPTION = _cleandoc(__doc__)
# OUTPUT_NODE = True # TODO
OUTPUT_NODE = True
FUNCTION = "main"
RETURN_TYPES = _return_types_simple
@@ -191,7 +197,7 @@ class BestResolutionFromArea:
def INPUT_TYPES(cls):
return _input_types_area
def main(self, square_size: int, step: int, landscape: bool, aspect_a: float, aspect_b: float):
def main(self, square_size: int, step: int, landscape: bool, aspect_a: float, aspect_b: float, unique_id: str = None):
# square_size = 1024; step = 48; landscape = True; aspect_a = 9.0; aspect_b = 16.0
aspect_big, aspect_small = _aspect_ratios_sorted(aspect_a, aspect_b)
aspect_x, aspect_y = (aspect_big, aspect_small) if landscape else (aspect_small, aspect_big)
@@ -206,4 +212,4 @@ class BestResolutionFromArea:
width_f = aspect_x * square_size
height_f = aspect_y * square_size
return _simple_result_from_approx_wh(width_f, height_f, step)
return _simple_result_from_approx_wh(width_f, height_f, step, unique_id)