Hello, I’m encountering some breakpoint messages that I would like to avoid. I believe this is because I am making changes prior to solution completion, but I am not familiar with the methods required to manage this safely & effectively. Other than these breakpoint messages, the script is working functionally as intended.
The below sample is a snippet extracted a larger project that is still in progress. I have aimed to keeping the sample code as simple and well-commented as possible for easier review. Apologies in advance if the sample is verbose and messy! Thanks in advance. 
Sample File:
Example.gh (18.8 KB)
Video Clip:
Description:
Context: Rhino8 Python 3 Script Editor
Objectives: Modify component output parameters based on a dictionary of {key : value}
pairs (simulated in the sample as a Grasshopper data tree, input into the _data
input node). Disconnect downstream nodes if there is any update to the data structure.
Issues:
-
Disconnecting downstream nodes triggers a breakpoint, leading to errors such as “Panel () object expired during solution” and “An exception was thrown during a solution: Component Panel,” or “Collection was modified; enumeration operation may not execute.”
-
Modifying the input data structure—specifically when reducing the number of {key:value}
pairs relative to the previous input—triggers a breakpoint. An example error message is: “Output parameters Index[5] too high or too low for Component Python 3 Script.”
Adding the Python code for the component below:
# -----------------------------------------------------------------------------
# Import libraries
# -----------------------------------------------------------------------------
import Grasshopper as gh
import System
import RhinoCodePluginGH.Parameters as rcpg
import Grasshopper.Kernel as kernel
# -----------------------------------------------------------------------------
# Define functions
# -----------------------------------------------------------------------------
def warning_message():
ghenv.Component.AddRuntimeMessage(gh.Kernel.GH_RuntimeMessageLevel.Warning, "Required input missing or invalid.")
# Define a function to add dynamic output parameters
def add_param(name,ind):
param = rcpg.ScriptVariableParam() # Constructor method
param.NickName = name # Add nickname
param.Name = name # Add name
param.Access = kernel.GH_ParamAccess.list
index = ind # Set position
ghenv.Component.Params.RegisterOutputParam(param)
ghenv.Component.Params.OnParametersChanged()
# Define a function to disconnect sources whenever the component is modified
def remove_sources():
# Get the current Grasshopper document / component info
doc = ghenv.Component.OnPingDocument()
component = ghenv.Component
output_params = component.Params.Output
output_comp_guids = []
for n in output_params:
output_comp_guids.append(n.InstanceGuid)
output_comp_guids_02 = []
for i in output_params:
recipients = list(i.Recipients)
for r in recipients:
output_comp_guids_02.append(r.InstanceGuid)
print(r.InstanceGuid)
removals = []
for obj in doc.Objects:
# Components
if hasattr(obj, 'Params') and hasattr(obj.Params, 'Input'):
for param_input in obj.Params.Input:
if param_input.InstanceGuid in output_comp_guids_02:
for item in output_comp_guids:
key = param_input
value = item
tuple_ = (key,value)
removals.append(tuple_)
# Parameters & Panels
elif hasattr(obj, 'Sources'):
sources = list(obj.Sources)
for param_input in sources:
if param_input.InstanceGuid in output_comp_guids:
key = obj
value = param_input.InstanceGuid
tuple_ = (key,value)
removals.append(tuple_)
else:
pass
# Perform your modifications
for item in removals:
item[0].RemoveSource(item[1])
item[0].ExpireSolution(False)
# -----------------------------------------------------------------------------
# Execute operations
# -----------------------------------------------------------------------------
# [0] Process & validate inputs
# ---------------------------------------------------------
# Split the key-value pairs
try:
keys = []
values = []
for branch in _data.Branches:
keys.append(str(branch[0]))
values.append(float(branch[1]))
except:
# Clear old outputs
remove_sources()
ghenv.Component.Params.Output.Clear()
warning_message()
# Ensure inputs are available, each value has a unique keyname, each keyname is unique
if any([x==None,keys==[None],values==[None],len(keys) != len(values),len(list(set(keys))) != len(keys)]):
remove_sources()
ghenv.Component.Params.Output.Clear()
warning_message()
# [1] Update the Grasshopper component
# ---------------------------------------------------------
else: # Validation passed
# Get a list of the previous outputs
previous_outputs = [param.Name for param in ghenv.Component.Params.Output]
# Create a list of desired outputs
current_outputs = keys
# Check if there are existing outputs
if len(previous_outputs)>0:
print("len(previous_outputs)>0")
# Check if existing outputs are not the same as the current
if set(previous_outputs) != set(current_outputs):
print("set(previous_outputs) != set(current_outputs)")
# Disconnect components
remove_sources()
# Clear old outputs
ghenv.Component.Params.Output.Clear()
# Create new outputs
for ind, item in enumerate (current_outputs):
add_param(item,ind)
# Skip if the existing and current are the same
else:
print("set(previous_outputs) == set(current_outputs)")
# pass
# Check if there are no existing outputs
else:
print("len(previous_outputs)==0")
# Create new outputs
for ind, item in enumerate (current_outputs):
add_param(item,ind)
# [2] Now that the component is updated, apply output values
# ---------------------------------------------------------
# Iterate for each output
for index, item in enumerate(current_outputs):
# Calculate each output value
output_value = x + values[index]
# Set the output result by accessing the newly created output parameter
ghenv.Component.Params.Output[index].AddVolatileData(gh.Kernel.Data.GH_Path(0), 0, output_value)