Files
Cyan9977 79635ef417 新增风格节点
增加了一个三段式提示词工具,开头风格,中间主体,下方质量提示词,会合并成一个。
2025-11-07 09:47:55 +08:00

705 lines
29 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import json
import os
import hashlib
import time
import torch
import torch.nn.functional as F
"""
最简批量帧文本编码(仅正向),增强兼容性:
- 解决:不同 prompt 生成的 cond 长度不一致导致 torch.cat 报错 -> 统一 padding
- 解决:某些 clip.encode_from_tokens 在 return_pooled=True 时返回 (cond, None) 或根本不支持 pooled 的情况
- 若 pooled 全部为 None,则不放 pooled_output 或放占位空字符串
"""
# ComfyUI 节点注册字典(在文件末尾进行 update 之前,必须先定义)
NODE_CLASS_MAPPINGS = {}
NODE_DISPLAY_NAME_MAPPINGS = {}
# 参数标签翻译映射
PARAMETER_LABELS = {
"multiline_text": "多行文本",
"use_file": "使用文件",
"file_path": "文件路径",
"mode": "运行模式",
"label": "队列标识",
"preset": "预设模板",
"template": "模板",
"save_preset": "保存预设",
"delete_preset": "删除预设",
"artist_style_prompt": "画师风格提示词",
"prompt_suffix": "提示词后缀",
"preset_name_for_save": "保存预设名称"
}
def parse_frame_prompts(raw_text: str):
"""
解析输入为 {frame_index: prompt} 的升序字典。
允许缺少最外层 {};允许末尾多余逗号。
"""
txt = raw_text.strip()
if not txt:
return {}
if not txt.startswith("{"):
txt = "{" + txt
if not txt.endswith("}"):
txt = txt + "}"
txt = txt.replace(",}", "}")
data = json.loads(txt)
out = {}
for k, v in data.items():
try:
idx = int(k)
except:
continue
# 非数字 key 忽略
out[idx] = str(v)
return dict(sorted(out.items(), key=lambda x: x[0]))
def encode_prompts_sequential(clip, ordered_items):
"""
逐条编码;对 cond 做长度统一;pooled 可能为 None。
ordered_items: List[(frame_index, prompt)]
返回: cond_final, pooled_final(或 None), has_pooled(bool)
"""
cond_list = []
pooled_list = []
pooled_available = True # 先假设有 pooled
max_tokens = 0
with torch.no_grad():
for frame_idx, prompt in ordered_items:
tokens = clip.tokenize(prompt)
# 兼容:有的实现如果 return_pooled=True 仍返回单值
enc = clip.encode_from_tokens(tokens, return_pooled=True)
if isinstance(enc, (list, tuple)) and len(enc) == 2:
cond, pooled = enc
else:
cond, pooled = enc, None
if cond.dim() != 3:
# 期望 shape: [B(=1), T, C]; 若是 [T, C] 补一个 batch 维
if cond.dim() == 2:
cond = cond.unsqueeze(0)
else:
raise ValueError(f"意外的 cond 张量形状: {cond.shape}")
if cond.shape[1] > max_tokens:
max_tokens = cond.shape[1]
cond_list.append(cond)
if pooled is None:
pooled_available = False
pooled_list.append(None)
else:
# 期望 [B(=1), D] 或 [D]; 若 [D] -> [1,D]
if pooled.dim() == 1:
pooled = pooled.unsqueeze(0)
pooled_list.append(pooled)
if not cond_list:
dummy = torch.zeros(1, 1)
# 创建正确维度的 dummy pooled tensor (SDXL 需要 2816 维)
dummy_pooled = torch.zeros(1, 2816)
return dummy, dummy_pooled, False
# 对 cond 做 padding
padded = []
for c in cond_list:
if c.shape[1] < max_tokens:
pad_len = max_tokens - c.shape[1]
# shape [1, T, C],pad 第二维 (tokens):(last_dim_left, last_dim_right, second_dim_left, second_dim_right)
c = F.pad(c, (0, 0, 0, pad_len))
padded.append(c)
cond_final = torch.cat(padded, dim=0) # [N, max_tokens, C]
pooled_final = None
if pooled_available:
# 校验 pooled 维度并 cat
processed = []
for p in pooled_list:
if p is None:
pooled_available = False
break
if p.dim() == 1:
p = p.unsqueeze(0)
processed.append(p)
if pooled_available and processed:
try:
pooled_final = torch.cat(processed, dim=0) # [N, D]
except Exception:
# 如果维度不一致,放弃 pooled
pooled_final = None
pooled_available = False
# 如果 pooled 不可用,创建 dummy pooled tensor
if pooled_final is None:
# 创建正确维度的 dummy pooled tensor (SDXL 需要 2816 维)
pooled_final = torch.zeros(cond_final.shape[0], 2816) # [N, 2816]
return cond_final, pooled_final, pooled_available
## 删除了 BatchPrompt 节点,保留 PromptQueue
def _read_lines_from_source(multiline_text: str, file_path: str = "", use_file: bool = False):
"""
从多行文本或文件读取,每行一个提示词;自动去除空行和首尾空白。
"""
lines = []
if use_file and file_path:
try:
with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
lines = f.readlines()
except Exception:
# 回退到多行文本
lines = multiline_text.splitlines()
else:
lines = multiline_text.splitlines()
cleaned = []
for raw in lines:
s = str(raw).strip()
if not s:
continue
cleaned.append(s)
return cleaned
def _apply_template_to_lines(lines, template: str):
"""
将每行替换进模板。若模板为空或不含 {p},则直接返回行本身。
"""
if not template or "{p}" not in template:
return lines
return [template.replace("{p}", line) for line in lines]
class SimplePromptQueue:
"""
简化版提示词队列节点,支持从文件读取和预设模板功能。
将多行提示词视为队列,从 txt 文件读取(逐行)。
输出按批次堆叠的 CONDITIONING,便于一次性批量生成。
"""
@classmethod
def INPUT_TYPES(cls):
# 动态获取预设列表(正面)
presets = _pq_load_positive_template_presets()
preset_list = ["None"] + list(presets.keys())
return {
"required": {
"clip": ("CLIP",),
"文件路径": ("STRING", {"multiline": False, "default": ""}),
"运行模式": (["all", "incremental"], {"default": "all"}),
"队列标识": ("STRING", {"multiline": False, "default": "Simple Queue 001"}),
"预设模板": (preset_list, {"default": "None"}),
}
}
RETURN_TYPES = ("CONDITIONING",)
RETURN_NAMES = ("positive",)
FUNCTION = "run"
CATEGORY = "PromptQueue"
def run(self, clip, 文件路径, 运行模式="all", 队列标识="Simple Queue 001", 预设模板="None"):
# 选择预设(若为 None 则不应用模板)
模板 = ""
if 预设模板 != "None":
presets = _pq_load_positive_template_presets()
if 预设模板 in presets:
模板 = presets[预设模板]
print(f"[Simple Prompt Queue] 使用预设 '{预设模板}': {模板}")
else:
print(f"[Simple Prompt Queue] 预设 '{预设模板}' 不存在,忽略模板")
if not 文件路径.strip():
dummy = torch.zeros(1, 1)
# 创建正确维度的 dummy pooled tensor (SDXL 需要 2816 维)
dummy_pooled = torch.zeros(1, 2816)
return ([[dummy, {"pooled_output": dummy_pooled}]],)
# 从文件读取提示词
lines = _read_lines_from_source("", 文件路径, True)
lines = _apply_template_to_lines(lines, 模板)
if not lines:
dummy = torch.zeros(1, 1)
# 创建正确维度的 dummy pooled tensor (SDXL 需要 2816 维)
dummy_pooled = torch.zeros(1, 2816)
return ([[dummy, {"pooled_output": dummy_pooled}]],)
# 模式:incremental 每次调用消费一行,使用每个标签的持久计数器
if 运行模式 == "incremental":
idx = _pq_get_next_index(队列标识, _pq_total=len(lines), source_descriptor=_pq_source_descriptor(True, 文件路径, ""))
line = lines[idx]
ordered = [(0, line)]
cond, pooled, has_pooled = encode_prompts_sequential(clip, ordered)
meta = {"pooled_output": pooled}
return ([[cond, meta]],)
# 默认:所有行一起批处理
ordered = list(enumerate(lines))
cond, pooled, has_pooled = encode_prompts_sequential(clip, ordered)
meta = {"pooled_output": pooled}
return ([[cond, meta]],)
@classmethod
def IS_CHANGED(cls, clip, 文件路径, 运行模式="all", 队列标识="Simple Queue 001", 预设模板="None"):
# 确保增量模式每次重新执行以推进队列
if 运行模式 == "incremental":
return str(time.time())
# 当预设更改时强制重新执行以实时更新模板
if 预设模板 != "None":
return str(time.time())
# 对于确定性模式,对输入进行哈希以便缓存按预期工作
try:
desc = _pq_source_descriptor(True, str(文件路径), "")
key = f"{desc}|{运行模式}|{队列标识}|{预设模板}"
return hashlib.sha1(key.encode("utf-8", errors="ignore")).hexdigest()
except Exception:
return ""
class NegativePromptQueue:
"""
负面提示词队列节点,专门用于处理负面提示词。
支持多行文本输入和预设模板功能,模板配置方式与PromptQueue一致。
"""
@classmethod
def INPUT_TYPES(cls):
# 动态获取预设列表(负面)
presets = _pq_load_negative_template_presets()
preset_list = ["None"] + list(presets.keys())
return {
"required": {
"clip": ("CLIP",),
"多行文本": ("STRING", {"multiline": True, "default": "blurry, low quality, distorted"}),
"预设模板": (preset_list, {"default": "None"}),
"模板": ("STRING", {"multiline": False, "default": "{p}"}),
"保存预设": ("BOOLEAN", {"default": False}),
"删除预设": ("BOOLEAN", {"default": False}),
}
}
RETURN_TYPES = ("CONDITIONING",)
RETURN_NAMES = ("negative",)
FUNCTION = "run"
CATEGORY = "PromptQueue"
def run(self, clip, 多行文本, 预设模板="None", 模板="{p}", 保存预设=False, 删除预设=False):
# 处理删除预设 - 拥有最高优先级
if 删除预设 and 预设模板 != "None":
_pq_delete_negative_template_preset(预设模板)
print(f"[Negative Prompt Queue] 已删除预设 '{预设模板}'")
print(f"[Negative Prompt Queue] 使用 template 内容(删除预设后): {模板}")
# 处理保存预设 - 第二优先级,强制使用 template 内容
elif 保存预设 and 模板.strip():
_pq_save_negative_template_preset(模板.strip(), 模板.strip())
print(f"[Negative Prompt Queue] 已保存预设 '{模板.strip()}': {模板.strip()}")
print(f"[Negative Prompt Queue] 使用 template 内容(save_preset 优先级): {模板}")
# 处理预设选择 - 当选择非"None"预设且未开启保存/删除时,使用预设内容
elif 预设模板 != "None":
presets = _pq_load_negative_template_presets()
if 预设模板 in presets:
模板 = presets[预设模板]
print(f"[Negative Prompt Queue] 使用预设 '{预设模板}': {模板}")
else:
print(f"[Negative Prompt Queue] 预设 '{预设模板}' 不存在,使用 template 内容")
else:
print(f"[Negative Prompt Queue] 使用 template 内容: {模板}")
lines = _read_lines_from_source(多行文本, "", False)
lines = _apply_template_to_lines(lines, 模板)
if not lines:
# 使用标准的 CLIP 编码方式处理空输入
tokens = clip.tokenize("")
cond, pooled = clip.encode_from_tokens(tokens, return_pooled=True)
if pooled is None:
pooled = torch.zeros(1, 2816) # SDXL 需要 2816 维
return ([[cond, {"pooled_output": pooled}]],)
# 使用标准的 CLIP 编码方式处理每一行
cond_list = []
pooled_list = []
with torch.no_grad():
for line in lines:
tokens = clip.tokenize(line)
cond, pooled = clip.encode_from_tokens(tokens, return_pooled=True)
# 确保 cond 是 3D 张量 [B, T, C]
if cond.dim() == 2:
cond = cond.unsqueeze(0)
# 确保 pooled 是 2D 张量 [B, D]
if pooled is None:
pooled = torch.zeros(1, 2816) # SDXL 需要 2816 维
elif pooled.dim() == 1:
pooled = pooled.unsqueeze(0)
cond_list.append(cond)
pooled_list.append(pooled)
# 将所有条件连接起来
if cond_list:
# 对 cond 做 padding 以确保长度一致
max_tokens = max(c.shape[1] for c in cond_list)
padded_conds = []
for c in cond_list:
if c.shape[1] < max_tokens:
pad_len = max_tokens - c.shape[1]
c = F.pad(c, (0, 0, 0, pad_len))
padded_conds.append(c)
cond_final = torch.cat(padded_conds, dim=0)
pooled_final = torch.cat(pooled_list, dim=0)
else:
# 空情况
cond_final = torch.zeros(1, 1)
pooled_final = torch.zeros(1, 2816)
return ([[cond_final, {"pooled_output": pooled_final}]],)
@classmethod
def IS_CHANGED(cls, clip, 多行文本, 预设模板="None", 模板="{p}", 保存预设=False, 删除预设=False):
# 当预设更改时强制重新执行以实时更新模板
if 预设模板 != "None":
return str(time.time())
# 对于确定性模式,对输入进行哈希以便缓存按预期工作
try:
desc = _pq_source_descriptor(False, "", str(多行文本))
key = f"{desc}|{模板}|{预设模板}"
return hashlib.sha1(key.encode("utf-8", errors="ignore")).hexdigest()
except Exception:
return ""
class PromptQueue:
"""
将多行提示词视为队列,或从 txt 文件读取(逐行)。
输出按批次堆叠的 CONDITIONING,便于一次性批量生成。
可选模板,将每行填充到模板中的 {prompt} 占位符。
"""
@classmethod
def INPUT_TYPES(cls):
# 动态获取预设列表(正面)
presets = _pq_load_positive_template_presets()
preset_list = ["None"] + list(presets.keys())
return {
"required": {
"clip": ("CLIP",),
"多行文本": ("STRING", {"multiline": True, "default": "A cat\nA dog\nA bird"}),
"使用文件": ("BOOLEAN", {"default": False}),
"文件路径": ("STRING", {"multiline": False, "default": ""}),
"运行模式": (["all", "incremental"], {"default": "all"}),
"队列标识": ("STRING", {"multiline": False, "default": "Prompt Queue 001"}),
"预设模板": (preset_list, {"default": "None"}),
"模板": ("STRING", {"multiline": False, "default": "{p}"}),
"保存预设": ("BOOLEAN", {"default": False}),
"删除预设": ("BOOLEAN", {"default": False}),
}
}
RETURN_TYPES = ("CONDITIONING",)
RETURN_NAMES = ("positive",)
FUNCTION = "run"
CATEGORY = "PromptQueue"
def run(self, clip, 多行文本, 使用文件, 文件路径, 模板, 运行模式="all", 队列标识="Prompt Queue 001", 预设模板="None", 保存预设=False, 删除预设=False):
# 处理删除预设 - 拥有最高优先级
if 删除预设 and 预设模板 != "None":
_pq_delete_positive_template_preset(预设模板)
print(f"[Prompt Queue] 已删除预设 '{预设模板}'")
print(f"[Prompt Queue] 使用 template 内容(删除预设后): {模板}")
# 处理保存预设 - 第二优先级,强制使用 template 内容
elif 保存预设 and 模板.strip():
_pq_save_positive_template_preset(模板.strip(), 模板.strip())
print(f"[Prompt Queue] 已保存预设 '{模板.strip()}': {模板.strip()}")
print(f"[Prompt Queue] 使用 template 内容(save_preset 优先级): {模板}")
# 处理预设选择 - 当选择非"None"预设且未开启保存/删除时,使用预设内容
elif 预设模板 != "None":
presets = _pq_load_positive_template_presets()
if 预设模板 in presets:
模板 = presets[预设模板]
print(f"[Prompt Queue] 使用预设 '{预设模板}': {模板}")
else:
print(f"[Prompt Queue] 预设 '{预设模板}' 不存在,使用 template 内容")
else:
print(f"[Prompt Queue] 使用 template 内容: {模板}")
lines = _read_lines_from_source(多行文本, 文件路径, 使用文件)
lines = _apply_template_to_lines(lines, 模板)
if not lines:
dummy = torch.zeros(1, 1)
# 创建正确维度的 dummy pooled tensor (SDXL 需要 2816 维)
dummy_pooled = torch.zeros(1, 2816)
return ([[dummy, {"pooled_output": dummy_pooled}]],)
# 模式:incremental 每次调用消费一行,使用每个标签的持久计数器
if 运行模式 == "incremental":
idx = _pq_get_next_index(队列标识, _pq_total=len(lines), source_descriptor=_pq_source_descriptor(使用文件, 文件路径, 多行文本))
line = lines[idx]
ordered = [(0, line)]
cond, pooled, has_pooled = encode_prompts_sequential(clip, ordered)
meta = {"pooled_output": pooled}
return ([[cond, meta]],)
# 默认:所有行一起批处理
ordered = list(enumerate(lines))
cond, pooled, has_pooled = encode_prompts_sequential(clip, ordered)
meta = {"pooled_output": pooled}
return ([[cond, meta]],)
@classmethod
def IS_CHANGED(cls, clip, 多行文本, 使用文件, 文件路径, 模板, 运行模式="all", 队列标识="Prompt Queue 001", 预设模板="None", 保存预设=False, 删除预设=False):
# 确保增量模式每次重新执行以推进队列
if 运行模式 == "incremental":
return str(time.time())
# 当预设更改时强制重新执行以实时更新模板
if 预设模板 != "None":
return str(time.time())
# 对于确定性模式,对输入进行哈希以便缓存按预期工作
try:
desc = _pq_source_descriptor(bool(使用文件), str(文件路径), str(多行文本))
key = f"{desc}|{模板}|{预设模板}"
return hashlib.sha1(key.encode("utf-8", errors="ignore")).hexdigest()
except Exception:
return ""
class StylePromptQueue:
"""
一个风格化的提示词队列节点,为画师/风格前缀和后缀提供独立的字段,
并为它们提供预设系统。仅在增量模式下运行。
"""
@classmethod
def INPUT_TYPES(cls):
# 动态获取预设列表(正面)
presets = _pq_load_positive_template_presets()
preset_list = ["None"] + list(presets.keys())
return {
"required": {
"clip": ("CLIP",),
"画师风格提示词": ("STRING", {"multiline": False, "default": ""}),
"多行文本": ("STRING", {"multiline": True, "default": "A cat\nA dog"}),
"使用文件": ("BOOLEAN", {"default": False}),
"文件路径": ("STRING", {"multiline": False, "default": ""}),
"提示词后缀": ("STRING", {"multiline": False, "default": ""}),
"队列标识": ("STRING", {"multiline": False, "default": "Style Queue 001"}),
"预设模板": (preset_list, {"default": "None"}),
"保存预设名称": ("STRING", {"multiline": False, "default": ""}),
"保存预设": ("BOOLEAN", {"default": False}),
"删除预设": ("BOOLEAN", {"default": False}),
}
}
RETURN_TYPES = ("CONDITIONING",)
RETURN_NAMES = ("positive",)
FUNCTION = "run"
CATEGORY = "PromptQueue"
def run(self, clip, 画师风格提示词, 多行文本, 使用文件, 文件路径, 提示词后缀, 队列标识, 预设模板, 保存预设名称, 保存预设, 删除预设):
prefix = 画师风格提示词
suffix = 提示词后缀
# 处理预设
if 删除预设 and 预设模板 != "None":
_pq_delete_positive_template_preset(预设模板)
print(f"[Style Prompt Queue] 已删除预设 '{预设模板}'")
elif 保存预设 and 保存预设名称.strip():
preset_name = 保存预设名称.strip()
parts = []
p = prefix.strip()
s = suffix.strip()
if p:
parts.append(p)
parts.append("{p}")
if s:
parts.append(s)
preset_data = ",".join(parts)
_pq_save_positive_template_preset(preset_name, preset_data)
print(f"[Style Prompt Queue] 已保存预设 '{preset_name}': {preset_data}")
elif 预设模板 != "None":
presets = _pq_load_positive_template_presets()
if 预设模板 in presets:
preset_content = presets[预设模板]
is_legacy_json = False
try:
data = json.loads(preset_content)
if isinstance(data, dict) and ("prefix" in data or "suffix" in data):
prefix = data.get("prefix", "")
suffix = data.get("suffix", "")
print(f"[Style Prompt Queue] 已从旧版JSON格式加载预设 '{预设模板}'")
is_legacy_json = True
except (json.JSONDecodeError, TypeError):
pass
if not is_legacy_json:
if "{p}" in preset_content:
parts = preset_content.split("{p}", 1)
prefix = parts[0].rstrip(',').strip()
if len(parts) > 1:
suffix = parts[1].lstrip(',').strip()
else:
suffix = ""
print(f"[Style Prompt Queue] 已加载预设 '{预设模板}'")
else:
prefix = preset_content
suffix = ""
print(f"[Style Prompt Queue] 无法解析预设 '{预设模板}',已作为前缀处理")
lines = _read_lines_from_source(多行文本, 文件路径, 使用文件)
# 应用前缀和后缀
final_lines = []
for line in lines:
parts = [p for p in [prefix, line, suffix] if p and p.strip()]
final_lines.append(", ".join(parts))
if not final_lines:
# 返回空的 conditioning
dummy = torch.zeros(1, 1)
dummy_pooled = torch.zeros(1, 2816)
return ([[dummy, {"pooled_output": dummy_pooled}]],)
# 始终为增量模式
idx = _pq_get_next_index(队列标识, _pq_total=len(final_lines), source_descriptor=_pq_source_descriptor(使用文件, 文件路径, 多行文本))
line_to_process = final_lines[idx]
ordered = [(0, line_to_process)]
cond, pooled, has_pooled = encode_prompts_sequential(clip, ordered)
meta = {"pooled_output": pooled}
return ([[cond, meta]],)
@classmethod
def IS_CHANGED(cls, *args, **kwargs):
# 始终返回时间戳以强制重新执行
return str(time.time())
# --- 持久队列状态(每个标签)---
class _JsonKVStore:
def __init__(self, file_path: str):
self.file_path = file_path
self.data = {}
self._load()
def _load(self):
try:
if os.path.exists(self.file_path):
with open(self.file_path, "r", encoding="utf-8") as f:
self.data = json.load(f)
else:
self.data = {}
except Exception:
self.data = {}
def _save(self):
try:
os.makedirs(os.path.dirname(self.file_path), exist_ok=True)
with open(self.file_path, "w", encoding="utf-8") as f:
json.dump(self.data, f, indent=2)
except Exception:
pass
def get(self, category: str, key: str, default=None):
return self.data.get(category, {}).get(key, default)
def put(self, category: str, key: str, value) -> None:
if category not in self.data:
self.data[category] = {}
self.data[category][key] = value
self._save()
_pq_state_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "_prompt_queue_state")
_pq_store = _JsonKVStore(os.path.join(_pq_state_dir, "state.json"))
def _pq_source_descriptor(use_file: bool, file_path: str, multiline_text: str) -> str:
if use_file and file_path:
try:
return "FILE:" + os.path.abspath(file_path)
except Exception:
return "FILE:" + file_path
# 文本哈希作为描述符;内容更改不会重置索引以允许在追加时继续
h = hashlib.sha1(multiline_text.encode("utf-8", errors="ignore")).hexdigest()
return "TEXT:" + h
def _pq_get_next_index(label: str, _pq_total: int, source_descriptor: str) -> int:
if _pq_total <= 0:
return 0
# 仅在源类型在 FILE 和 TEXT 之间切换或文件路径更改时重置
stored_src = _pq_store.get("PromptQueue Sources", label, None)
if stored_src != source_descriptor:
_pq_store.put("PromptQueue Sources", label, source_descriptor)
_pq_store.put("PromptQueue Counters", label, 0)
current = _pq_store.get("PromptQueue Counters", label, 0) or 0
next_index = current % _pq_total
_pq_store.put("PromptQueue Counters", label, (current + 1) % _pq_total)
return next_index
def _pq_save_positive_template_preset(name: str, template: str) -> None:
"""保存正面模板预设"""
_pq_store.put("Template Presets Positive", name, template)
def _pq_load_positive_template_presets() -> dict:
"""加载所有正面模板预设"""
return _pq_store.data.get("Template Presets Positive", {})
def _pq_delete_positive_template_preset(name: str) -> None:
"""删除正面模板预设"""
if "Template Presets Positive" in _pq_store.data and name in _pq_store.data["Template Presets Positive"]:
del _pq_store.data["Template Presets Positive"][name]
_pq_store._save()
def _pq_save_negative_template_preset(name: str, template: str) -> None:
"""保存负面模板预设"""
_pq_store.put("Template Presets Negative", name, template)
def _pq_load_negative_template_presets() -> dict:
"""加载所有负面模板预设"""
return _pq_store.data.get("Template Presets Negative", {})
def _pq_delete_negative_template_preset(name: str) -> None:
"""删除负面模板预设"""
if "Template Presets Negative" in _pq_store.data and name in _pq_store.data["Template Presets Negative"]:
del _pq_store.data["Template Presets Negative"][name]
_pq_store._save()
# 注册新节点
NODE_CLASS_MAPPINGS.update({
"PromptQueue": PromptQueue,
"SimplePromptQueue": SimplePromptQueue,
"NegativePromptQueue": NegativePromptQueue,
"StylePromptQueue": StylePromptQueue,
})
NODE_DISPLAY_NAME_MAPPINGS.update({
"PromptQueue": "Prompt Queue / 提示词队列",
"SimplePromptQueue": "Simple Prompt Queue / 简化提示词队列",
"NegativePromptQueue": "Negative Prompt Queue / 负面提示词队列",
"StylePromptQueue": "Style Prompt Queue / 风格化提示词队列",
})