5 Commits
Author SHA1 Message Date
Cyan9977 0b86452a6e Add files via upload 2025-12-23 19:39:38 +08:00
Cyan9977 fbbe3d2e60 Add files via upload 2025-12-10 09:45:27 +08:00
Cyan9977 28cb7cfec3 Add files via upload 2025-12-09 16:40:09 +08:00
Cyan9977 09c4f12d94 Add files via upload 2025-12-09 16:16:05 +08:00
Cyan9977 9780a9d720 Add files via upload 2025-12-09 15:22:13 +08:00
2 changed files with 171 additions and 42 deletions
+9 -3
View File
@@ -33,14 +33,19 @@
* **核心逻辑**: 每次运行只输出**一行**提示词。
* **容错机制**: 如果您的 KSampler 设置 `Batch Size > 1` (例如 4),该节点会输出 4 份**完全相同**的 conditioning。这意味着您将得到 4 张基于同一提示词的图片,方便优选,提高出图容错率。
* **参数详解**:
* **全局前缀**: 一个全局通用的提示词前缀(例如通用画质词、常用 LoRA 触发词等),**始终拼在最前面**。
* **保存为全局前缀**: 勾选后,会将当前“全局前缀”写入插件全局状态,供后续所有 PQ 节点默认使用。
* **画师风格提示词 (前缀)**: 输入风格、艺术家等前缀。
* **多行文本**: 核心主体提示词列表,每行一个。
* **提示词后缀**: 画面质量、细节描述等通用后缀。
* **使用文件**: 从文件读取提示词列表(每行一个)。
* **提示词文件夹 / 文件名**: 将文件路径拆分为“文件夹 + 文件名”,文件扩展名固定为 `.txt`。
* 示例:文件夹 `C:\Users\Administrator\Desktop\prompt` + 文件名 `21` -> 实际读取 `...\prompt\21.txt`
* **索引设置 (核心功能)**: 控制本次运行使用哪一行提示词。
* `-1` (或留空): **记忆模式**。接着上次的进度继续跑(适合长序列生成)。
* `0`: **重置模式**。强制本次任务从第 1 行开始(适合完成调试后想完整跑一遍)。
* `5`: **锁定模式**。强制本次任务只跑第 6 行(适合反复调试特定提示词)。
* `2,8,35`: **列表模式**。只在第 3、9、36 行之间循环生成(适合精选生成)。
* `0`: **顺序模式**。重置计数器,从第 1 行开始,并在每次生成后自动递增(适合想从头完整跑一遍队列)。
* `5` (数字 > 0): **指定模式**。强制只跑第 N 行 (如输入 5 则跑第 5 行),**不会**自动递增(适合反复调试特定提示词)。
* `2,8,35`: **列表模式**。只在第 2、8、35 行之间循环生成(适合精选生成)。
* **文本输出**: 提供 `prompt_text` 输出端口,可连接 ShowText 节点查看当前使用的完整提示词。
### 2. PQ (Batch) / 批量队列 🆕
@@ -51,6 +56,7 @@
* **预设支持**: 现已支持保存和加载预设模板。
* **⚠️ 注意**: 保存或删除预设后,请**刷新浏览器**以更新预设列表。
* **文本输出**: 提供 `prompt_text_list` 输出端口,输出当前批次的所有提示词文本。
* **提示词来源**: 同样支持“使用文件 + 提示词文件夹/文件名(固定 .txt)”。
### 3. PQ Simple / 简易版系列 🆕
包含 **增量简易** (`PQ Simple (Incr.)`) 和 **批量简易** (`PQ Simple (Batch)`) 两个节点。
+162 -39
View File
@@ -20,19 +20,61 @@ NODE_DISPLAY_NAME_MAPPINGS = {}
PARAMETER_LABELS = {
"multiline_text": "多行文本",
"use_file": "使用文件",
"file_path": "文件路径",
"file_folder": "提示词文件夹",
"file_name": "文件名",
"mode": "运行模式",
"label": "队列标识",
"preset": "预设模板",
"template": "模板",
"save_preset": "保存预设",
"delete_preset": "删除预设",
"global_prefix": "全局前缀",
"save_global_prefix": "保存为全局前缀",
"artist_style_prompt": "画师风格提示词",
"prompt_suffix": "提示词后缀",
"preset_name_for_save": "保存预设名称",
"index_setting": "索引设置"
}
def _pq_normalize_prompt_part(s: str) -> str:
"""清理提示词片段首尾空白与多余逗号,避免拼接后出现 ', ,' 或 ',,'。"""
if s is None:
return ""
s = str(s).strip()
# 仅处理首尾逗号(不改中间内容)
s = s.strip(",").strip()
return s
_PQ_GLOBAL_PREFIX_CATEGORY = "Global"
_PQ_GLOBAL_PREFIX_KEY = "Global Prefix"
def _pq_get_global_prefix() -> str:
return str(_pq_store.get(_PQ_GLOBAL_PREFIX_CATEGORY, _PQ_GLOBAL_PREFIX_KEY, "") or "")
def _pq_set_global_prefix(value: str) -> None:
_pq_store.put(_PQ_GLOBAL_PREFIX_CATEGORY, _PQ_GLOBAL_PREFIX_KEY, str(value or ""))
def _pq_build_prompt_file_path(file_folder: str, file_name: str) -> str:
"""
将“提示词文件夹 + 文件名”组合成最终 .txt 路径。
- file_name 若已包含 .txt(忽略大小写),则不重复追加。
- file_folder 可为空;为空时返回 file_name(便于直接写完整路径)。
"""
folder = str(file_folder or "").strip()
name = str(file_name or "").strip()
if not name:
return ""
if not name.lower().endswith(".txt"):
name = name + ".txt"
if folder:
return os.path.join(folder, name)
return name
def parse_frame_prompts(raw_text: str):
"""
解析输入为 {frame_index: prompt} 的升序字典。
@@ -328,7 +370,7 @@ class StylePromptQueue:
[增量模式] 风格化提示词队列节点。
- 每次运行仅输出一行提示词。
- 即使 Batch Size > 1,也全部使用这一行(用于提高容错率)。
- 索引设置:支持 "-1"(记忆), "0"(重置), "5"(指定行), "2,8,35"(列表)
- 索引设置:支持 "-1"(记忆/自动), "0"(从0开始), "5"(从5开始), "2,8,35"(列表循环)
"""
@classmethod
def INPUT_TYPES(cls):
@@ -339,11 +381,14 @@ class StylePromptQueue:
return {
"required": {
"clip": ("CLIP",),
"全局前缀": ("STRING", {"multiline": False, "default": ""}),
"保存为全局前缀": ("BOOLEAN", {"default": False}),
"画师风格提示词": ("STRING", {"multiline": False, "default": ""}),
"多行文本": ("STRING", {"multiline": True, "default": "A cat\nA dog"}),
"提示词后缀": ("STRING", {"multiline": False, "default": ""}),
"使用文件": ("BOOLEAN", {"default": False}),
"文件路径": ("STRING", {"multiline": False, "default": ""}),
"提示词文件夹": ("STRING", {"multiline": False, "default": ""}),
"文件名": ("STRING", {"multiline": False, "default": ""}),
"索引设置": ("STRING", {"multiline": False, "default": "-1"}),
"预设模板": (preset_list, {"default": "None"}),
"保存预设名称": ("STRING", {"multiline": False, "default": ""}),
@@ -357,7 +402,12 @@ class StylePromptQueue:
FUNCTION = "run"
CATEGORY = "PromptQueue"
def run(self, clip, 画师风格提示词, 多行文本, 提示词后缀, 使用文件, 文件路径, 索引设置, 预设模板, 保存预设名称, 保存预设, 删除预设):
def run(self, clip, 全局前缀, 保存为全局前缀, 画师风格提示词, 多行文本, 提示词后缀, 使用文件, 提示词文件夹, 文件名, 索引设置, 预设模板, 保存预设名称, 保存预设, 删除预设, unique_id=None):
# 处理全局前缀(独立于预设逻辑,永远保留最高层级)
if 保存为全局前缀:
_pq_set_global_prefix(全局前缀)
global_prefix = _pq_normalize_prompt_part(全局前缀) or _pq_normalize_prompt_part(_pq_get_global_prefix())
prefix = 画师风格提示词
suffix = 提示词后缀
@@ -412,12 +462,18 @@ class StylePromptQueue:
suffix = ""
print(f"[Style Prompt Queue] 无法解析预设 '{预设模板}',已作为前缀处理")
lines = _read_lines_from_source(多行文本, 文件路径, 使用文件)
# 组合文件路径(优先“文件夹+文件名”,回退到“文件路径”以兼容旧工作流)
effective_file_path = _pq_build_prompt_file_path(提示词文件夹, 文件名)
lines = _read_lines_from_source(多行文本, effective_file_path, 使用文件)
# 应用前缀和后缀
final_lines = []
for line in lines:
parts = [p for p in [prefix, line, suffix] if p and p.strip()]
parts = []
for p in [global_prefix, prefix, line, suffix]:
pp = _pq_normalize_prompt_part(p)
if pp:
parts.append(pp)
final_lines.append(", ".join(parts))
if not final_lines:
@@ -430,31 +486,61 @@ class StylePromptQueue:
idx_mode = indices[0]
target_idx = 0
desc = _pq_source_descriptor(使用文件, effective_file_path, 多行文本)
# 扩展逻辑:支持 "Start From X" 且自动递增
# 检测 索引设置变更 或 源内容变更
queue_label = "Auto_Queue"
if unique_id:
queue_label = f"{unique_id}_Auto_Queue"
current_setting_str = str(索引设置).strip()
# 索引设置状态也需要隔离,否则不同节点使用相同文本但不同设置时会互相干扰
setting_key = f"{queue_label}_Settings_{desc}"
last_setting = _pq_store.get("Index Settings", setting_key, "")
setting_changed = (current_setting_str != last_setting)
if setting_changed:
_pq_store.put("Index Settings", setting_key, current_setting_str)
# 获取 Auto_Queue 的最后一次源,用于判断是否发生源变更
last_source = _pq_store.get("PromptQueue Sources", queue_label, None)
source_changed = (last_source != desc)
should_reset_counter = setting_changed or source_changed
if idx_mode == -1:
# 记忆模式 (Auto/Resume)
# 使用原有的 _pq_get_next_index 逻辑,自动递增
# 队列标识这里隐式使用 "Index Setting - Auto" 或者是文件名哈希,
# 为了简单起见,我们还是需要一个 Key。
# 这里我们用 "Auto_Queue" + 文件描述符 作为 key
desc = _pq_source_descriptor(使用文件, 文件路径, 多行文本)
target_idx = _pq_get_next_index("Auto_Queue", len(final_lines), desc)
target_idx = _pq_get_next_index(queue_label, len(final_lines), desc)
elif idx_mode == 0:
# 重置/顺序模式 (Reset to 0)
target_idx = 0
elif len(indices) == 1 and idx_mode >= 0:
# 0: Auto from beginning (Start from 0 + Auto Increment)
# N > 0: Fixed single line N (1-based index) -> Index N-1
if idx_mode == 0:
# Auto Mode (0)
if should_reset_counter:
_pq_store.put("PromptQueue Counters", queue_label, 0)
_pq_store.put("PromptQueue Sources", queue_label, desc)
target_idx = _pq_get_next_index(queue_label, len(final_lines), desc)
else:
# Fixed Line Mode (e.g. 1 -> 1st line -> index 0)
# Non-incremental
target_idx = idx_mode - 1
else:
# 指定模式 / 列表模式
if len(indices) == 1:
target_idx = indices[0]
else:
# 列表循环
# 使用一个基于列表内容的特殊 key
list_key = f"List_{索引设置}"
# 这里的 total 是列表长度
list_idx = _pq_get_next_index(list_key, len(indices), list_key)
target_idx = indices[list_idx]
# 列表模式 (2,8,35) -> 视为 1-based index
# 使用一个基于列表内容的特殊 key
list_key = f"List_{索引设置}"
# 这里的 total 是列表长度
list_idx = _pq_get_next_index(list_key, len(indices), list_key)
val = indices[list_idx]
# 转换为 0-based
target_idx = val - 1
if target_idx < 0:
target_idx = 0
# 越界保护
if target_idx >= len(final_lines):
@@ -462,6 +548,9 @@ class StylePromptQueue:
if target_idx < 0:
target_idx = 0
# Debug print
print(f"[PromptQueue] Index Setting: '{索引设置}' -> Mode: {idx_mode} -> Target Line: {target_idx + 1}")
line_to_process = final_lines[target_idx]
ordered = [(0, line_to_process)]
@@ -470,7 +559,7 @@ class StylePromptQueue:
return ([[cond, meta]], line_to_process)
@classmethod
def IS_CHANGED(cls, clip, 画师风格提示词, 多行文本, 提示词后缀, 使用文件, 文件路径, 索引设置, 预设模板, 保存预设名称, 保存预设, 删除预设):
def IS_CHANGED(cls, clip, 全局前缀, 保存为全局前缀, 画师风格提示词, 多行文本, 提示词后缀, 使用文件, 提示词文件夹, 文件名, 索引设置, 预设模板, 保存预设名称, 保存预设, 删除预设):
# 始终返回时间戳以强制重新执行
return str(time.time())
@@ -487,22 +576,41 @@ class SimpleStylePromptQueue(StylePromptQueue):
return {
"required": {
"clip": ("CLIP",),
"全局前缀": ("STRING", {"multiline": False, "default": ""}),
"保存为全局前缀": ("BOOLEAN", {"default": False}),
"画师风格提示词": ("STRING", {"multiline": False, "default": ""}),
"多行文本": ("STRING", {"multiline": True, "default": "A cat\nA dog"}),
"提示词后缀": ("STRING", {"multiline": False, "default": ""}),
"使用文件": ("BOOLEAN", {"default": False}),
"文件路径": ("STRING", {"multiline": False, "default": ""}),
"提示词文件夹": ("STRING", {"multiline": False, "default": ""}),
"文件名": ("STRING", {"multiline": False, "default": ""}),
"索引设置": ("STRING", {"multiline": False, "default": "-1"}),
"预设模板": (preset_list, {"default": "None"}),
}
}
def run(self, clip, 画师风格提示词, 多行文本, 提示词后缀, 使用文件, 文件路径, 索引设置, 预设模板):
def run(self, clip, 全局前缀, 保存为全局前缀, 画师风格提示词, 多行文本, 提示词后缀, 使用文件, 提示词文件夹, 文件名, 索引设置, 预设模板, unique_id=None):
# 调用父类的 run,传入 None/False 作为预设参数
return super().run(clip, 画师风格提示词, 多行文本, 提示词后缀, 使用文件, 文件路径, 索引设置, 预设模板, "", False, False)
return super().run(
clip,
全局前缀,
保存为全局前缀,
画师风格提示词,
多行文本,
提示词后缀,
使用文件,
提示词文件夹,
文件名,
索引设置,
预设模板,
"",
False,
False,
unique_id=unique_id
)
@classmethod
def IS_CHANGED(cls, clip, 画师风格提示词, 多行文本, 提示词后缀, 使用文件, 文件路径, 索引设置, 预设模板):
def IS_CHANGED(cls, clip, 全局前缀, 保存为全局前缀, 画师风格提示词, 多行文本, 提示词后缀, 使用文件, 提示词文件夹, 文件名, 索引设置, 预设模板):
return str(time.time())
@@ -520,11 +628,14 @@ class StylePromptQueueBatch:
return {
"required": {
"clip": ("CLIP",),
"全局前缀": ("STRING", {"multiline": False, "default": ""}),
"保存为全局前缀": ("BOOLEAN", {"default": False}),
"画师风格提示词": ("STRING", {"multiline": False, "default": ""}),
"多行文本": ("STRING", {"multiline": True, "default": "A cat\nA dog"}),
"提示词后缀": ("STRING", {"multiline": False, "default": ""}),
"使用文件": ("BOOLEAN", {"default": False}),
"文件路径": ("STRING", {"multiline": False, "default": ""}),
"提示词文件夹": ("STRING", {"multiline": False, "default": ""}),
"文件名": ("STRING", {"multiline": False, "default": ""}),
"索引设置": ("STRING", {"multiline": False, "default": "0"}),
"预设模板": (preset_list, {"default": "None"}),
"保存预设名称": ("STRING", {"multiline": False, "default": ""}),
@@ -538,7 +649,11 @@ class StylePromptQueueBatch:
FUNCTION = "run"
CATEGORY = "PromptQueue"
def run(self, clip, 画师风格提示词, 多行文本, 提示词后缀, 使用文件, 文件路径, 索引设置, 预设模板, 保存预设名称, 保存预设, 删除预设):
def run(self, clip, 全局前缀, 保存为全局前缀, 画师风格提示词, 多行文本, 提示词后缀, 使用文件, 提示词文件夹, 文件名, 索引设置, 预设模板, 保存预设名称, 保存预设, 删除预设):
if 保存为全局前缀:
_pq_set_global_prefix(全局前缀)
global_prefix = _pq_normalize_prompt_part(全局前缀) or _pq_normalize_prompt_part(_pq_get_global_prefix())
prefix = 画师风格提示词
suffix = 提示词后缀
@@ -579,11 +694,16 @@ class StylePromptQueueBatch:
prefix = preset_content
suffix = ""
lines = _read_lines_from_source(多行文本, 文件路径, 使用文件)
effective_file_path = _pq_build_prompt_file_path(提示词文件夹, 文件名)
lines = _read_lines_from_source(多行文本, effective_file_path, 使用文件)
final_lines = []
for line in lines:
parts = [p for p in [prefix, line, suffix] if p and p.strip()]
parts = []
for p in [global_prefix, prefix, line, suffix]:
pp = _pq_normalize_prompt_part(p)
if pp:
parts.append(pp)
final_lines.append(", ".join(parts))
if not final_lines:
@@ -651,22 +771,25 @@ class SimpleStylePromptQueueBatch(StylePromptQueueBatch):
return {
"required": {
"clip": ("CLIP",),
"全局前缀": ("STRING", {"multiline": False, "default": ""}),
"保存为全局前缀": ("BOOLEAN", {"default": False}),
"画师风格提示词": ("STRING", {"multiline": False, "default": ""}),
"多行文本": ("STRING", {"multiline": True, "default": "A cat\nA dog"}),
"提示词后缀": ("STRING", {"multiline": False, "default": ""}),
"使用文件": ("BOOLEAN", {"default": False}),
"文件路径": ("STRING", {"multiline": False, "default": ""}),
"提示词文件夹": ("STRING", {"multiline": False, "default": ""}),
"文件名": ("STRING", {"multiline": False, "default": ""}),
"索引设置": ("STRING", {"multiline": False, "default": "0"}),
"预设模板": (preset_list, {"default": "None"}),
}
}
def run(self, clip, 画师风格提示词, 多行文本, 提示词后缀, 使用文件, 文件路径, 索引设置, 预设模板):
def run(self, clip, 全局前缀, 保存为全局前缀, 画师风格提示词, 多行文本, 提示词后缀, 使用文件, 提示词文件夹, 文件名, 索引设置, 预设模板):
# 调用父类的 run,传入 None 作为预设参数
return super().run(clip, 画师风格提示词, 多行文本, 提示词后缀, 使用文件, 文件路径, 索引设置, 预设模板, "", False, False)
return super().run(clip, 全局前缀, 保存为全局前缀, 画师风格提示词, 多行文本, 提示词后缀, 使用文件, 提示词文件夹, 文件名, 索引设置, 预设模板, "", False, False)
@classmethod
def IS_CHANGED(cls, clip, 画师风格提示词, 多行文本, 提示词后缀, 使用文件, 文件路径, 索引设置, 预设模板):
def IS_CHANGED(cls, clip, 全局前缀, 保存为全局前缀, 画师风格提示词, 多行文本, 提示词后缀, 使用文件, 提示词文件夹, 文件名, 索引设置, 预设模板):
return float("nan")
@@ -797,7 +920,7 @@ def _pq_delete_negative_template_preset(name: str) -> None:
# 注册新节点
NODE_CLASS_MAPPINGS.update({
"NegativePromptQueue": NegativePromptQueue,
"StylePromptQueue": "PromptQueue 增量",
"StylePromptQueue": StylePromptQueue,
"SimpleStylePromptQueue": SimpleStylePromptQueue,
"StylePromptQueueBatch": StylePromptQueueBatch,
"SimpleStylePromptQueueBatch": SimpleStylePromptQueueBatch,