This commit is contained in:
kk8bit
2025-02-26 20:17:10 +08:00
parent b4f81f79ac
commit b3c13d7bcd
14 changed files with 217 additions and 99 deletions
+14 -3
View File
@@ -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
View File
@@ -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 中的显示名称一致
}
+2 -3
View File
@@ -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": (
[
+2 -3
View File
@@ -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))]
-4
View File
@@ -21,11 +21,7 @@ class DisplayAny:
return True
def execute(self, input):
text = str(input)
return {"ui": {"text": text}, "result": (text,)}
__all__ = ['DisplayAny']
+24
View File
@@ -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,)
+24
View File
@@ -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,)
+24
View File
@@ -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,)
+2 -2
View File
@@ -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": {},
+31
View File
@@ -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
View File
@@ -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]
+49
View File
@@ -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.`);
}
};
}
-58
View File
@@ -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.');
}
};
}
},
});
+13
View File
@@ -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);
}
},
});