v0.15.10
This commit is contained in:
+14
-3
@@ -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"
|
||||
+31
-25
@@ -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): # <left> <operator> <right>
|
||||
elif isinstance(node, ast.BinOp):
|
||||
return operators[type(node.op)](eval_(node.left), eval_(node.right))
|
||||
elif isinstance(node, ast.UnaryOp): # <operator> <operand> 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 中的显示名称一致
|
||||
}
|
||||
@@ -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": (
|
||||
[
|
||||
|
||||
@@ -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))]
|
||||
|
||||
@@ -21,11 +21,7 @@ class DisplayAny:
|
||||
return True
|
||||
|
||||
def execute(self, input):
|
||||
|
||||
text = str(input)
|
||||
|
||||
|
||||
return {"ui": {"text": text}, "result": (text,)}
|
||||
|
||||
|
||||
__all__ = ['DisplayAny']
|
||||
@@ -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,)
|
||||
@@ -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,)
|
||||
@@ -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,)
|
||||
@@ -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": {},
|
||||
|
||||
@@ -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']
|
||||
+1
-1
@@ -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]
|
||||
|
||||
@@ -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.`);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -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.');
|
||||
}
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -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);
|
||||
}
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user