diff --git a/__init__.py b/__init__.py index 1d2e425..d82a50d 100644 --- a/__init__.py +++ b/__init__.py @@ -1,5 +1,4 @@ import os - from .nodes.custom_save_image import CustomSaveImage from .nodes.color_adjustment import ColorAdjustment from .nodes.strong_prompt import StrongPrompt @@ -10,6 +9,10 @@ from .nodes.image_size_extractor import ImageSizeExtractor from .nodes.tencent_translater import TencentTranslater from .nodes.text import Text from .nodes.aio_translater import AIOTranslater +from .nodes.slider_1000 import Slider1000 +from .nodes.slider_100 import Slider100 +from .nodes.slider_10 import Slider10 +from .nodes.to_int import ToInt NODE_CLASS_MAPPINGS = { "Custom_Save_Image": CustomSaveImage, @@ -21,7 +24,11 @@ NODE_CLASS_MAPPINGS = { "Image_Size_Extractor": ImageSizeExtractor, "Tencent_Translater": TencentTranslater, "Text": Text, - "AIO_Translater": AIOTranslater, + "AIO_Translater": AIOTranslater, + "Slider_1000": Slider1000, + "Slider_100": Slider100, + "Slider_10": Slider10, + "To_Int": ToInt } NODE_DISPLAY_NAME_MAPPINGS = { @@ -34,7 +41,11 @@ NODE_DISPLAY_NAME_MAPPINGS = { "Image_Size_Extractor": "Image Size Extractor", "Tencent_Translater": "Tencent Translater", "Text": "Text", - "AIO_Translater": "AIO Translater", + "AIO_Translater": "AIO Translater", + "Slider_1000": "Slider 1000", + "Slider_100": "Slider 100", + "Slider_10": "Slider 10", + "To_Int": "To Int" } WEB_DIRECTORY = "web" \ No newline at end of file diff --git a/nodes/abc_math.py b/nodes/abc_math.py index 44e5697..3c24e12 100644 --- a/nodes/abc_math.py +++ b/nodes/abc_math.py @@ -2,7 +2,6 @@ import ast import operator as op import math -# 定义 AnyType 类 class AnyType(str): def __ne__(self, __value: object) -> bool: return False @@ -12,7 +11,7 @@ class AbcMath: def INPUT_TYPES(s): return { "optional": { - "a": (AnyType("*"), {"default": 0.0}), # 使用 AnyType 允许更多类型的输入 + "a": (AnyType("*"), {"default": 0.0}), "b": (AnyType("*"), {"default": 0.0}), "c": (AnyType("*"), {"default": 0.0}), }, @@ -23,17 +22,18 @@ class AbcMath: RETURN_TYPES = ("INT", "FLOAT",) FUNCTION = "execute" + OUTPUT_NODE = True CATEGORY = "KayTool" def execute(self, value, a=0.0, b=0.0, c=0.0): try: + if hasattr(a, 'shape'): a = list(a.shape) if hasattr(b, 'shape'): b = list(b.shape) if hasattr(c, 'shape'): c = list(c.shape) - if isinstance(a, str): a = float(a) if '.' in a or 'e' in a.lower() else int(a) if isinstance(b, str): @@ -41,6 +41,7 @@ class AbcMath: if isinstance(c, str): c = float(c) if '.' in c or 'e' in c.lower() else int(c) + operators = { ast.Add: op.add, ast.Sub: op.sub, @@ -60,7 +61,6 @@ class AbcMath: ast.Or: lambda x, y: x or y, ast.Not: op.not_ } - op_functions = { 'min': min, 'max': max, @@ -69,40 +69,41 @@ class AbcMath: 'len': len, } + def eval_(node): - if isinstance(node, ast.Num): # number (Python < 3.8) + if isinstance(node, ast.Num): return node.n - elif isinstance(node, ast.Constant): # number (Python >= 3.8) + elif isinstance(node, ast.Constant): return node.value - elif isinstance(node, ast.Name): # variable + elif isinstance(node, ast.Name): if node.id == "a": return a if node.id == "b": return b if node.id == "c": return c - elif isinstance(node, ast.BinOp): # + elif isinstance(node, ast.BinOp): return operators[type(node.op)](eval_(node.left), eval_(node.right)) - elif isinstance(node, ast.UnaryOp): # e.g., -1 + elif isinstance(node, ast.UnaryOp): return operators[type(node.op)](eval_(node.operand)) - elif isinstance(node, ast.Compare): # comparison operators + elif isinstance(node, ast.Compare): left = eval_(node.left) for op_, comparator in zip(node.ops, node.comparators): if not operators[type(op_)](left, eval_(comparator)): return 0 return 1 - elif isinstance(node, ast.BoolOp): # boolean operators (And, Or) + elif isinstance(node, ast.BoolOp): values = [eval_(value) for value in node.values] return operators[type(node.op)](*values) - elif isinstance(node, ast.Call): # custom function + elif isinstance(node, ast.Call): if isinstance(node.func, ast.Name) and node.func.id in op_functions: args = [eval_(arg) for arg in node.args] return op_functions[node.func.id](*args) - elif isinstance(node, ast.Subscript): # indexing or slicing + elif isinstance(node, ast.Subscript): value = eval_(node.value) - if isinstance(node.slice, ast.Index): # Python < 3.9 + if isinstance(node.slice, ast.Index): index = eval_(node.slice.value) - else: # Python >= 3.9 + else: index = eval_(node.slice) return value[index] if isinstance(index, int) and 0 <= index < len(value) else 0 else: @@ -111,19 +112,24 @@ class AbcMath: parsed_expression = ast.parse(value, mode='eval') result = eval_(parsed_expression.body) + if math.isnan(result): result = 0.0 - return (int(round(result)), float(result)) - except Exception: - return (0, 0.0) + + int_result = int(round(result)) + float_result = float(result) -# 导出必须的变量 -NODE_CLASS_MAPPINGS = { - "Abc_Math": AbcMath, # 确保与 __init__.py 中的键一致 -} + if int_result == float_result: + ui_text = f"INT: {int_result}" + else: + ui_text = f"INT: {int_result}\nFLOAT: {float_result}" + + + return {"ui": {"text": ui_text}, "result": (int_result, float_result)} + + except Exception as e: + print(f"Error during execution: {e}") + return {"ui": {"text": "Error"}, "result": (0, 0.0)} -NODE_DISPLAY_NAME_MAPPINGS = { - "Abc_Math": "Abc Math", # 确保与 __init__.py 中的显示名称一致 -} \ No newline at end of file diff --git a/nodes/baidu_translater.py b/nodes/baidu_translater.py index fcd9dc7..0cc3aeb 100644 --- a/nodes/baidu_translater.py +++ b/nodes/baidu_translater.py @@ -4,7 +4,6 @@ import requests import random from hashlib import md5 -# 设置资源目录 JSON_DIR = os.path.join(os.path.dirname(__file__), "../json") def make_md5(s, encoding='utf-8'): @@ -33,8 +32,8 @@ class BaiduTranslater: input_types = { "required": { - "Text_A": ("STRING", {"multiline": True, "placeholder": "Input Text A (输入文本 A)"}), - "Text_B": ("STRING", {"multiline": True, "placeholder": "Input Text B (输入文本 B)"}), + "Text_A": ("STRING", {"multiline": True, "placeholder": "Text A"}), + "Text_B": ("STRING", {"multiline": True, "placeholder": "Text B"}), "Translate": ("BOOLEAN", {"default": True, "label": "Enable Translation (启用翻译)", "tooltip": "Toggle this switch to enable or disable the translation function. (切换此开关以启用或禁用翻译功能。)"}), # 参数名称为 Translate,默认值为 true "From": ( [ diff --git a/nodes/color_adjustment.py b/nodes/color_adjustment.py index e492ac2..3a0d911 100644 --- a/nodes/color_adjustment.py +++ b/nodes/color_adjustment.py @@ -4,13 +4,12 @@ from PIL import Image, ImageEnhance import torch import subprocess -# 安装 pilgram 库(如果尚未安装) try: import pilgram except ImportError: subprocess.check_call(['pip', 'install', 'pilgram']) -# 将 tensor 转换为 PIL 图像 + def tensor2pil(image): return Image.fromarray(np.clip(255. * image.cpu().numpy().squeeze(), 0, 255).astype(np.uint8)) @@ -18,7 +17,7 @@ def tensor2pil(image): def pil2tensor(image): return torch.from_numpy(np.array(image).astype(np.float32) / 255.0).unsqueeze(0) -# 动态获取所有 pilgram 滤镜,并为每个滤镜编号,增加一个 "None" 选项 + def get_pilgram_filters(): filters = [f for f in dir(pilgram) if not f.startswith('_') and callable(getattr(pilgram, f))] numbered_filters = [f"{i+1}_{filters[i]}" for i in range(len(filters))] diff --git a/nodes/display_any.py b/nodes/display_any.py index 033df43..a936469 100644 --- a/nodes/display_any.py +++ b/nodes/display_any.py @@ -21,11 +21,7 @@ class DisplayAny: return True def execute(self, input): - text = str(input) - - return {"ui": {"text": text}, "result": (text,)} - __all__ = ['DisplayAny'] \ No newline at end of file diff --git a/nodes/slider_10.py b/nodes/slider_10.py new file mode 100644 index 0000000..0d6c5f3 --- /dev/null +++ b/nodes/slider_10.py @@ -0,0 +1,24 @@ +class Slider10: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "value": ("INT", { + "default": 5, + "min": 0, + "max": 10, + "step": 1, + "display": "slider" + }), + }, + } + + RETURN_TYPES = ("INT",) + FUNCTION = "get_value" + CATEGORY = "KayTool" + + def get_value(self, value): + return (value,) \ No newline at end of file diff --git a/nodes/slider_100.py b/nodes/slider_100.py new file mode 100644 index 0000000..312179e --- /dev/null +++ b/nodes/slider_100.py @@ -0,0 +1,24 @@ +class Slider100: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "value": ("INT", { + "default": 50, + "min": 0, + "max": 100, + "step": 1, + "display": "slider" + }), + }, + } + + RETURN_TYPES = ("INT",) + FUNCTION = "get_value" + CATEGORY = "KayTool" + + def get_value(self, value): + return (value,) \ No newline at end of file diff --git a/nodes/slider_1000.py b/nodes/slider_1000.py new file mode 100644 index 0000000..5f65985 --- /dev/null +++ b/nodes/slider_1000.py @@ -0,0 +1,24 @@ +class Slider1000: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "value": ("INT", { + "default": 500, + "min": 0, + "max": 1000, + "step": 1, + "display": "slider" + }), + }, + } + + RETURN_TYPES = ("INT",) + FUNCTION = "get_value" + CATEGORY = "KayTool" + + def get_value(self, value): + return (value,) \ No newline at end of file diff --git a/nodes/strong_prompt.py b/nodes/strong_prompt.py index a384d8a..5d37a32 100644 --- a/nodes/strong_prompt.py +++ b/nodes/strong_prompt.py @@ -31,8 +31,8 @@ class StrongPrompt: input_types = { "required": { - "positive": ("STRING", {"multiline": True, "placeholder": "positive (正向提示词)", "tooltip": "The positive prompt text. (正向提示词文本。)"}), # 用户输入的正向提示词 - "negative": ("STRING", {"multiline": True, "placeholder": "negative (负向提示词)", "tooltip": "The negative prompt text. (负向提示词文本。)"}), # 用户输入的负向提示词 + "positive": ("STRING", {"multiline": True, "placeholder": "positive", "tooltip": "The positive prompt text. (正向提示词文本。)"}), # 用户输入的正向提示词 + "negative": ("STRING", {"multiline": True, "placeholder": "negative", "tooltip": "The negative prompt text. (负向提示词文本。)"}), # 用户输入的负向提示词 "clip": ("CLIP", {"tooltip": "The CLIP model used for encoding the prompts. (用于编码提示词的CLIP模型。)"}) # 接收一个CLIP模型作为输入 }, "optional": {}, diff --git a/nodes/to_int.py b/nodes/to_int.py new file mode 100644 index 0000000..42a9d9c --- /dev/null +++ b/nodes/to_int.py @@ -0,0 +1,31 @@ +class ToInt: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "input": (("*", {})), + }, + } + + RETURN_TYPES = ("INT",) + FUNCTION = "execute" + OUTPUT_NODE = True + CATEGORY = "KayTool" + NAME = "To Int" + + @classmethod + def VALIDATE_INPUTS(cls, input_types): + return True + + def execute(self, input): + try: + rounded_value = int(round(float(input))) + except (ValueError, TypeError): + rounded_value = 0 + text = str(rounded_value) + return {"ui": {"text": text}, "result": (rounded_value,)} + +__all__ = ['ToInt'] \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 3ec0c0c..a643bba 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "kaytool" description ="This is a custom node toolkit that includes a variety of practical nodes, such as a custom prompt preset node, wireless transmission nodes, Tencent and Baidu AI translator nodes, mathematical operation nodes, image size extraction nodes, display-any nodes, color adjustment nodes, and custom image saving nodes, among others." -version = "0.11.6" +version = "0.15.10" license = {file = "LICENSE"} [project.urls] diff --git a/web/common_display.js b/web/common_display.js new file mode 100644 index 0000000..114cdf6 --- /dev/null +++ b/web/common_display.js @@ -0,0 +1,49 @@ + +import { app } from "../../scripts/app.js"; +import { ComfyWidgets } from "../../scripts/widgets.js"; + +export function setupGenericDisplay(nodeType, nodeName) { + const onExecuted = nodeType.prototype.onExecuted; + nodeType.prototype.onExecuted = function (message) { + + onExecuted?.apply(this, arguments); + + + if (this.widgets && this.widgets.length > 0) { + for (let i = 1; i < this.widgets.length; i++) { + this.widgets[i]?.onRemove?.(); + } + this.widgets.length = 1; + } + + let textWidget = Array.isArray(this.widgets) + ? this.widgets.find(w => w && w.name === "displaytext") + : null; + + if (!textWidget) { + try { + + textWidget = ComfyWidgets["STRING"](this, "displaytext", ["STRING", { multiline: true }], app).widget; + if (textWidget && textWidget.inputEl) { + textWidget.inputEl.readOnly = true; + textWidget.inputEl.style.border = "none"; + textWidget.inputEl.style.backgroundColor = "transparent"; + } + } catch (error) { + console.error(`[${nodeName}] Failed to create text widget:`, error); + } + } + + + if (message && message.hasOwnProperty("text")) { + const text = message["text"]; + if (textWidget) { + textWidget.value = Array.isArray(text) ? text.join("") : text; + } else { + console.warn(`[${nodeName}] Text widget not available to set value.`); + } + } else { + console.warn(`[${nodeName}] Message does not contain a "text" property.`); + } + }; +} \ No newline at end of file diff --git a/web/display_any_extension.js b/web/display_any_extension.js deleted file mode 100644 index da3deac..0000000 --- a/web/display_any_extension.js +++ /dev/null @@ -1,58 +0,0 @@ -import { app } from "../../scripts/app.js"; -import { ComfyWidgets } from "../../scripts/widgets.js"; - -app.registerExtension({ - name: "kaytool.Display_Any", - async beforeRegisterNodeDef(nodeType, nodeData, app) { - if (!nodeData?.category?.startsWith("KayTool")) { - return; - } - - if (nodeData.name === "Display_Any") { - const onExecuted = nodeType.prototype.onExecuted; - - nodeType.prototype.onExecuted = function (message) { - onExecuted?.apply(this, arguments); - - // 清除旧的小部件(除了第一个) - if (this.widgets && this.widgets.length > 0) { - for (let i = 1; i < this.widgets.length; i++) { - this.widgets[i]?.onRemove?.(); - } - this.widgets.length = 1; - } - - // 检查 "text" 小部件是否已经存在,并且确保widgets是数组类型 - let textWidget; - if (Array.isArray(this.widgets)) { - textWidget = this.widgets.find(w => w && w.name === "displaytext"); - } - - if (!textWidget) { - try { - textWidget = ComfyWidgets["STRING"](this, "displaytext", ["STRING", { multiline: true }], app).widget; - if (textWidget && textWidget.inputEl) { - textWidget.inputEl.readOnly = true; - textWidget.inputEl.style.border = "none"; - textWidget.inputEl.style.backgroundColor = "transparent"; - } - } catch (error) { - console.error('Failed to create text widget:', error); - } - } - - // 修改这行来处理文本 - if (message && message.hasOwnProperty("text")) { - const text = message["text"]; - if (textWidget) { - textWidget.value = Array.isArray(text) ? text.join("") : text; - } else { - console.warn('Text widget not available to set value.'); - } - } else { - console.warn('Message does not contain a "text" property.'); - } - }; - } - }, -}); \ No newline at end of file diff --git a/web/extensions.js b/web/extensions.js new file mode 100644 index 0000000..211b6c7 --- /dev/null +++ b/web/extensions.js @@ -0,0 +1,13 @@ +import { app } from "../../scripts/app.js"; +import { setupGenericDisplay } from "./common_display.js"; + +app.registerExtension({ + name: "KayTool.GenericDisplay", + async beforeRegisterNodeDef(nodeType, nodeData, app) { + + const nodesWithDisplay = ["Display_Any", "To_Int", "Abc_Math"]; + if (nodesWithDisplay.includes(nodeData.name)) { + setupGenericDisplay(nodeType, nodeData.name); + } + }, +}); \ No newline at end of file