def load_volumes(self, file_list, data_type, memory_map=False, fdr_thresholds=False): """ Load some volumes from a list of paths. Parameters ---------- file_list: list list of paths to volumes data_type: str vol data etc memory_map: bool whether to memory map after reading fdr_thresholds: dict q -> t statistic mappings {0.01: 3.4, 0.05:, 3.1} """ non_loaded = [] for i, vol_path in enumerate(file_list): try: self.model.add_volume(vol_path, data_type, memory_map, fdr_thresholds) self.appdata.add_used_volume(vol_path) if not self.any_data_loaded: # Load up one of the volumes just loaded into the bottom layer self.add_initial_volume() self.any_data_loaded = True except (IOError, RuntimeError) as e: # RT error Raised by SimpleITK print(e) non_loaded.append(vol_path) if len(non_loaded) > 0: common.error_dialog(self.mainwindow, 'Volumes not loaded', '\n'.join(non_loaded)) # self.any_data_loaded self.check_non_ras() self._auto_load_annotations(file_list)
def load_annotations(self, annotation_file_list): """ Load annotations that were previously saved as xml Parameters ---------- annotation_file_list Returns ------- """ non_loaded = [] for path in annotation_file_list: # At this point looked_at set to True error = self.model.load_annotation(path) if error: non_loaded.append(path) common.error_dialog(self.mainwindow, 'Annotations not loaded', error) else: # Load annotations - these should be called within self.model.load_annotation self.annotations_manager.populate_available_terms() self.annotations_manager.update() if not non_loaded: common.info_dialog(self.mainwindow, 'Load success', 'Annotations loaded')
def parameter_done_signal(self, child: QTreeWidgetItem, check_box: QCheckBox): vol = self.controller.current_annotation_volume() if not vol: error_dialog(self, "Error", "No volume selected") return base_node = child term = base_node.text(1) ann = vol.annotations.get_by_term(term) ann.looked_at = bool(check_box.isChecked())
def update_annotation(self, child: QTreeWidgetItem, cbox: QComboBox): """ On getting a change signal from the parameter option combobox, set options on the volume.annotations object Also set the selected row to active Parameters ---------- child: is the node in the QTreeWidget that corresponds to our paramter option selection cbox: Qt combobox that was clicked row: The row of the qtreewidget where the combobox is """ vol = self.controller.current_annotation_volume() if not vol: error_dialog(self, "Error", "No volume selected") return try: x = int(self.ui.labelXPos.text()) y = int(self.ui.labelYPos.text()) z = int(self.ui.labelZPos.text()) except ValueError: x = y = z = None base_node = child term = base_node.text(1) option = cbox.currentText() # If we are updating the annotation to ImageOnly, we should wipe any coordinates that may have been added to # a previous annotation option. if option == 'imageOnly': x = y = z = None elif option in (centre_stage_options.opts['options_requiring_points']): if None in (x, y, z): info_dialog( self, 'Select a region!', "For option '{}' coordinates must be specified".format( option)) # this will reset the option back to what it is on the volume.annotation object cbox.setCurrentIndex( cbox.findText( vol.annotations.get_by_term(term).selected_option)) return if not term: error_dialog(self, "Error", "No term is selected!") return vol.annotations.update_annotation(term, x, y, z, option) self.on_tree_clicked(base_node) # We are now autosaving. so save on each annotation term changed self.save_annotations(suppress_msg=True)
def update_annotation(self, child: QTreeWidgetItem, cbox: QComboBox): """ On getting a change signal from the parameter option combobox, set options on the volume.annotations object Also set the selected row to active Parameters ---------- child: is the node in the QTreeWidget that corresponds to our paramter option selection cbox: Qt combobox that was clicked row: The row of the qtreewidget where the combobox is """ vol = self.controller.current_annotation_volume() if not vol: error_dialog(self, "Error", "No volume selected") return try: x = int(self.ui.labelXPos.text()) y = int(self.ui.labelYPos.text()) z = int(self.ui.labelZPos.text()) except ValueError: x = y = z = None base_node = child term = base_node.text(1) option = cbox.currentText() # If we are updating the annotation to ImageOnly, we should wipe any coordinates that may have been added to # a previous annotation option. if option == 'imageOnly': x = y = z = None elif option in (centre_stage_options.opts['options_requiring_points']): if None in (x, y, z): info_dialog(self, 'Select a region!', "For option '{}' coordinates must be specified".format(option)) # this will reset the option back to what it is on the volume.annotation object cbox.setCurrentIndex(cbox.findText(vol.annotations.get_by_term(term).selected_option)) return if not term: error_dialog(self, "Error", "No term is selected!") return vol.annotations.update_annotation(term, x, y, z, option) self.on_tree_clicked(base_node) # We are now autosaving. so save on each annotation term changed self.save_annotations(suppress_msg=True)
def load_data(self): dir_ = QFileDialog.getExistingDirectory(None, "Select root directory containing lama runs", self.appdata.last_qc_dir) self.appdata.last_qc_dir = str(dir_) root = Path(dir_) self.load_atlas_metadata() if self.specimens: # Any qc in memory? doit = question_dialog(self.mainwindow, 'Load QC?', 'This will delete any previously made qc flags') if not doit: return last_qc_dir = self.appdata.last_qc_output_dir if not last_qc_dir: last_qc_dir = Path() suggested_qc_file = Path(last_qc_dir) / f'{root.name}_vpv_qc.yaml' res = QFileDialog.getSaveFileName(self.mainwindow, "Select new or existing qc file", str(suggested_qc_file), "QC files (*.yaml)") self.qc_results_file = Path(res[0]) self.appdata.last_qc_output_dir = str(self.qc_results_file.parent) # create dir for storing screenshota self.screenshot_dir = self.qc_results_file.parent / 'screenshots' self.screenshot_dir.mkdir(exist_ok=True) # Check that we can write to this directory if not os.access(self.qc_results_file.parent, os.W_OK): error_dialog(self.mainwindow, 'Data not loaded!', 'Directory needs to be writable for qc output') return self.load_qc(root) self.root_dir = root # self.load_specimens_into_vpv(root) self.update_specimen_list() self.load_specimen(0)
def __init__(self, parent, log_file): super(Logview, self).__init__(parent) self.ui = Ui_Dialog() self.ui.setupUi(self) self.log_file = log_file self.ui.pushButtonClose.clicked.connect(self.close) self.ui.pushButtonCopyText.clicked.connect(self.copy_text) self.ui.pushButtonClearLog.clicked.connect(self.clear_log) try: with open(log_file, 'r') as fh: lines = fh.readlines() except (IOError, FileNotFoundError) as e: error_dialog(parent, "Cannot open logfile", log_file) return self.log_text = ''.join(lines) self.ui.textBrowser.setPlainText(self.log_text) self.show()
def load_annotations(self, annotation_file_list): """ Load annotations that were previosly saved as xml Parameters ---------- annotation_file_list Returns ------- """ non_loaded = [] for path in annotation_file_list: # At this point looked_at set to True error = self.model.load_annotation(path) if error: non_loaded.append(path) common.error_dialog(self.mainwindow, 'Annotations not loaded', error) else: # Load annotations - these should be called within self.model.load_annotation self.annotations_manager.populate_available_terms() self.annotations_manager.update() if not non_loaded: common.info_dialog(self.mainwindow, 'Load success', 'Annotations loaded')
def save_annotations(self, suppress_msg=False): """ Parameters ---------- suppress_msg: bool When doing autosave do not give a dialog informing od save (True) When doing manual save, give dialog with saved file path Returns ------- """ annotator_id = self.ui.lineEditAnnotatorId.text() if annotator_id.isspace() or not annotator_id: error_dialog(self, 'Experimenter ID missing', "An annotator ID is required") return saved_file_paths = [] for vol in self.controller.model.all_volumes(): points_for_series_media = [] # The first time an annotation is saved, get the date of annotation and set on volume # Next time it's loaded load the original date of annotation if vol.annotations.date_of_annotation: date_of_annotation = vol.annotations.date_of_annotation else: date_of_annotation = self.ui.dateEdit.date().toString('yyyy-MM-dd') vol.annotations.date_of_annotation = date_of_annotation xml_exporter = impc_xml.ExportXML(date_of_annotation, annotator_id, vol.annotations.metadata_parameter_file) ann = vol.annotations for i, a in enumerate(ann.annotations): xml_exporter.add_parameter(a.term, a.selected_option) if all((a.x, a.y, a.z)): points_for_series_media.append(a) # save as need to add points after simpleParameters xml_dir = vol.annotations.annotation_dir increment = get_increment(xml_dir) xml_file_name = "{}.{}.{}.experiment.impc.xml".format(vol.annotations.center, date_of_annotation, increment) xml_path = join(xml_dir, xml_file_name) # Add the series media parameter now we have SimpleParameters loaded, so we can add points to it xml_exporter.add_series_media_parameter() for a in points_for_series_media: xml_exporter.add_point(a.term, (a.x, a.y, a.z), (a.x_percent, a.y_percent, a.z_percent)) # Now we can add metadata at the bottom xml_exporter.add_metadata() try: xml_exporter.write(xml_path) except OSError as e: error_dialog(self, 'Save file failure', 'Annotation file not saved:{}'.format(e)) else: saved_file_paths.append(xml_path) if saved_file_paths and not suppress_msg: sf_str = '\n'.join(saved_file_paths) info_dialog(self, 'Success', 'Annotation files saved:{}'.format(sf_str))
def load_impc_analysis(self, impc_zip_file): """ Load a zip file containing the results of the IMPC automated analysis (TCP pipeline) Parameters ---------- impc_zip_file: str path tho analysis results zip """ try: zf = zipfile.ZipFile(impc_zip_file) except zipfile.BadZipFile as e: common.error_dialog( self.mainwindow, 'Zip file error', 'Cannot read IMPC analysis zip file\nThe zip may be corrupted') return names = zf.namelist() file_names = addict.Dict({ 'intensity_tstats_file': None, 'jacobians_tstats_file': None, 'qvals_intensity_file': None, 'qvals_jacobians_file': None, 'popavg_file': None }) files_remaining = [] for name in names: name_lc = name.lower() if 'intensities-tstats' in name_lc: file_names.intensity_tstats_file = name elif 'jacobians-tstats' in name_lc: file_names.jacobians_tstats_file = name elif 'qvals-intensities' in name_lc: file_names.qvals_intensity_file = name elif 'qvals-jacobians' in name_lc: file_names.qvals_jacobians_file = name elif 'popavg' in name_lc: file_names.popavg_file = name else: files_remaining.append(name) if all(file_names.values()): td = tempfile.TemporaryDirectory() zf.extractall(td.name) popavg = join(td.name, file_names.popavg_file) self.load_volumes([popavg], 'vol') # get the trhesholds from the csv files qval_int_csv = join(td.name, file_names.qvals_intensity_file) intensity_fdr_thresh = self.extract_fdr_thresholds(qval_int_csv) inten_tstat = join(td.name, file_names.intensity_tstats_file) self.load_volumes([inten_tstat], 'heatmap', memory_map=False, fdr_thresholds=intensity_fdr_thresh) qval_jac_csv = join(td.name, file_names.qvals_jacobians_file) jacobian_fdr_thresh = self.extract_fdr_thresholds(qval_jac_csv) jac_tstat = join(td.name, file_names.jacobians_tstats_file) self.load_volumes([jac_tstat], 'heatmap', memory_map=False, fdr_thresholds=jacobian_fdr_thresh) # Load any other volumes in the zip. Probably will be mutants mutants = [ join(td.name, x) for x in files_remaining if x.endswith('nrrd') ] self.load_volumes(mutants, 'vol', memory_map=False) if not intensity_fdr_thresh: common.info_dialog( self.mainwindow, "No hits", "There are no hits in the intensity heatmap. The threshold is set to max" ) if not jacobian_fdr_thresh: common.info_dialog( self.mainwindow, "No hits", "There are no hits in the jacobian heatmap. The threshold is set to max" ) else: failed = [] for key, name in file_names.items(): if not name: failed.append(key) common.error_dialog( self.mainwindow, 'Error loading file', 'The following files could not be found in the zip\n {}'. format('\n'.join(failed))) print( 'IMPC analysis data failed to load. The following files could not be found in the zip' ) print(failed)
def _load_options_and_metadata(self): """ The volume has been loaded. Now see if there is an associated annotation folder that will contain the IMPC metadata parameter file. Also load in any partially completed xml annotation files Returns ------- """ if not self.annotation_dir: return self.metadata_parameter_file = join(self.annotation_dir, PROCEDURE_METADATA) if not isfile(self.metadata_parameter_file): self.metadata_parameter_file = None return cso = centre_stage_options.opts metadata_params = load_yaml(self.metadata_parameter_file) if not metadata_params: return # proc_id = metadata_params['procedure_id'] proc_id = metadata_params['procedure_id'] = ANNOTATIONS_PROC_VERSION center_id = metadata_params['centre_id'] stage_id = get_stage_from_proc_id(proc_id, center_id) self.stage = stage_id self.center = center_id # Get the procedure parameters for the given center/stage center_stage_default_params = cso['centers'][center_id]['stages'][stage_id]['parameters'] for _, param_info in center_stage_default_params.items(): try: options = centre_stage_options.opts['available_options'][param_info['options']] except TypeError as e: logging.error('Falied to load annotation parameter file {}'.format(e)) return if param_info['default_option'] not in options: logging.error( """Annotation parameter list load error default option: {} not in available options:{} Centre:, stage:{}""".format(param_info['default_option'], options, center_id, stage_id)) error_dialog(None, 'Annotation options load error', "See log file - 'info/show log'") return default = param_info['default_option'] self.add_impc_annotation(None, None, None, param_info['impc_id'], param_info['name'], options, default, self.stage, param_info['order'], param_info['mandatory'], self.dims ) # Sort the list and set the interator index self.annotations.sort(key=lambda x: x.order, reverse=True) self.index = len(self.annotations)
def load_impc_analysis(self, impc_zip_file): """ Load a zip file containing the results of the IMPC automated analysis (TCP pipeline) Parameters ---------- impc_zip_file: str path tho analysis results zip """ try: zf = zipfile.ZipFile(impc_zip_file) except zipfile.BadZipFile as e: common.error_dialog(self.mainwindow, 'Zip file error', 'Cannot read IMPC analysis zip file\nThe zip may be corrupted') return names = zf.namelist() file_names = addict.Dict( {'intensity_tstats_file': None, 'jacobians_tstats_file': None, 'qvals_intensity_file': None, 'qvals_jacobians_file': None, 'popavg_file': None} ) files_remaining = [] for name in names: name_lc = name.lower() if 'intensities-tstats' in name_lc: file_names.intensity_tstats_file = name elif 'jacobians-tstats' in name_lc: file_names.jacobians_tstats_file = name elif 'qvals-intensities' in name_lc: file_names.qvals_intensity_file = name elif 'qvals-jacobians' in name_lc: file_names.qvals_jacobians_file = name elif 'popavg' in name_lc: file_names.popavg_file = name else: files_remaining.append(name) if all(file_names.values()): td = tempfile.TemporaryDirectory() zf.extractall(td.name) popavg = join(td.name, file_names.popavg_file) self.load_volumes([popavg], 'vol') # get the trhesholds from the csv files qval_int_csv = join(td.name, file_names.qvals_intensity_file) intensity_fdr_thresh = self.extract_fdr_thresholds(qval_int_csv) inten_tstat = join(td.name, file_names.intensity_tstats_file) self.load_volumes([inten_tstat], 'heatmap', memory_map=False, fdr_thresholds=intensity_fdr_thresh) qval_jac_csv = join(td.name, file_names.qvals_jacobians_file) jacobian_fdr_thresh = self.extract_fdr_thresholds(qval_jac_csv) jac_tstat = join(td.name, file_names.jacobians_tstats_file) self.load_volumes([jac_tstat], 'heatmap', memory_map=False, fdr_thresholds=jacobian_fdr_thresh) # Load any other volumes in the zip. Probably will be mutants mutants = [join(td.name, x) for x in files_remaining if x.endswith('nrrd')] self.load_volumes(mutants, 'vol', memory_map=False) if not intensity_fdr_thresh: common.info_dialog(self.mainwindow, "No hits", "There are no hits in the intensity heatmap. The threshold is set to max") if not jacobian_fdr_thresh: common.info_dialog(self.mainwindow, "No hits", "There are no hits in the jacobian heatmap. The threshold is set to max") else: failed = [] for key, name in file_names.items(): if not name: failed.append(key) common.error_dialog(self.mainwindow, 'Error loading file', 'The following files could not be found in the zip\n {}'.format('\n'.join(failed))) print('IMPC analysis data failed to load. The following files could not be found in the zip') print(failed)
def load_annotation(self, ann_path): """ Load annotations from an IMPC xml file. Parameters ---------- ann_path: str path to xml annotation file Returns ------- """ # Load in data from xml try: centerID, pipeline, project, doe, ex_id, spec_id, proc_id, \ simple_and_series_params, procedure_metadata = load_xml(ann_path) except IOError as e: print("Cannot read xml file {}\n".format(ann_path, e)) error_dialog(None, 'File read error', "Problem reading annotaitons file\n{}".format(ann_path)) return # try to find a corresponding procedure_metadata.yaml file ann_dir = os.path.split(ann_path)[0] procedure_metadata_file = os.path.join(ann_dir, PROCEDURE_METADATA) if not os.path.isfile(procedure_metadata_file): vol = None # Should also check if annotation options have been loaded else: vol_id = os.path.basename(ann_dir) # The annotation directory is the same name as the annotated volume vol = self._volumes.get(vol_id) if vol: vol.annotations.clear() # Get the dict that contains the available options for a given center/stage default_opts = centre_stage_options.opts stage = get_stage_from_proc_id(proc_id, centerID) # Get all the simpleParameter entries form the xml file for xml_param, xml_data in simple_and_series_params.items(): option = xml_data['option'] xyz = xml_data.get('xyz') if xyz: x, y, z = [int(i) for i in xyz] else: x = y = z = None dims = vol.shape_xyz() # Some of the data needed to create an annotation object is not recorded in the XML output # So we need to load that from the center annotation options file for center, default_data in default_opts['centers'].items(): if default_data['short_name'] == centerID: params = default_data['stages'][stage]['parameters'] for param_id, default_param_info in params.items(): if param_id == xml_param: name = default_param_info['name'] options = default_opts['available_options'][ default_param_info['options']] # available options for this parameter order = default_param_info['order'] is_mandatory = default_param_info['mandatory'] vol.annotations.add_impc_annotation(x, y, z, xml_param, name, options, option, stage, order, is_mandatory, dims) vol.annotations._load_done_status() else: return "Could not load annotation: {}. Not able to find loaded volume with same id".format(vol_id) return None
def save_annotations(self, suppress_msg=False): """ Parameters ---------- suppress_msg: bool When doing autosave do not give a dialog informing of save (True) When doing manual save, give dialog with saved file path Returns ------- """ annotator_id = self.ui.lineEditAnnotatorId.text() if annotator_id.isspace() or not annotator_id: error_dialog(self, 'Experimenter ID missing', "An annotator ID is required") return saved_file_paths = [] for vol in self.controller.model.all_volumes(): points_for_series_media = [] # The first time an annotation is saved, get the date of annotation and set on volume # Next time it's loaded load the original date of annotation if vol.annotations.saved_xml_fname: xml_path = vol.annotations.saved_xml_fname date_of_annotation = vol.annotations.annotation_date else: date_of_annotation = self.ui.dateEdit.date().toString('yyyy-MM-dd') xml_path = None xml_exporter = impc_xml.ExportXML( date_of_annotation, annotator_id, vol.annotations.metadata_parameter_file) ann = vol.annotations for i, a in enumerate(ann.annotations): xml_exporter.add_parameter(a.term, a.selected_option) if all((a.x, a.y, a.z)): points_for_series_media.append(a) # save as need to add points after simpleParameters if not xml_path: xml_dir = vol.annotations.annotation_dir increment = get_increment(xml_dir) xml_file_name = "{}.{}.{}.experiment.impc.xml".format(vol.annotations.center, date_of_annotation, increment) xml_path = join(xml_dir, xml_file_name) # Add the series media parameter now we have SimpleParameters loaded, so we can add points to it # To get the reconstruction parameter just strip off digits from procedure and add _001_001 reconstruction_param = vol.annotations.proc_id[:8] + '_001_001' xml_exporter.add_series_media_parameter(reconstruction_param) for a in points_for_series_media: xml_exporter.add_point(a.term, (a.x, a.y, a.z), (a.x_percent, a.y_percent, a.z_percent)) # Now we can add metadata at the bottom xml_exporter.add_metadata() try: xml_exporter.write(xml_path) except OSError as e: error_dialog(self, 'Save file failure', 'Annotation file not saved:{}'.format(e)) else: saved_file_paths.append(xml_path) if saved_file_paths and not suppress_msg: sf_str = '\n'.join(saved_file_paths) info_dialog(self, 'Success', 'Annotation files saved:{}'.format(sf_str))
def _load_options_and_metadata(self): """ The volume has been loaded. Now see if there is an associated annotation folder that will contain the IMPC metadata parameter file. Also load in any partially completed xml annotation files. """ if not self.annotation_dir: return self.metadata_parameter_file = join(self.annotation_dir, PROCEDURE_METADATA) if not isfile(self.metadata_parameter_file): self.metadata_parameter_file = None return cso = centre_stage_options.opts metadata_params = load_yaml(self.metadata_parameter_file) if not metadata_params: return proc_id = metadata_params['procedure_id'] # Temp fix 030320 to increment procedures to 002 so they vallidate correctly proc_id = proc_id.replace('_001', '_002') self.proc_id = proc_id center_id = metadata_params['centre_id'] stage_id, modality = get_stage_and_modality(proc_id, center_id) self.stage = stage_id self.center = center_id self.modality = modality # Get the procedure parameters for the given center/stage try: cso['centers'][center_id]['procedures'][proc_id] except KeyError: logging.error(f'Procedure id {proc_id} not in the annotation_conf') return center_stage_default_params = cso['centers'][center_id]['procedures'][proc_id]['parameters'] # opts['centers'][center]['procedures'][proc_id]['parameters'] = self.load_centre_stage_file(param_file_) for _, param_info in center_stage_default_params.items(): try: options = centre_stage_options.opts['available_options'][param_info['options']] except TypeError as e: logging.error('Falied to load annotation parameter file {}'.format(e)) return if param_info['default_option'] not in options: logging.error( """Annotation parameter list load error default option: {} not in available options:{} Centre:, stage:{}""".format(param_info['default_option'], options, center_id, stage_id)) error_dialog(None, 'Annotation options load error', "See log file - 'info/show log'") return default = param_info['default_option'] self.add_impc_annotation(None, None, None, param_info['impc_id'], param_info['name'], options, default, self.stage, param_info['order'], param_info['mandatory'], self.dims ) # Sort the list and set the interator index self.annotations.sort(key=lambda x: x.order, reverse=True) self.index = len(self.annotations)
def load_annotation(self, ann_path): """ Load annotations from an IMPC xml file. Parameters ---------- ann_path: str path to xml annotation file Returns ------- """ # Load in data from xml try: centerID, pipeline, project, doe, ex_id, spec_id, proc_id, \ simple_and_series_params, procedure_metadata = load_xml(ann_path) except IOError as e: print("Cannot read xml file {}\n".format(ann_path, e)) error_dialog(None, 'File read error', "Problem reading annotaitons file\n{}".format(ann_path)) return # try to find a corresponding procedure_metadata.yaml file ann_dir = os.path.split(ann_path)[0] procedure_metadata_file = os.path.join(ann_dir, PROCEDURE_METADATA) if not os.path.isfile(procedure_metadata_file): vol = None # Should also check if annotation options have been loaded else: vol_id = os.path.basename(ann_dir) # The annotation directory is the same name as the annotated volume vol = self._volumes.get(vol_id) if not vol: return "Could not load annotation: {}. Not able to find loaded volume with same id".format(vol_id) vol.annotations.clear() # Get the dict that contains the available options for a given center/stage annotation_date_param_id = get_annotator_id_and_date(proc_id)[1] ann_date = [x[1] for x in procedure_metadata if x[0] == annotation_date_param_id] ann_date = ann_date[0] vol.annotations.annotation_date = ann_date default_opts = centre_stage_options.opts stage = get_stage_and_modality(proc_id, centerID) ###################################### # This all needs moving into Annotations # Set the xml file path which is where it will get resaved to vol.annotations.saved_xml_fname = ann_path # Get all the simpleParameter entries form the xml file for xml_param, xml_data in simple_and_series_params.items(): option = xml_data['option'] xyz = xml_data.get('xyz') if xyz: x, y, z = [int(i) for i in xyz] else: x = y = z = None dims = vol.shape_xyz() # Some of the data needed to create an annotation object is not recorded in the XML output # So we need to load that from the center annotation options file for center, default_data in default_opts['centers'].items(): if default_data['short_name'] == centerID: params = default_data['procedures'][proc_id]['parameters'] for param_id, default_param_info in params.items(): if param_id == xml_param: name = default_param_info['name'] options = default_opts['available_options'][ default_param_info['options']] # available options for this parameter order = default_param_info['order'] is_mandatory = default_param_info['mandatory'] vol.annotations.add_impc_annotation(x, y, z, xml_param, name, options, option, stage, order, is_mandatory, dims) vol.annotations._load_done_status()