both toggle
This commit is contained in:
+23
-8
@@ -368,7 +368,8 @@ class PowerLoraLoaderV2:
|
||||
|
||||
def __init__(self):
|
||||
self.properties = {
|
||||
"Show Strengths": "Single Strength" # Single Strength or Separate Model & Clip
|
||||
"Show Strengths": "Single Strength", # Single Strength or Separate Model & Clip
|
||||
"output_both": False # Apply all loras to model_low using low strength values
|
||||
}
|
||||
|
||||
@classmethod
|
||||
@@ -397,10 +398,13 @@ class PowerLoraLoaderV2:
|
||||
"""Combines functionality from both v1 and v2 loaders."""
|
||||
# Get show_strengths property
|
||||
show_strengths = getattr(self, 'Show Strengths', 'Single Strength')
|
||||
|
||||
|
||||
# Check if clip is actually connected (from JavaScript hasClip property)
|
||||
has_clip = getattr(self, 'hasClip', clip is not None)
|
||||
|
||||
# Check if output_both is enabled (from widget value or property)
|
||||
output_both = kwargs.get('output_both', self.properties.get('output_both', False))
|
||||
|
||||
# Initialize lists for WanVideo format
|
||||
loras_list = []
|
||||
low_loras_list = []
|
||||
@@ -438,7 +442,6 @@ class PowerLoraLoaderV2:
|
||||
if os.path.exists(lora_path):
|
||||
# Extract just the filename for consistency with the rest of the code
|
||||
lora_filename = os.path.basename(lora_name.replace('\\', '/'))
|
||||
print(f"[PowerLoraLoaderV2] Found LoRA at full path: '{lora_path}'")
|
||||
else:
|
||||
print(f"[PowerLoraLoaderV2] LoRA file '{lora_name}' not found in loras folder.")
|
||||
continue
|
||||
@@ -499,6 +502,23 @@ class PowerLoraLoaderV2:
|
||||
|
||||
loras_list.append(lora_entry)
|
||||
|
||||
# Handle output_both mode - apply all loras to model_low using low_strength
|
||||
if output_both:
|
||||
if model_low is not None:
|
||||
low_strength = value.get('low_strength', strength_model)
|
||||
if low_strength is None:
|
||||
low_strength = 0.0
|
||||
try:
|
||||
if not has_clip:
|
||||
model_low, _ = LoraLoader().load_lora(model_low, None, lora_filename, low_strength, 0)
|
||||
else:
|
||||
model_low, clip = LoraLoader().load_lora(model_low, clip, lora_filename, low_strength, 0)
|
||||
low_loras_summary.append((low_strength, f"{lora_filename} (output_both)"))
|
||||
except Exception as e:
|
||||
print(f"[PowerLoraLoaderV2] Error applying LoRA to model_low (output_both): {e}")
|
||||
else:
|
||||
pass # model_low is None, cannot apply OUTPUT_BOTH
|
||||
|
||||
# Check if JavaScript detected special mode flags
|
||||
use_for_both = value.get('use_for_both', False)
|
||||
low_only = value.get('low_only', False)
|
||||
@@ -511,12 +531,10 @@ class PowerLoraLoaderV2:
|
||||
# Handle different LoRA states for PowerLoraLoaderV2
|
||||
if high_only:
|
||||
# "High only" mode - apply to main model only (same as default but with visual indicator)
|
||||
print(f"[PowerLoraLoaderV2] Using LoRA '{lora_filename}' for HIGH model only")
|
||||
high_loras_summary.append((strength_model, f"{lora_filename} (high only)"))
|
||||
|
||||
elif low_only:
|
||||
# "Low only" mode - apply to model_low only
|
||||
print(f"[PowerLoraLoaderV2] Using LoRA '{lora_filename}' for LOW model only")
|
||||
# Apply LoRA to model_low if it exists
|
||||
if model_low is not None:
|
||||
try:
|
||||
@@ -531,7 +549,6 @@ class PowerLoraLoaderV2:
|
||||
|
||||
elif use_for_both:
|
||||
# "Enable for both" mode - apply to both main and model_low
|
||||
print(f"[PowerLoraLoaderV2] Using LoRA '{lora_filename}' for both HIGH and LOW models")
|
||||
high_loras_summary.append((strength_model, f"{lora_filename} (both)"))
|
||||
|
||||
# Apply LoRA to model_low if it exists
|
||||
@@ -581,8 +598,6 @@ class PowerLoraLoaderV2:
|
||||
else:
|
||||
# Clip provided, apply to model_low with clip
|
||||
model_low, _ = LoraLoader().load_lora(model_low, clip, low_lora_path, low_strength, strength_clip if strength_clip is not None else 0)
|
||||
|
||||
print(f"[PowerLoraLoaderV2] Applied low variant LoRA '{low_variant_name}' (strength: {low_strength}) to model_low")
|
||||
except Exception as e:
|
||||
print(f"[PowerLoraLoaderV2] Error applying low variant LoRA '{low_variant_name}' to model_low: {e}")
|
||||
|
||||
|
||||
@@ -434,12 +434,10 @@ async def api_save_lora_preview_image(request):
|
||||
base_dir = folder_paths.get_folder_paths('checkpoints')[0]
|
||||
# Use _power_preview subdirectory for model previews
|
||||
power_preview_dir = os.path.join(base_dir, '_power_preview')
|
||||
print(f"[Preview Image Debug] MODEL: Using directory {power_preview_dir}")
|
||||
else:
|
||||
# For LoRAs, use loras directory
|
||||
base_dir = folder_paths.get_folder_paths('loras')[0]
|
||||
power_preview_dir = os.path.join(base_dir, '_power_preview')
|
||||
print(f"[Preview Image Debug] LORA: Using directory {power_preview_dir}")
|
||||
|
||||
if lora_path:
|
||||
save_dir = os.path.join(power_preview_dir, lora_path)
|
||||
@@ -448,13 +446,10 @@ async def api_save_lora_preview_image(request):
|
||||
|
||||
# Ensure directory exists
|
||||
os.makedirs(save_dir, exist_ok=True)
|
||||
print(f"[Preview Image Debug] Directory ensured: {save_dir}")
|
||||
print(f"[Preview Image Debug] Directory exists check: {os.path.exists(save_dir)}")
|
||||
|
||||
# Construct save path with optional suffix
|
||||
filename = f"{lora_name}{suffix}.jpg"
|
||||
save_path = os.path.join(save_dir, filename)
|
||||
print(f"[Preview Image Debug] Saving image to: {save_path}")
|
||||
|
||||
# Read image data
|
||||
image_data = image_file.file.read()
|
||||
|
||||
@@ -146,6 +146,7 @@ app.registerExtension({
|
||||
this.hasClip = false; // Initialize clip connection state
|
||||
this.properties = this.properties || {};
|
||||
this.properties['Show Strengths'] = "Single Strength";
|
||||
this.properties['output_both'] = false;
|
||||
this.addNonLoraWidgets();
|
||||
|
||||
|
||||
@@ -221,6 +222,12 @@ app.registerExtension({
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sync output_both property to widget
|
||||
const outputBothWidget = this.widgets.find(w => w.name === "output_both");
|
||||
if (outputBothWidget && this.properties.output_both !== undefined) {
|
||||
outputBothWidget.value = this.properties.output_both;
|
||||
}
|
||||
};
|
||||
|
||||
nodeType.prototype.addNewLoraWidget = function () {
|
||||
@@ -253,6 +260,12 @@ app.registerExtension({
|
||||
{ serialize: true });
|
||||
uiStateWidget.type = "hidden";
|
||||
// Keep serialize: true to ensure UI state is saved in workflow JSON
|
||||
|
||||
// Add hidden widget for output_both toggle state
|
||||
const outputBothValue = this.properties.output_both || false;
|
||||
const outputBothWidget = this.addWidget("boolean", "output_both", outputBothValue, () => {},
|
||||
{ serialize: true });
|
||||
outputBothWidget.type = "hidden";
|
||||
};
|
||||
|
||||
nodeType.prototype.allLorasState = function() {
|
||||
|
||||
@@ -252,6 +252,7 @@ export class StrengthCopyWidget extends RgthreeBaseWidget {
|
||||
super("StrengthCopyWidget");
|
||||
this.value = {};
|
||||
this.hitAreas = {
|
||||
output_both_toggle: { bounds: [0, 0] },
|
||||
high_to_low_button: { bounds: [0, 0] },
|
||||
low_to_high_button: { bounds: [0, 0] },
|
||||
};
|
||||
@@ -260,19 +261,39 @@ export class StrengthCopyWidget extends RgthreeBaseWidget {
|
||||
}
|
||||
|
||||
draw(ctx, node, w, posY, height) {
|
||||
// Ensure property exists (off by default)
|
||||
if (!node.properties) node.properties = {};
|
||||
if (node.properties['output_both'] === undefined) node.properties['output_both'] = false;
|
||||
|
||||
// Constants
|
||||
const margin = 20;
|
||||
const buttonWidth = 25;
|
||||
const buttonHeight = height;
|
||||
const buttonSpacing = 36;
|
||||
const rightOffset = 25;
|
||||
const innerMargin = 8;
|
||||
|
||||
// Calculated values
|
||||
const midY = posY + height * 0.5;
|
||||
const totalButtonsWidth = (buttonWidth * 2) + buttonSpacing;
|
||||
const rightX = w - margin - totalButtonsWidth - rightOffset;
|
||||
|
||||
ctx.save();
|
||||
ctx.textAlign = "left";
|
||||
ctx.textBaseline = "middle";
|
||||
ctx.fillStyle = LiteGraph.WIDGET_TEXT_COLOR;
|
||||
|
||||
// Draw "Output both" toggle
|
||||
const toggleSpacing = 15; // Extra spacing between toggle and buttons
|
||||
let posX = w - margin - totalButtonsWidth - rightOffset - toggleSpacing;
|
||||
const outputBothValue = node.properties['output_both'] || false;
|
||||
// Draw label and toggle from right to left
|
||||
const toggleBounds = drawTogglePart(ctx, [posX - height * 1.5, posY], height, outputBothValue);
|
||||
this.hitAreas['output_both_toggle'].bounds = toggleBounds;
|
||||
const labelText = "Use loras on both";
|
||||
ctx.fillText(labelText, posX - height * 1.5 - ctx.measureText(labelText).width - innerMargin, midY);
|
||||
|
||||
// Adjust rightX for buttons (they stay in same position)
|
||||
const rightX = w - margin - totalButtonsWidth - rightOffset;
|
||||
|
||||
// Draw ">" button (high to low)
|
||||
const highToLowX = rightX;
|
||||
@@ -288,6 +309,21 @@ export class StrengthCopyWidget extends RgthreeBaseWidget {
|
||||
}
|
||||
|
||||
onMouseDown(event, pos, node) {
|
||||
// Ensure property exists
|
||||
if (!node.properties) node.properties = {};
|
||||
if (node.properties['output_both'] === undefined) node.properties['output_both'] = false;
|
||||
|
||||
if (this.clickWasWithinBounds(pos, this.hitAreas.output_both_toggle.bounds)) {
|
||||
node.properties['output_both'] = !node.properties['output_both'];
|
||||
// Also sync to hidden widget for Python to read
|
||||
const outputBothWidget = node.widgets.find(w => w.name === "output_both");
|
||||
if (outputBothWidget) {
|
||||
outputBothWidget.value = node.properties['output_both'];
|
||||
}
|
||||
this.cancelMouseDown();
|
||||
node.setDirtyCanvas(true, true);
|
||||
return true;
|
||||
}
|
||||
if (this.clickWasWithinBounds(pos, this.hitAreas.high_to_low_button.bounds)) {
|
||||
this.highToLowPressed = true;
|
||||
node.setDirtyCanvas(true, true);
|
||||
@@ -339,6 +375,15 @@ export class StrengthCopyWidget extends RgthreeBaseWidget {
|
||||
}
|
||||
}
|
||||
|
||||
serializeValue(node, index) {
|
||||
// Ensure output_both property exists and is synchronized
|
||||
if (!node.properties) node.properties = {};
|
||||
if (node.properties['output_both'] === undefined) node.properties['output_both'] = false;
|
||||
// Return the value to ensure it's serialized
|
||||
node.properties['output_both'] = node.properties['output_both'] || false;
|
||||
return {};
|
||||
}
|
||||
|
||||
computeSize(width) {
|
||||
return [width, LiteGraph.NODE_WIDGET_HEIGHT];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user