Saving multiple documents after updating user parameters

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report
We are attempting to update and save Fusion 360 documents with user parameters via the Python API. Our current working design consists of a top-level assembly, with one child assembly (which itself has two child components) and one child component. We call our script from the top-level assembly and recursively update parameters based on an input YAML file. While we can update parameters for child components in the two assemblies, the changes do not persist in the child components. In addition, we want to be able to save these documents from the API if possible; we can only save the new changes after updating out-of-date components (which in theory there should be no out of date components if we make persistent updates).
Our script, depending on the value of a boolean variable, either takes in a YAML file to update the parameters of the assembly, or writes the assembly parameters to a YAML file. An example YAML file is shown below:
testAssem: dp: num_cylinders: '80' testCylinder: dp: circleDiam: 80 mm circleHeight: 100 mm testWheelAndBox: testBox: dp: boxHeight: 200 mm boxLength: 100 mm boxWidth: 150 mm circleDepth: 20 mm circleDiam: 80 mm testWheel: dp: circleDiam: 50 mm circleHeight: 20 mm
"dp" or "design parameters" is the key corresponding to user parameters that we want to change. Other key names like "testAssem" or "testWheel" refer to Fusion designs, which will have user parameters under "dp".
Our script is located below:
def build_names_to_versions_helper(occurrences, input_dict): """ Helper used to build dictionary mapping component names used in parameters file to component names used in Fusion 360, e.g. testAssem -> testAssem v14 :param occurrences: Occurrences object used to access child occurrences :param input_dict: Python dict to update with new key-value pairs :return: The completed mapping of param component names to Fusion component names """ for i in range(0, occurrences.count): occ = occurrences.item(i) full_name = occ.name name_parts = full_name.split(" ") name_only = name_parts[0] input_dict[name_only] = full_name if occ.childOccurrences: child_dict = build_names_to_versions_helper(occ.childOccurrences, input_dict) child_copy = child_dict.copy() input_dict.update(child_copy) return input_dict def build_names_to_versions(root_component): """ Builds a mapping from parameter file component names to versioned Fusion 360 component names, e.g. testAssem -> testAssem v14 :param root_component: Root component of Fusion 360 assembly :return: Dictionary mapping parameter component names to Fusion 360 component names """ input_dict = {} full_name = root_component.name name_parts = full_name.split(" ") name_only = name_parts[0] input_dict[name_only] = full_name occurrences = root_component.occurrences.asList return build_names_to_versions_helper(occurrences, input_dict) def build_orig_params_helper(root_component): ret = {} if hasattr(root_component, "occurrences"): for i in range(root_component.occurrences.count): occurrence = root_component.occurrences.item(i) full_name = occurrence.name name_parts = full_name.split(" ") name_only = name_parts[0] ret[name_only] = build_orig_params_helper(occurrence.component) params = {} for i in range(root_component.parentDesign.userParameters.count): param = root_component.parentDesign.userParameters.item(i) params[param.name] = param.expression if root_component.parentDesign.userParameters.count > 0: ret["dp"] = params return ret def build_orig_params(root_component): result = build_orig_params_helper(root_component) name = root_component.name.split(" ")[0] return { name : result } def change_param_value(root_component, param_name, param_value): """ Update a Fusion 360 component's user parameter corresponding to param_name with expression param_value :param root_component: Component whose parameter is to be updated :param param_name: Name of user parameter to update :param param_value: New expression for user parameter :return: None """ #root_component.parentDocument.activate root_component.parentDesign.userParameters.itemByName(param_name).expression = param_value #root_component.parentDocument.save print("Updated component: {} {}={}".format(root_component.name, param_name, param_value)) def update_user_params(root_component, yaml_dict, component_names_to_versions): """ Updates a Fusion 360 assembly, with root_component as the root component, by setting the user parameters using the structure in yaml_dict, with name mappings in component_names_to_versions :param root_component: Root component of Fusion 360 assembly :param yaml_dict: Python dict built from yaml parameter file specifying which parameters to change :param component_names_to_versions: Python dict mapping parameter file component names to Fusion 360 component names :return: None """ for prop in list(yaml_dict.keys()): #testAssem if prop == "dp": # modify this component's params user_params = yaml_dict[prop] for param in list(user_params.keys()): expression = user_params[param] change_param_value(root_component, param, expression) elif prop == "hp": break else: # it's a component, not user params component_name = prop true_name = component_names_to_versions[component_name] working_occurrence = root_component.occurrences.asList.itemByName(true_name) if working_occurrence: # that means we are not operating at the highest level, i.e. it's a child occurrence update_user_params(working_occurrence.component, yaml_dict[prop], component_names_to_versions) else: # it's the root component update_user_params(root_component, yaml_dict[prop], component_names_to_versions) def run(context): """ Fusion 360 entry point. This script updates the user parameters of a Fusion 360 assembly using the structure specified in the yaml file located at the hard-coded file path. :param context: Fusion 360 context :return: None """ save_or_open = 1 #1 for save yaml, 0 for open yaml and change params ui = None print("IS THIS WORKING") try: app = adsk.core.Application.get() ui = app.userInterface product = app.activeProduct design = adsk.fusion.Design.cast(product) if not design: ui.messageBox('No active Fusion design', 'No Design') return # Get the root component of the active design. root_component = design.rootComponent #extract parameters from Fusion into a dictionary assembly_dict = build_orig_params(root_component) print("--------------------------") print(json.dumps(assembly_dict, indent=4)) if(save_or_open == 1): #Saves all the parameters in the assembly into a yaml file yamlSaveDialog = ui.createFileDialog() yamlSaveDialog.isMultiSelectEnabled = False yamlSaveDialog.title = "Specify yaml save file" yamlSaveDialog.filter = 'yaml files (*.yaml)' yamlSaveDialog.filterIndex = 0 saveDialogResult = yamlSaveDialog.showSave() if saveDialogResult == adsk.core.DialogResults.DialogOK: yaml_out_file = yamlSaveDialog.filename else: return with open(yaml_out_file, 'w+') as yamlOut: yaml.dump(assembly_dict, yamlOut, default_flow_style=False) else: component_names_to_versions = build_names_to_versions(root_component) print(json.dumps(component_names_to_versions)) # Takes in a yaml parameter file to change the parameters in assembly file into yamlFileDialog = ui.createFileDialog() yamlFileDialog.isMultiSelectEnabled = False yamlFileDialog.title = "Specify yaml parameter file" yamlFileDialog.filter = 'yaml files (*.yaml)' yamlFileDialog.filterIndex = 0 takeDialogResult = yamlFileDialog.showOpen() if takeDialogResult == adsk.core.DialogResults.DialogOK: yaml_file_path = yamlFileDialog.filename else: return with open(yaml_file_path, "r") as f: doc = yaml.load(f) update_user_params(root_component, doc, component_names_to_versions) except: print(traceback.format_exc())
TL;DR: We want to be able to update Fusion parameters via the Python API and SAVE the changes, and make sure the parameter updates are persistent through child components.