選択結果の出力形式を整理。明示的な空候補に対応し、ノードのタイトル表示を改善。

This commit is contained in:
るみなー先生
2026-05-15 01:52:57 +09:00
parent 4389d11eb1
commit 28febe623b
4 changed files with 80 additions and 33 deletions
+29 -14
View File
@@ -16,6 +16,8 @@
- **区切りは `|` または実際の改行**
- `|` と改行の両方を区切りとして扱うので、混在していても動くぞ。
- 区切り文字が連続して出現した場合は、候補から取り除かれることに注意じゃ。
- 明示的に空白文字列を候補として返却させたい場合は、明示的に `()` と指定するのじゃ。
- **候補の前後の `,` と空白を自動で整理**
- `town,`
@@ -23,10 +25,6 @@
- `castle, fortress,`
のような書き方でも大丈夫じゃ。
- **プロンプト結合向けの返り値**
- 選ばれた候補の前後に `,` を付けて返すぞ。
- 例: `coffee shop` → `,coffee shop,`
- **現在の選択結果をタイトル表示**
- 実行時にノードタイトルが `Choice: coffee shop` のように更新されるぞ。
- KSamplerのプレビューと見比べやすいのじゃ。
@@ -34,6 +32,12 @@
- **複数設置に対応**
- 背景用、時間帯用、天気用など、複数ノードを置いてそれぞれ別々に使えるぞ。
- **ファイル名向けの返り値**
- ファイル名向けに安全化した文字列 `selected_text_safe` を出力するのじゃ。
- **明示的な空候補 `()` に対応**
- `()` が選ばれた場合、プロンプト向け出力は空文字に、ファイル名向け出力は `empty` になるのじゃ。
## 導入方法
ComfyUIの `custom_nodes` ディレクトリで、以下のコマンドを打ち込むのじゃ!
@@ -47,7 +51,7 @@ git clone https://github.com/ruminar/ComfyUI-PromptRandomChoice.git
1. `Prompt Random Choice` ノードを置く
2. `options_text` に候補を入れる (入力例を参照)
3. 必要に応じて `change_every` を選ぶ (NEW)
4. `prompt_text` を文字列結合ノードなどでポジティブプロンプトへ足す
4. `selected_text` を `Join String Multi` などの文字列結合ノードへ繋ぎ、ポジティブプロンプトへ足す
5. キューを好きなだけ積む
<br/>
@@ -95,6 +99,15 @@ flower field|
starry sky|
coffee shop|
```
### 何も追加しない候補
`()` は明示的な空候補として扱われます。
```text
()|(full body:0.9)
```
この例では、何も追加しない場合と、`(full body:0.9)` を追加する場合をランダムに切り替えられます。
### 選ばれやすさを調整したい場合
@@ -112,13 +125,20 @@ day|day|day|sunset|night
- `\n` という文字列は区切りとして扱わない
- 各候補の前後の空白と `,` を trim
- 空候補は無視
- 有効な候補が1件も無い場合は空文字 `""` を返す
- 候補があれば `,selected,` を返す
- `()` は明示的な空候補として扱う
- `change_every` が 1 なら毎回選び直す
- `change_every` が 2 以上なら、その回数ぶん同じ候補を維持する
- 実行時にタイトルへ `Choice: lake` や `Choice: (empty) (2/3)` のように表示する
## 出力
- `prompt_text`
- 例: `,coffee shop,` (結合しやすいように、前後をtrimしてから`,`を付与します)
- `selected_text`
trim後の選択文字列です。
`()` が選ばれた場合は空文字 `""` になります。
- `selected_text_safe`
ファイル名向けに安全化した出力です。
`selected_text` が空なら `empty` を返します。
<br/>
@@ -154,8 +174,3 @@ GPL-3.0(ComfyUI本体の掟に従っておるぞ!)
## 宣伝画像
<img width="1024" height="1536" alt="PromptRandomChoice説明画像v0 2 0" src="https://github.com/user-attachments/assets/84bd447c-5b17-409b-aa3e-cfe4bf369bed" />
※ 区切り文字は 改行 または `|` です。カンマは候補の前後に付いた場合だけ自動で取り除きます。 <br/><br/>
(最初の版)
<img width="1024" height="1536" alt="PromptRandomChoice説明画像" src="https://github.com/user-attachments/assets/e5308bd1-82cb-4f9b-bd89-0dbddb7a009e" />
+10 -3
View File
@@ -1,6 +1,10 @@
# v0.2.0
## v0.3.0
change_everyを追加したのじゃ!
- 出力を整理
- `selected_text` : プロンプト向け出力
- `selected_text_safe` : ファイル名向け出力
- 明示的な空候補 `()` に対応
- `()` が選ばれた場合、プロンプト向け出力は空文字になり、ファイル名向け出力は `empty` になります
`ComfyUI-PromptRandomChoice` は、プロンプト候補リストから実行のたびにランダムで1項目を選び、ポジティブプロンプトなどへ追加しやすい形で出力する ComfyUI カスタムノードです。
@@ -9,7 +13,8 @@ change_everyを追加したのじゃ!
- プロンプト候補から実行ごとにランダムで1項目を選択
- 候補は `|` または実際の改行で区切り
- 候補の前後にある空白や `,` を自動で trim
- 選ばれた候補を `,coffee shop,` のように前後カンマ付きで出力
- 選ばれた候補を `selected_text` として通常の文字列で出力
- ファイル名向けに安全化した `selected_text_safe` を出力
- `change_every` に対応
- `1` なら毎回選び直し
- `3` なら同じ候補を3回返してから次を選択
@@ -20,6 +25,8 @@ change_everyを追加したのじゃ!
- 背景用、時間帯用、天気用などを別々にランダム化可能
- 同じ候補を複数回書くことで、選ばれやすさを調整可能
- 例: `day|day|day|sunset|night`
- 明示的な空候補 `()` に対応
- `()` が選ばれた場合、プロンプト向け出力は空文字になり、ファイル名向け出力は `empty` になります
## 注意
+37 -10
View File
@@ -26,12 +26,38 @@ def _split_options(options_text: str):
options = []
for part in parts:
item = str(part).strip(" \t\r\n,")
if item:
options.append(item)
# Empty parts caused by repeated delimiters are ignored.
if not item:
continue
# Explicit empty choice.
# Example: ()|(full body:0.9)
if item == "()":
options.append("")
continue
options.append(item)
return options
def _safe_text(value: str):
text = str(value or "")
if not text:
return "empty"
text = text.replace("\\", "/").replace("/", "_")
text = re.sub(r'[<>:"|?*\x00-\x1f]', "_", text)
text = re.sub(r"[\s,]+", "_", text)
text = text.replace("(", "_").replace(")", "_")
text = re.sub(r"_+", "_", text)
text = text.strip().strip("._-")
return text or "empty"
class PromptRandomChoice:
def __init__(self):
self._current_choice = None
@@ -62,8 +88,8 @@ class PromptRandomChoice:
},
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("prompt_text",)
RETURN_TYPES = ("STRING", "STRING")
RETURN_NAMES = ("selected_text", "selected_text_safe")
FUNCTION = "pick"
CATEGORY = "utils/prompt"
@@ -90,11 +116,11 @@ class PromptRandomChoice:
self._reset_state()
return {
"ui": {
"selected_text": [""],
"selected_text_title": ["(empty)"],
"repeat_index": [0],
"change_every": [change_every],
},
"result": ("",),
"result": ("", "empty"),
}
state_changed = (
@@ -113,16 +139,17 @@ class PromptRandomChoice:
self._last_options_key = options_key
self._last_change_every = change_every
selected = self._current_choice
prompt_text = f",{selected},"
selected_text = str(self._current_choice or "")
selected_text_safe = _safe_text(selected_text)
selected_text_title = selected_text if selected_text else "(empty)"
return {
"ui": {
"selected_text": [selected],
"selected_text_title": [selected_text_title],
"repeat_index": [self._repeat_index],
"change_every": [change_every],
},
"result": (prompt_text,),
"result": (selected_text, selected_text_safe),
}
+4 -6
View File
@@ -24,16 +24,14 @@ app.registerExtension({
nodeType.prototype.onExecuted = function (message) {
originalOnExecuted?.apply(this, arguments);
const selected = message?.selected_text?.[0] ?? "";
const selectedTitle = message?.selected_text_title?.[0] ?? "(empty)";
const repeatIndex = Number(message?.repeat_index?.[0] ?? 0);
const changeEvery = Number(message?.change_every?.[0] ?? 1);
if (!selected) {
this.title = "Prompt Random Choice";
} else if (changeEvery <= 1) {
this.title = `Choice: ${shorten(selected)}`;
if (changeEvery <= 1) {
this.title = `Choice: ${shorten(selectedTitle)}`;
} else {
this.title = `Choice: ${shorten(selected)} (${repeatIndex}/${changeEvery})`;
this.title = `Choice: ${shorten(selectedTitle)} (${repeatIndex}/${changeEvery})`;
}
this.setDirtyCanvas?.(true, true);