def __init__(self): # Load test DICOM files desired_path = Path.cwd().joinpath('test', 'testdata') selected_files = find_DICOM_files(desired_path) # list of DICOM test files file_path = os.path.dirname(os.path.commonprefix(selected_files)) # file path of DICOM files read_data_dict, file_names_dict = ImageLoading.get_datasets(selected_files) # Create patient dict container object patient_dict_container = PatientDictContainer() patient_dict_container.clear() patient_dict_container.set_initial_values(file_path, read_data_dict, file_names_dict) # Set additional attributes in patient dict container (otherwise program will crash and test will fail) if "rtss" in file_names_dict: dataset_rtss = dcmread(file_names_dict['rtss']) self.rois = ImageLoading.get_roi_info(dataset_rtss) dict_raw_contour_data, dict_numpoints = ImageLoading.get_raw_contour_data(dataset_rtss) dict_pixluts = ImageLoading.get_pixluts(read_data_dict) patient_dict_container.set("rois", self.rois) patient_dict_container.set("raw_contour", dict_raw_contour_data) patient_dict_container.set("num_points", dict_numpoints) patient_dict_container.set("pixluts", dict_pixluts) # Open the main window self.main_window = MainWindow()
def create_new_rtstruct(cls, progress_callback): """ Generates a new RTSS and edits the patient dict container. Used for batch processing. """ # Get common directory patient_dict_container = PatientDictContainer() file_path = patient_dict_container.filepaths.values() file_path = Path(os.path.commonpath(file_path)) # Get new RT Struct file path file_path = str(file_path.joinpath("rtss.dcm")) # Create RT Struct file progress_callback.emit(("Generating RT Structure Set", 60)) ct_uid_list = ImageLoading.get_image_uid_list( patient_dict_container.dataset) ds = ROI.create_initial_rtss_from_ct(patient_dict_container.dataset[0], file_path, ct_uid_list) ds.save_as(file_path) # Add RT Struct file path to patient dict container patient_dict_container.filepaths['rtss'] = file_path filepaths = patient_dict_container.filepaths # Add RT Struct dataset to patient dict container patient_dict_container.dataset['rtss'] = ds dataset = patient_dict_container.dataset # Set some patient dict container attributes patient_dict_container.set("file_rtss", filepaths['rtss']) patient_dict_container.set("dataset_rtss", dataset['rtss']) dicom_tree_rtss = DicomTree(filepaths['rtss']) patient_dict_container.set("dict_dicom_tree_rtss", dicom_tree_rtss.dict) dict_pixluts = ImageLoading.get_pixluts(patient_dict_container.dataset) patient_dict_container.set("pixluts", dict_pixluts) rois = ImageLoading.get_roi_info(ds) patient_dict_container.set("rois", rois) patient_dict_container.set("selected_rois", []) patient_dict_container.set("dict_polygons_axial", {}) patient_dict_container.set("rtss_modified", True)
def __init__(self): # Load test DICOM files if platform.system() == "Windows": desired_path = "\\testdata\\DICOM-RT-TEST" elif platform.system() == "Linux" or platform.system() == "Darwin": desired_path = "/testdata/DICOM-RT-TEST" desired_path = os.path.dirname( os.path.realpath(__file__)) + desired_path selected_files = find_DICOM_files( desired_path) # list of DICOM test files file_path = os.path.dirname( os.path.commonprefix(selected_files)) # file path of DICOM files read_data_dict, file_names_dict = ImageLoading.get_datasets( selected_files) # Create patient dict container object patient_dict_container = PatientDictContainer() patient_dict_container.clear() patient_dict_container.set_initial_values(file_path, read_data_dict, file_names_dict) # Set additional attributes in patient dict container (otherwise program will crash and test will fail) if "rtss" in file_names_dict: dataset_rtss = dcmread(file_names_dict['rtss']) self.rois = ImageLoading.get_roi_info(dataset_rtss) dict_raw_contour_data, dict_numpoints = ImageLoading.get_raw_contour_data( dataset_rtss) dict_pixluts = ImageLoading.get_pixluts(read_data_dict) patient_dict_container.set("rois", self.rois) patient_dict_container.set("raw_contour", dict_raw_contour_data) patient_dict_container.set("num_points", dict_numpoints) patient_dict_container.set("pixluts", dict_pixluts) # Open the main window self.main_window = MainWindow() self.main_window.show() self.dicom_view = self.main_window.dicom_view self.new_polygons = {} slider_id = self.dicom_view.slider.value() self.curr_slice = self.dicom_view.patient_dict_container.get( "dict_uid")[slider_id]
def __init__(self): # Load test DICOM files desired_path = Path.cwd().joinpath('test', 'pet-testdata') # List of DICOM test files selected_files = find_DICOM_files(desired_path) # File path of DICOM files file_path = os.path.dirname(os.path.commonprefix(selected_files)) read_data_dict, file_names_dict = \ ImageLoading.get_datasets(selected_files) # Create patient dict container object self.patient_dict_container = PatientDictContainer() self.patient_dict_container.clear() self.patient_dict_container.set_initial_values \ (file_path, read_data_dict, file_names_dict) # Set additional attributes in patient dict container # (otherwise program will crash and test will fail) self.patient_dict_container.set("existing_rtss_files", []) if "rtss" in file_names_dict: dataset_rtss = dcmread(file_names_dict['rtss']) self.rois = ImageLoading.get_roi_info(dataset_rtss) dict_raw_contour_data, dict_numpoints = \ ImageLoading.get_raw_contour_data(dataset_rtss) dict_pixluts = ImageLoading.get_pixluts(read_data_dict) self.patient_dict_container.set("rois", self.rois) self.patient_dict_container.set("raw_contour", dict_raw_contour_data) self.patient_dict_container.set("num_points", dict_numpoints) self.patient_dict_container.set("pixluts", dict_pixluts) else: img_loader = ImageLoader(selected_files, None, None) img_loader.load_temp_rtss(file_path, DummyProgressWindow, DummyProgressWindow) # Open the main window self.main_window = MainWindow() # Get the initial structure and ROI count self.initial_structure_count = \ self.main_window.structures_tab.layout_content.count() self.initial_roi_count = len(self.main_window.structures_tab.rois)
def load_temp_rtss(self, path, progress_callback, interrupt_flag): """ Generate a temporary rtss and load its data into MovingDictContainer :param path: str. The common root folder of all DICOM files. :param progress_callback: A signal that receives the current progress of the loading. :param interrupt_flag: A threading.Event() object that tells the function to stop loading. """ progress_callback.emit(("Generating temporary rtss...", 20)) moving_dict_container = MovingDictContainer() rtss_path = Path(path).joinpath('rtss.dcm') uid_list = ImageLoading.get_image_uid_list( moving_dict_container.dataset) rtss = create_initial_rtss_from_ct(moving_dict_container.dataset[0], rtss_path, uid_list) if interrupt_flag.is_set(): # Stop loading. print("stopped") return False progress_callback.emit(("Loading temporary rtss...", 50)) # Set ROIs rois = ImageLoading.get_roi_info(rtss) moving_dict_container.set("rois", rois) # Set pixluts dict_pixluts = ImageLoading.get_pixluts(moving_dict_container.dataset) moving_dict_container.set("pixluts", dict_pixluts) # Add RT Struct file path and dataset to moving dict container moving_dict_container.filepaths['rtss'] = rtss_path moving_dict_container.dataset['rtss'] = rtss # Set some moving dict container attributes moving_dict_container.set("file_rtss", rtss_path) moving_dict_container.set("dataset_rtss", rtss) ordered_dict = DicomTree(None).dataset_to_dict(rtss) moving_dict_container.set("dict_dicom_tree_rtss", ordered_dict) moving_dict_container.set("selected_rois", [])
def __init__(self): # Load test DICOM files desired_path = Path.cwd().joinpath('test', 'testdata') # list of DICOM test files selected_files = find_DICOM_files(desired_path) # file path of DICOM files file_path = os.path.dirname(os.path.commonprefix(selected_files)) read_data_dict, file_names_dict = \ ImageLoading.get_datasets(selected_files) # Create patient dict container object self.patient_dict_container = PatientDictContainer() self.patient_dict_container.clear() self.patient_dict_container.set_initial_values\ (file_path, read_data_dict, file_names_dict) # Set additional attributes in patient dict container # (otherwise program will crash and test will fail) if "rtss" in file_names_dict: dataset_rtss = dcmread(file_names_dict['rtss']) self.rois = ImageLoading.get_roi_info(dataset_rtss) dict_raw_contour_data, dict_numpoints = \ ImageLoading.get_raw_contour_data(dataset_rtss) dict_pixluts = ImageLoading.get_pixluts(read_data_dict) self.patient_dict_container.set("rois", self.rois) self.patient_dict_container.set("raw_contour", dict_raw_contour_data) self.patient_dict_container.set("num_points", dict_numpoints) self.patient_dict_container.set("pixluts", dict_pixluts) # Set location of rtss file file_paths = self.patient_dict_container.filepaths self.patient_dict_container.set("file_rtss", file_paths['rtss']) # Create ISO2ROI object self.iso2roi = ISO2ROI()
def load(self, interrupt_flag, progress_callback): """ :param interrupt_flag: A threading.Event() object that tells the function to stop loading. :param progress_callback: A signal that receives the current progress of the loading. :return: PatientDictContainer object containing all values related to the loaded DICOM files. """ progress_callback.emit(("Creating datasets...", 0)) try: # Gets the common root folder. path = os.path.dirname(os.path.commonprefix(self.selected_files)) read_data_dict, file_names_dict = ImageLoading.get_datasets( self.selected_files) except ImageLoading.NotAllowedClassError: raise ImageLoading.NotAllowedClassError # Populate the initial values in the PatientDictContainer singleton. moving_dict_container = MovingDictContainer() moving_dict_container.clear() moving_dict_container.set_initial_values( path, read_data_dict, file_names_dict, existing_rtss_files=self.existing_rtss) if interrupt_flag.is_set(): print("stopped") return False if 'rtss' in file_names_dict and 'rtdose' in file_names_dict: self.parent_window.signal_advise_calc_dvh.connect( self.update_calc_dvh) self.signal_request_calc_dvh.emit() while not self.advised_calc_dvh: pass if 'rtss' in file_names_dict: dataset_rtss = dcmread(file_names_dict['rtss']) progress_callback.emit(("Getting ROI info...", 10)) rois = ImageLoading.get_roi_info(dataset_rtss) if interrupt_flag.is_set(): # Stop loading. print("stopped") return False progress_callback.emit(("Getting contour data...", 30)) dict_raw_contour_data, dict_numpoints = \ ImageLoading.get_raw_contour_data(dataset_rtss) # Determine which ROIs are one slice thick dict_thickness = ImageLoading.get_thickness_dict( dataset_rtss, read_data_dict) if interrupt_flag.is_set(): # Stop loading. print("stopped") return False progress_callback.emit(("Getting pixel LUTs...", 50)) dict_pixluts = ImageLoading.get_pixluts(read_data_dict) if interrupt_flag.is_set(): # Stop loading. print("stopped") return False # Add RTSS values to MovingDictContainer moving_dict_container.set("rois", rois) moving_dict_container.set("raw_contour", dict_raw_contour_data) moving_dict_container.set("num_points", dict_numpoints) moving_dict_container.set("pixluts", dict_pixluts) if 'rtdose' in file_names_dict and self.calc_dvh: dataset_rtdose = dcmread(file_names_dict['rtdose']) # Spawn-based platforms (i.e Windows and MacOS) have a large # overhead when creating a new process, which ends up making # multiprocessing on these platforms more expensive than linear # calculation. As such, multiprocessing is only available on # Linux until a better solution is found. fork_safe_platforms = ['Linux'] if platform.system() in fork_safe_platforms: progress_callback.emit(("Calculating DVHs...", 60)) raw_dvh = ImageLoading.multi_calc_dvh( dataset_rtss, dataset_rtdose, rois, dict_thickness) else: progress_callback.emit( ("Calculating DVHs... (This may take a while)", 60)) raw_dvh = ImageLoading.calc_dvhs(dataset_rtss, dataset_rtdose, rois, dict_thickness, interrupt_flag) if interrupt_flag.is_set(): # Stop loading. print("stopped") return False progress_callback.emit(("Converging to zero...", 80)) dvh_x_y = ImageLoading.converge_to_0_dvh(raw_dvh) if interrupt_flag.is_set(): # Stop loading. print("stopped") return False # Add DVH values to MovingDictContainer moving_dict_container.set("raw_dvh", raw_dvh) moving_dict_container.set("dvh_x_y", dvh_x_y) moving_dict_container.set("dvh_outdated", False) create_moving_model() else: create_moving_model() self.load_temp_rtss(path, progress_callback, interrupt_flag) progress_callback.emit(("Loading Moving Model", 85)) if interrupt_flag.is_set(): # Stop loading. progress_callback.emit(("Stopping", 85)) return False return True
def fixed_container_structure_modified(self, changes): """ Executes when a structure of fixed patient container is modified Displays indicator that structure has changed. Changes is a tuple of (new_dataset, description_of_changes) description_of_changes follows the format {"type_of_change": value_of_change}. Examples: {"rename": ["TOOTH", "TEETH"]} represents that the TOOTH structure has been renamed to TEETH. {"delete": ["TEETH", "MAXILLA"]} represents that the TEETH and MAXILLA structures have been deleted. {"draw": "AORTA"} represents that a new structure AORTA has been drawn. Note: Use {"draw": None} after multiple ROIs are generated (E.g., from ISO2ROI functionality), and use {"transfer":None} for ROI Transfer instead of calling this function multiple times. This will trigger auto save. """ new_dataset = changes[0] change_description = changes[1] # Only show the modified indicator if description_of_changes is # not {"draw": None}, as this description means that the RTSS # is autosaved, and therefore there is no need to tell the user # that the RTSS has been modified if not("draw" in change_description and change_description["draw"] is None) and \ not ("transfer" in change_description): self.show_modified_indicator() # If this is the first change made to the RTSS file, update the # dataset with the new one so that OnkoDICOM starts working off this # dataset rather than the original RTSS file. self.patient_dict_container.set("rtss_modified", True) self.patient_dict_container.set("dataset_rtss", new_dataset) # Refresh ROIs in main page self.patient_dict_container.set("rois", ImageLoading.get_roi_info(new_dataset)) self.rois = self.patient_dict_container.get("rois") contour_data = ImageLoading.get_raw_contour_data(new_dataset) self.patient_dict_container.set("raw_contour", contour_data[0]) self.patient_dict_container.set("num_points", contour_data[1]) pixluts = ImageLoading.get_pixluts(self.patient_dict_container.dataset) self.patient_dict_container.set("pixluts", pixluts) self.patient_dict_container.set( "list_roi_numbers", ordered_list_rois(self.patient_dict_container.get("rois"))) self.patient_dict_container.set("selected_rois", []) self.patient_dict_container.set("dict_polygons_axial", {}) self.patient_dict_container.set("dict_polygons_sagittal", {}) self.patient_dict_container.set("dict_polygons_coronal", {}) if "draw" in change_description or "transfer" in change_description: dicom_tree_rtss = DicomTree(None) dicom_tree_rtss.dataset = new_dataset dicom_tree_rtss.dict = dicom_tree_rtss.dataset_to_dict( dicom_tree_rtss.dataset) self.patient_dict_container.set("dict_dicom_tree_rtss", dicom_tree_rtss.dict) self.color_dict = self.init_color_roi(self.patient_dict_container) self.patient_dict_container.set("roi_color_dict", self.color_dict) if self.patient_dict_container.has_attribute("raw_dvh"): # DVH will be outdated once changes to it are made, and # recalculation will be required. self.patient_dict_container.set("dvh_outdated", True) if self.patient_dict_container.has_attribute("raw_dvh"): # Rename structures in DVH list if "rename" in change_description: new_raw_dvh = self.patient_dict_container.get("raw_dvh") for key, dvh in new_raw_dvh.items(): if dvh.name == change_description["rename"][0]: dvh.name = change_description["rename"][1] break self.patient_dict_container.set("raw_dvh", new_raw_dvh) dvh2rtdose(new_raw_dvh) # Remove structures from DVH list - the only visible effect of # this section is the exported DVH csv if "delete" in change_description: list_of_deleted = [] new_raw_dvh = self.patient_dict_container.get("raw_dvh") for key, dvh in new_raw_dvh.items(): if dvh.name in change_description["delete"]: list_of_deleted.append(key) for key in list_of_deleted: new_raw_dvh.pop(key) self.patient_dict_container.set("raw_dvh", new_raw_dvh) dvh2rtdose(new_raw_dvh) # Refresh ROIs in DVH tab and DICOM View self.request_update_structures.emit() # Refresh structure tab self.update_content() if "draw" in change_description and change_description["draw"] is None: self.save_new_rtss_to_fixed_image_set(auto=True) elif "transfer" in change_description \ and change_description["transfer"] is None: self.save_new_rtss_to_fixed_image_set(auto=True)
def load(self, interrupt_flag, progress_callback): """ :param interrupt_flag: A threading.Event() object that tells the function to stop loading. :param progress_callback: A signal that receives the current progress of the loading. :return: PatientDictContainer object containing all values related to the loaded DICOM files. """ progress_callback.emit(("Creating datasets...", 0)) try: path = os.path.dirname(os.path.commonprefix(self.selected_files)) # Gets the common root folder. read_data_dict, file_names_dict = ImageLoading.get_datasets(self.selected_files) except ImageLoading.NotAllowedClassError: raise ImageLoading.NotAllowedClassError # Populate the initial values in the PatientDictContainer singleton. patient_dict_container = PatientDictContainer() patient_dict_container.clear() patient_dict_container.set_initial_values(path, read_data_dict, file_names_dict) # As there is no way to interrupt a QRunnable, this method must check after every step whether or not the # interrupt flag has been set, in which case it will interrupt this method after the currently processing # function has finished running. It's not very pretty, and the thread will still run some functions for, in some # cases, up to a couple seconds after the close button on the Progress Window has been clicked, however it's # the best solution I could come up with. If you have a cleaner alternative, please make your contribution. if interrupt_flag.is_set(): print("stopped") return False if 'rtss' in file_names_dict and 'rtdose' in file_names_dict: self.parent_window.signal_advise_calc_dvh.connect(self.update_calc_dvh) self.signal_request_calc_dvh.emit() while not self.advised_calc_dvh: pass if 'rtss' in file_names_dict: dataset_rtss = dcmread(file_names_dict['rtss']) progress_callback.emit(("Getting ROI info...", 10)) rois = ImageLoading.get_roi_info(dataset_rtss) if interrupt_flag.is_set(): # Stop loading. print("stopped") return False progress_callback.emit(("Getting contour data...", 30)) dict_raw_contour_data, dict_numpoints = ImageLoading.get_raw_contour_data(dataset_rtss) # Determine which ROIs are one slice thick dict_thickness = ImageLoading.get_thickness_dict(dataset_rtss, read_data_dict) if interrupt_flag.is_set(): # Stop loading. print("stopped") return False progress_callback.emit(("Getting pixel LUTs...", 50)) dict_pixluts = ImageLoading.get_pixluts(read_data_dict) if interrupt_flag.is_set(): # Stop loading. print("stopped") return False # Add RTSS values to PatientDictContainer patient_dict_container.set("rois", rois) patient_dict_container.set("raw_contour", dict_raw_contour_data) patient_dict_container.set("num_points", dict_numpoints) patient_dict_container.set("pixluts", dict_pixluts) if 'rtdose' in file_names_dict and self.calc_dvh: dataset_rtdose = dcmread(file_names_dict['rtdose']) # Spawn-based platforms (i.e Windows and MacOS) have a large overhead when creating a new process, which # ends up making multiprocessing on these platforms more expensive than linear calculation. As such, # multiprocessing is only available on Linux until a better solution is found. fork_safe_platforms = ['Linux'] if platform.system() in fork_safe_platforms: progress_callback.emit(("Calculating DVHs...", 60)) raw_dvh = ImageLoading.multi_calc_dvh(dataset_rtss, dataset_rtdose, rois, dict_thickness) else: progress_callback.emit(("Calculating DVHs... (This may take a while)", 60)) raw_dvh = ImageLoading.calc_dvhs(dataset_rtss, dataset_rtdose, rois, dict_thickness, interrupt_flag) if interrupt_flag.is_set(): # Stop loading. print("stopped") return False progress_callback.emit(("Converging to zero...", 80)) dvh_x_y = ImageLoading.converge_to_0_dvh(raw_dvh) if interrupt_flag.is_set(): # Stop loading. print("stopped") return False # Add DVH values to PatientDictContainer patient_dict_container.set("raw_dvh", raw_dvh) patient_dict_container.set("dvh_x_y", dvh_x_y) patient_dict_container.set("dvh_outdated", False) return True else: return True return True
def load_images(cls, patient_files, required_classes): """ Loads required datasets for the selected patient. :param patient_files: dictionary of classes and patient files. :param required_classes: list of classes required for the selected/current process. :return: True if all required datasets found, false otherwise. """ files = [] found_classes = set() # Loop through each item in patient_files for key, value in patient_files.items(): # If the item is an allowed class if key in cls.allowed_classes: for i in range(len(value)): # Add item's files to the files list files.extend(value[i].get_files()) # Get the modality name modality_name = cls.allowed_classes.get(key).get('name') # If the modality name is not found_classes, add it if modality_name not in found_classes \ and modality_name in required_classes: found_classes.add(modality_name) # Get the difference between required classes and found classes class_diff = set(required_classes).difference(found_classes) # If the dataset is missing required files, pass on it if len(class_diff) > 0: print("Skipping dataset. Missing required file(s) {}".format( class_diff)) return False # Try to get the datasets from the selected files try: # Convert paths to a common file system representation for i, file in enumerate(files): files[i] = Path(file).as_posix() read_data_dict, file_names_dict = cls.get_datasets(files) path = os.path.dirname( os.path.commonprefix(list(file_names_dict.values()))) # Otherwise raise an exception (OnkoDICOM does not support the # selected file type) except ImageLoading.NotAllowedClassError: raise ImageLoading.NotAllowedClassError # Populate the initial values in the PatientDictContainer patient_dict_container = PatientDictContainer() patient_dict_container.clear() patient_dict_container.set_initial_values(path, read_data_dict, file_names_dict) # If an RT Struct is included, set relevant values in the # PatientDictContainer if 'rtss' in file_names_dict: dataset_rtss = dcmread(file_names_dict['rtss']) rois = ImageLoading.get_roi_info(dataset_rtss) dict_raw_contour_data, dict_numpoints = \ ImageLoading.get_raw_contour_data(dataset_rtss) dict_pixluts = ImageLoading.get_pixluts(read_data_dict) # Add RT Struct values to PatientDictContainer patient_dict_container.set("rois", rois) patient_dict_container.set("raw_contour", dict_raw_contour_data) patient_dict_container.set("num_points", dict_numpoints) patient_dict_container.set("pixluts", dict_pixluts) return True
def structure_modified(self, changes): """ Executes when a structure is renamed/deleted. Displays indicator that structure has changed. changes is a tuple of (new_dataset, description_of_changes) description_of_changes follows the format {"type_of_change": value_of_change}. Examples: {"rename": ["TOOTH", "TEETH"]} represents that the TOOTH structure has been renamed to TEETH. {"delete": ["TEETH", "MAXILLA"]} represents that the TEETH and MAXILLA structures have been deleted. {"draw": "AORTA"} represents that a new structure AORTA has been drawn. """ new_dataset = changes[0] change_description = changes[1] # If this is the first time the RTSS has been modified, create a modified indicator giving the user the option # to save their new file. if self.patient_dict_container.get("rtss_modified") is False: self.show_modified_indicator() # If this is the first change made to the RTSS file, update the dataset with the new one so that OnkoDICOM # starts working off this dataset rather than the original RTSS file. self.patient_dict_container.set("rtss_modified", True) self.patient_dict_container.set("dataset_rtss", new_dataset) # Refresh ROIs in main page self.patient_dict_container.set("rois", ImageLoading.get_roi_info(new_dataset)) self.rois = self.patient_dict_container.get("rois") contour_data = ImageLoading.get_raw_contour_data(new_dataset) self.patient_dict_container.set("raw_contour", contour_data[0]) self.patient_dict_container.set("num_points", contour_data[1]) pixluts = ImageLoading.get_pixluts(self.patient_dict_container.dataset) self.patient_dict_container.set("pixluts", pixluts) self.patient_dict_container.set( "list_roi_numbers", ordered_list_rois(self.patient_dict_container.get("rois"))) self.patient_dict_container.set("selected_rois", []) self.patient_dict_container.set("dict_polygons", {}) if "draw" in change_description: dicom_tree_rtss = DicomTree(None) dicom_tree_rtss.dataset = new_dataset dicom_tree_rtss.dict = dicom_tree_rtss.dataset_to_dict( dicom_tree_rtss.dataset) self.patient_dict_container.set("dict_dicom_tree_rtss", dicom_tree_rtss.dict) self.color_dict = self.init_color_roi() self.patient_dict_container.set("roi_color_dict", self.color_dict) if self.patient_dict_container.has_attribute("raw_dvh"): # DVH will be outdated once changes to it are made, and recalculation will be required. self.patient_dict_container.set("dvh_outdated", True) if self.patient_dict_container.has_modality("raw_dvh"): # Rename structures in DVH list if "rename" in changes[1]: new_raw_dvh = self.patient_dict_container.get("raw_dvh") for key, dvh in new_raw_dvh.items(): if dvh.name == change_description["rename"][0]: dvh.name = change_description["rename"][1] break self.patient_dict_container.set("raw_dvh", new_raw_dvh) # Remove structures from DVH list - the only visible effect of this section is the exported DVH csv if "delete" in changes[1]: list_of_deleted = [] new_raw_dvh = self.patient_dict_container.get("raw_dvh") for key, dvh in new_raw_dvh.items(): if dvh.name in change_description["delete"]: list_of_deleted.append(key) for key in list_of_deleted: new_raw_dvh.pop(key) self.patient_dict_container.set("raw_dvh", new_raw_dvh) # Refresh ROIs in DVH tab and DICOM View self.request_update_structures.emit() # Refresh structure tab self.update_content()