From 8ba21d0b442002e66389d6e87faf828bfe05b2ad Mon Sep 17 00:00:00 2001 From: Z-nonymous <123163912+Z-nonymous@users.noreply.github.com> Date: Tue, 31 Mar 2026 09:11:24 +0200 Subject: [PATCH] XY plot issue with XY Inputs Lora (#976) * Fixed the LoRA handling bug in [`py/libs/xyplot.py:365-395`](py/libs/xyplot.py:365). The original code used an if/else pattern that only processed one LoRA at a time: ```python xy_values = x_value if self.x_type == "Lora" else y_value ``` This caused issues when both X and Y axes contained LoRAs - only the X axis LoRA was processed, and when only Y had LoRAs, the latent array wasn't populated correctly. The fix now: 1. Creates an empty `lora_stack` list 2. Adds the X axis LoRA to the stack if `self.x_type == "Lora"` 3. Adds the Y axis LoRA to the stack if `self.y_type == "Lora"` 4. Appends any existing `plot_image_vars['lora_stack']` to the combined stack 5. Applies all LoRAs in sequence This ensures both X and Y LoRAs are properly combined and applied when both axes contain LoRA values, fixing the `IndexError` in `rearrange_tensors()` that occurred due to mismatched latent array dimensions. * Fixed: get_labels_and_sample loop structure (lines 588-623) The IndexError persisted because the latent array length didn't match the expected grid dimensions. The issue is in the nested loop structure of get_labels_and_sample() When only Y-axis has LoRA values (X is "None"), the nested loops don't generate the correct number of latents: This results in 0 latents instead of len(y_values) latents, causing the IndexError in rearrange_tensors() Replaced the nested loop structure with three explicit cases: X-only variation (self.y_type == 'None'): Iterates over X values only Y-only variation (self.x_type == 'None'): Iterates over Y values only Both X and Y variation: Nested iteration over both axes This ensures the correct number of latents are generated for all scenarios, fixing the IndexError in rearrange_tensors() that occurred when only Y-axis had values (like LoRAs). * Fix for the LoRA label generation logic in [`py/libs/xyplot.py`](py/libs/xyplot.py:63-69). The changes made to the `define_variable()` method: 1. **Reduced model name truncation** from 30 to 25 characters to leave room for weight information 2. **Changed weight format** from `(0.50)` to ` w:0.50` for better visibility 3. **Only show weight when it differs from default** (1.0) - this ensures weight is displayed for non-default values 4. **Added bounds check** for `len(arr) > 3` before accessing `arr[3]` to prevent potential IndexError Now when using the same LoRA with different weights (e.g., LoRA A at 0.5 and 1.0), each variation will have a distinct label in the axis, making it clear which weight is being applied in each column/row of the XY plot. --- py/libs/xyplot.py | 66 +++++++++++++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 22 deletions(-) diff --git a/py/libs/xyplot.py b/py/libs/xyplot.py index 8cab23f..37c8a6f 100644 --- a/py/libs/xyplot.py +++ b/py/libs/xyplot.py @@ -63,10 +63,10 @@ class easyXYPlot(): if value_type in ['Lora', 'Checkpoint']: arr = value.split(',') model_name = os.path.basename(os.path.splitext(arr[0])[0]) - trigger_words = ' ' + arr[3] if value_type == 'Lora' and len(arr[3]) > 2 else '' + trigger_words = ' ' + arr[3] if value_type == 'Lora' and len(arr) > 3 and len(arr[3]) > 2 else '' lora_weight = float(arr[1]) if value_type == 'Lora' and len(arr) > 1 else 0 - lora_weight_desc = f"({lora_weight:.2f})" if lora_weight > 0 else '' - value_label = f"{model_name[:30]}{lora_weight_desc} {trigger_words}" + lora_weight_desc = f" w:{lora_weight:.2f}" if value_type == 'Lora' and lora_weight != 1.0 else '' + value_label = f"{model_name[:25]}{lora_weight_desc}{trigger_words}" if value_type in ["ModelMergeBlocks"]: if ":" in value: @@ -367,9 +367,19 @@ class easyXYPlot(): # print(f"Lora: {x_value} {y_value}") model = model if model is not None else plot_image_vars["model"] clip = clip if clip is not None else plot_image_vars["clip"] - xy_values = x_value if self.x_type == "Lora" else y_value - lora_name, lora_model_strength, lora_clip_strength, _ = xy_values.split(",") - lora_stack = [{"lora_name": lora_name, "model": model, "clip" :clip, "model_strength": float(lora_model_strength), "clip_strength": float(lora_clip_strength)}] + + # Build lora_stack from both X and Y axes if both are LoRA types + lora_stack = [] + + # Add X axis LoRA if present + if self.x_type == "Lora": + lora_name, lora_model_strength, lora_clip_strength, _ = x_value.split(",") + lora_stack.append({"lora_name": lora_name, "model": model, "clip": clip, "model_strength": float(lora_model_strength), "clip_strength": float(lora_clip_strength)}) + + # Add Y axis LoRA if present + if self.y_type == "Lora": + lora_name, lora_model_strength, lora_clip_strength, _ = y_value.split(",") + lora_stack.append({"lora_name": lora_name, "model": model, "clip": clip, "model_strength": float(lora_model_strength), "clip_strength": float(lora_clip_strength)}) # print(f"new_lora_stack: {new_lora_stack}") @@ -577,28 +587,40 @@ class easyXYPlot(): def get_labels_and_sample(self, plot_image_vars, latent_image, preview_latent, start_step, last_step, force_full_denoise, disable_noise): - for x_index, x_value in enumerate(self.x_values): - plot_image_vars, x_value_label = self.define_variable(plot_image_vars, self.x_type, x_value, - x_index) - self.x_label = self.update_label(self.x_label, x_value_label, len(self.x_values)) - if self.y_type != 'None': + # Handle X-only variation (Y is "None") + if self.y_type == 'None': + for x_index, x_value in enumerate(self.x_values): + plot_image_vars, x_value_label = self.define_variable(plot_image_vars, self.x_type, x_value, x_index) + self.x_label = self.update_label(self.x_label, x_value_label, len(self.x_values)) + + self.image_list, self.max_width, self.max_height, self.latents_plot = self.sample_plot_image( + plot_image_vars, latent_image, preview_latent, self.latents_plot, self.image_list, + disable_noise, start_step, last_step, force_full_denoise, x_value) + self.num += 1 + # Handle Y-only variation (X is "None") + elif self.x_type == 'None': + for y_index, y_value in enumerate(self.y_values): + plot_image_vars, y_value_label = self.define_variable(plot_image_vars, self.y_type, y_value, y_index) + self.y_label = self.update_label(self.y_label, y_value_label, len(self.y_values)) + + self.image_list, self.max_width, self.max_height, self.latents_plot = self.sample_plot_image( + plot_image_vars, latent_image, preview_latent, self.latents_plot, self.image_list, + disable_noise, start_step, last_step, force_full_denoise, y_value=y_value) + self.num += 1 + # Handle both X and Y variation + else: + for x_index, x_value in enumerate(self.x_values): + plot_image_vars, x_value_label = self.define_variable(plot_image_vars, self.x_type, x_value, x_index) + self.x_label = self.update_label(self.x_label, x_value_label, len(self.x_values)) + for y_index, y_value in enumerate(self.y_values): - plot_image_vars, y_value_label = self.define_variable(plot_image_vars, self.y_type, y_value, - y_index) + plot_image_vars, y_value_label = self.define_variable(plot_image_vars, self.y_type, y_value, y_index) self.y_label = self.update_label(self.y_label, y_value_label, len(self.y_values)) - # ttNl(f'{CC.GREY}X: {x_value_label}, Y: {y_value_label}').t( - # f'Plot Values {self.num}/{self.total} ->').p() - + self.image_list, self.max_width, self.max_height, self.latents_plot = self.sample_plot_image( plot_image_vars, latent_image, preview_latent, self.latents_plot, self.image_list, disable_noise, start_step, last_step, force_full_denoise, x_value, y_value) self.num += 1 - else: - # ttNl(f'{CC.GREY}X: {x_value_label}').t(f'Plot Values {self.num}/{self.total} ->').p() - self.image_list, self.max_width, self.max_height, self.latents_plot = self.sample_plot_image( - plot_image_vars, latent_image, preview_latent, self.latents_plot, self.image_list, disable_noise, - start_step, last_step, force_full_denoise, x_value) - self.num += 1 # Rearrange latent array to match preview image grid self.latents_plot = self.rearrange_tensors(self.latents_plot, self.num_cols, self.num_rows)