def loadSavedProjects(self): """Load the savedProjects dictionary from the saved_projects.yml file. If the saved_projects.yml file is not existing, it is created with the "{paths: []}" value and the returned dictionnary is {paths: []}. :returns: the dictionary """ config = Config() try: with open( os.path.join(config.get_mia_path(), 'properties', 'saved_projects.yml'), 'r') as stream: try: if verCmp(yaml.__version__, '5.1', 'sup'): return yaml.load(stream, Loader=yaml.FullLoader) else: return yaml.load(stream) except yaml.YAMLError as exc: print(exc) except FileNotFoundError as exc: with open( os.path.join(config.get_mia_path(), 'properties', 'saved_projects.yml'), 'w') as stream: yaml.dump({'paths': []}, stream, default_flow_style=False) return {'paths': []}
def image2DModifications(self, idx, im2D=None): """Apply modifications to display the image correctly. :param idx: the selected index :param im2D: image to modify """ display_size = (128, 128) display_type = np.uint8 # this MUST be an integer data type display_pctl = 0.5 # percentile (0.5%) of values to clip at the low and high end of intensities. display_max = np.iinfo(display_type).max display_min = np.iinfo(display_type).min im2d_provided = im2D is not None if not im2d_provided: im2D = self.im_2D[idx] # Resize image first, for three reasons: # 1 - it may slightly changes the intensity scale, so re-scaling should be done after this # 2 - rescaling before rotation is slightly faster, specially for large images (> display_size). # 3 - rescaling may alter the occurrence of nan or infinite values (e.g. an image may become all-nan) # anti_aliasing keyword is defined in skimage since version 0.14.0 if verCmp(sk.__version__, '0.14.0', 'sup'): im2D = resize(im2D, display_size, mode='constant', anti_aliasing=False) else: im2D = resize(im2D, display_size, mode='constant') # Rescale image while handling Nans and infinite values im_mask = np.isfinite(im2D) if np.any(im_mask): # if we have any finite value to work with im2D -= np.percentile( im2D[im_mask], display_pctl) # shift the lower percentile chosen to 0.0 im_max = np.percentile( im2D[im_mask], 100.0 - display_pctl) # determine max from upper percentile if im_max > 0: # avoid dividing by zero im2D *= (display_max - display_min) / im_max # re-scale to display range im2D += display_min # shift lowest value to lower limit of display range np.clip( im2D, display_min, display_max, im2D) # clip all values to display range, remove infinite values im2D = im2D.astype( display_type ) # convert to integer display data type. NaNs get converted to 0. im2D = np.rot90(im2D, 3).copy( ) # Rotate. Copy array to avoid negative strides (Qt doesn't handle that) if im2d_provided: return im2D else: self.im_2D[idx] = im2D
def loadProperties(self): """Load the properties file.""" with open(os.path.join(self.folder, 'properties', 'properties.yml'), 'r') as stream: try: if verCmp(yaml.__version__, '5.1', 'sup'): return yaml.load(stream, Loader=yaml.FullLoader) else: return yaml.load(stream) except yaml.YAMLError as exc: print(exc)
def image2DModifications(self, idx, im2D=None): """Apply modifications to display the image correctly. :param idx: the selected index :param im2D: image to modify """ if im2D is not None: im2D = rotate(im2D, -90, reshape=False) im2D = np.uint8((im2D - im2D.min()) / im2D.ptp() * 255.0) # anti_aliasing keyword is defined in skimage since version 0.14.0 if verCmp(sk.__version__, '0.14.0', 'sup'): im2D = resize(im2D, (128, 128), mode='constant', anti_aliasing=False) else: im2D = resize(im2D, (128, 128), mode='constant') im2D = (im2D * 255).astype(np.uint8) return im2D else: self.im_2D[idx] = rotate(self.im_2D[idx], -90, reshape=False) self.im_2D[idx] = np.uint8( (self.im_2D[idx] - self.im_2D[idx].min()) / self.im_2D[idx].ptp() * 255.0) # anti_aliasing keyword is defined in skimage since version 0.14.0 if verCmp(sk.__version__, '0.14.0', 'sup'): self.im_2D[idx] = resize(self.im_2D[idx], (128, 128), mode='constant', anti_aliasing=False) else: self.im_2D[idx] = resize(self.im_2D[idx], (128, 128), mode='constant') self.im_2D[idx] = (self.im_2D[idx] * 255).astype(np.uint8)
def verify_processes(): """Install or update to the last version available on the station, of the \ nipype and the mia_processes processes libraries. By default, mia provides two process libraries in the pipeline library (available in Pipeline Manager tab). The nipype, given as it is because it is developed by another team (https://github.com/nipy/nipype), and mia_processes which is developed under the umbrella of populse (https://github.com/populse/mia_processes). When installing mia in user mode, these two libraries are automatically installed on the station. The idea is to use the versioning available with pypi (https://pypi.org/). Thus, it is sufficient for the user to change the version of the library installed on the station (pip install...) to also change the version available in mia. Indeed, when starting mia, the verify_processes function will install or update nipype and mia_processes libraries in the pipeline library. Currently it is mandatory to have nipype and may_processes installed in the station. All these informations, as well as the installed versions and package paths are saved in the mia_path/properties/process_config.yml file. When an upgrade or downgrade is performed for a package, the last configuration used by the user is kept (if a pipeline was visible, it remains so and vice versa). However, if a new pipeline is available in the new version it is automatically marked as visible in the library. :Contains: :Private function: - *_deepCompDic()* Try to keep the previous configuration existing before the update of the packages. Recursive comparison of the old_dic and new _dic dictionary. If all keys are recursively identical, the final value at the end of the whole tree in old_dic is kept in the new _dic. To sum up, this function is used to keep up the user display preferences in the processes library of the Pipeline Manager Editor. **param old_dic**: the dic representation of the previous package configuration **param new_dic**: the dic representation of the new package \ configuration **param level**: the index level in the package (0: root, +1: in a subpackage/pipeline) :returns: True if the current level is a pipeline that existed in the old configuration, False if the package/subpackage/pipeline did not exist """ def _deepCompDic(old_dic, new_dic, level="0"): PY3 = sys.version_info[0] == 3 if PY3: if isinstance(old_dic, str): return True else: if isinstance(old_dic, basestring): return True for key in old_dic: if key not in new_dic: if level == "0": pass elif level == "+1": return False # keep the same configuration for the pipeline in new and old dic elif (_deepCompDic(old_dic[str(key)], new_dic[str(key)], level="+1")): new_dic[str(key)] = old_dic[str(key)] proc_content = False nipypeVer = False miaProcVer = False pack2install = [] config = Config() proc_config = os.path.join(config.get_mia_path(), 'properties', 'process_config.yml') # check if nipype and mia_processes are available on the station # if not available inform the user to install them print('\nChecking the installed versions of nipype and mia_processes ...') print('***************************************************************') pkg_error = [] try: __import__('nipype') nipypeVer = sys.modules['nipype'].__version__ except ImportError as e: pkg_error.append('nipype') print('\n' + '*' * 37) print('MIA warning: {0}'.format(e)) print('*' * 37 + '\n') try: __import__('mia_processes') miaProcVer = sys.modules['mia_processes'].__version__ except ImportError as e: pkg_error.append('mia_processes') print('\n' + '*' * 37) print('MIA warning: {0}'.format(e)) print('*' * 37 + '\n') if len(pkg_error) > 0: app = QApplication(sys.argv) msg = QMessageBox() msg.setIcon(QMessageBox.Warning) msg.setWindowTitle("populse_mia - warning: ModuleNotFoundError!") if len(pkg_error) == 1: msg.setText("{0} package not found !\nPlease install " "the package and " "start again mia ...".format(pkg_error[0])) else: msg.setText("{0} and {1} packages not found !\n" "Please install the packages and start again mia " "...".format(pkg_error[0], pkg_error[1])) msg.setStandardButtons(QMessageBox.Ok) msg.buttonClicked.connect(msg.close) msg.exec() del app sys.exit(1) if os.path.isfile(proc_config): with open(proc_config, 'r') as stream: if verCmp(yaml.__version__, '5.1', 'sup'): proc_content = yaml.load(stream, Loader=yaml.FullLoader) else: proc_content = yaml.load(stream) if (isinstance(proc_content, dict)) and ('Packages' in proc_content): othPckg = [ f for f in proc_content['Packages'] if f not in ['mia_processes', 'nipype'] ] if 'othPckg' in dir(): # othPckg: a list containing all packages, other than nipype and # mia_processes, used during the previous launch of mia. for pckg in othPckg: try: __import__(pckg) except ImportError as e: # try to update the sys.path for the processes/ directory # currently used if (not os.path.relpath( os.path.join(config.get_mia_path(), 'processes')) in sys.path) and (not os.path.abspath( os.path.join(config.get_mia_path(), 'processes')) in sys.path): sys.path.append( os.path.abspath( os.path.join(config.get_mia_path(), 'processes'))) try: __import__(pckg) # update the Paths parameter (processes/ directory # currently used) saved later in the # mia_path/properties/process_config.yml file if (('Paths' in proc_content) and (isinstance(proc_content['Paths'], list))): if ((not os.path.relpath( os.path.join(config.get_mia_path(), 'processes')) in proc_content['Paths']) and (not os.path.abspath( os.path.join(config.get_mia_path(), 'processes')) in proc_content['Paths'])): proc_content['Paths'].append( os.path.abspath( os.path.join(config.get_mia_path(), 'processes'))) else: proc_content['Paths'] = [ os.path.abspath( os.path.join(config.get_mia_path(), 'processes')) ] with open(proc_config, 'w', encoding='utf8') as stream: yaml.dump(proc_content, stream, default_flow_style=False, allow_unicode=True) # finally the processes/ directory currently used is # removed from the sys.path because this directory is # now added to the Paths parameter in the # mia_path/properties/process_config.yml file sys.path.remove( os.path.abspath( os.path.join(config.get_mia_path(), 'processes'))) # if an exception is raised, ask to the user to remove the # package from the pipeline library or reload it except ImportError as e: print('{0}'.format(e)) app = QApplication(sys.argv) msg = QMessageBox() msg.setIcon(QMessageBox.Warning) msg.setWindowTitle( "populse_mia - warning: {0}".format(e)) msg.setText( ("At least, {0} has not been found in {1}." "\nTo prevent mia crash when using it, " "please remove (see File > Package " "library manager) or load again (see More" " > Install processes) the corresponding " "process library.").format( e.msg.split()[-1], os.path.abspath( os.path.join(config.get_mia_path(), 'processes')))) msg.setStandardButtons(QMessageBox.Ok) msg.buttonClicked.connect(msg.close) msg.exec() del app sys.path.remove( os.path.abspath( os.path.join(config.get_mia_path(), 'processes'))) # the processes/ directory being already in the sys.path, the # package is certainly not properly installed in the processes/ # directory else: print("No module named '{0}'".format(pckg)) app = QApplication(sys.argv) msg = QMessageBox() msg.setIcon(QMessageBox.Warning) msg.setWindowTitle("populse_mia - warning: {0}".format(e)) msg.setText(("At least, {0} has not been found in {1}." "\nTo prevent mia crash when using it, " "please remove (see File > Package " "library manager) or load again (see More" " > Install processes) the corresponding " "process library.").format( e.msg.split()[-1], os.path.abspath( os.path.join(config.get_mia_path(), 'processes')))) msg.setStandardButtons(QMessageBox.Ok) msg.buttonClicked.connect(msg.close) msg.exec() del app # the file mia_path/properties/process_config.yml is corrupted or # no pipeline processes was available during the previous use of mia or # their version is not known if (not isinstance(proc_content, dict)) or ( (isinstance(proc_content, dict)) and ('Packages' not in proc_content)) or ( (isinstance(proc_content, dict)) and ('Versions' not in proc_content)): pack2install = ['nipype.interfaces', 'mia_processes'] else: # during the previous use of mia, nipype was not available or its # version was not known or its version was different from the one # currently available on the station if ((isinstance(proc_content, dict)) and ('Packages' in proc_content) and ('nipype' not in proc_content['Packages'])) or ( (isinstance(proc_content, dict)) and ('Versions' in proc_content) and ('nipype' not in proc_content['Versions'])) or ( (isinstance(proc_content, dict)) and ('Versions' in proc_content) and ('nipype' in proc_content['Versions']) and (proc_content['Versions']['nipype'] != nipypeVer)): pack2install.append('nipype.interfaces') # during the previous use of mia, mia_processes was not available or # its version was not known or its version was different from the one # currently available on the station if ((isinstance(proc_content, dict)) and ('Packages' in proc_content) and ('mia_processes' not in proc_content['Packages'])) or ( (isinstance(proc_content, dict)) and ('Versions' in proc_content) and ('mia_processes' not in proc_content['Versions'])) or ( (isinstance(proc_content, dict)) and ('Versions' in proc_content) and ('mia_processes' in proc_content['Versions']) and (proc_content['Versions']['mia_processes'] != miaProcVer)): pack2install.append('mia_processes') final_pckgs = dict() # final_pckgs: the final dic of dic with the final_pckgs["Packages"] = {} # informations about the final_pckgs["Versions"] = {} # installed packages, their # version, and the path to access # them for pckg in pack2install: # pack2install: a list containing the package (nipype and/or # mia_processes) to install package = PackagesInstall() # pckg_dic: a dic of dic representation of a package and its # subpackages/modules Ex. {package: {subpackage: {pipeline: # 'process_enabled'}}} pckg_dic = package.add_package(pckg) for item in pckg_dic: final_pckgs["Packages"][item] = pckg_dic[item] if 'nipype' in pckg: # Save the packages version final_pckgs["Versions"]["nipype"] = nipypeVer print( '\n** Upgrading the {0} library processes to {1} version ...'. format(pckg, nipypeVer)) if 'mia_processes' in pckg: final_pckgs["Versions"]["mia_processes"] = miaProcVer print( '\n** Upgrading the {0} library processes to {1} version ...'. format(pckg, miaProcVer)) if pack2install: if not any("nipype" in s for s in pack2install): print( '\n** The nipype library processes in mia use already the ' 'installed version ', nipypeVer) elif not any("mia_processes" in s for s in pack2install): print( '\n** The mia_processes library in mia use already the ' 'installed version ', miaProcVer) if (isinstance(proc_content, dict)) and ('Paths' in proc_content): for item in proc_content['Paths']: # Save the path to the packages final_pckgs["Paths"] = proc_content['Paths'] if (isinstance(proc_content, dict)) and ('Versions' in proc_content): for item in proc_content['Versions']: if item not in final_pckgs['Versions']: final_pckgs['Versions'][item] = proc_content['Versions'][ item] # try to keep the previous configuration before the update # of the packages if (isinstance(proc_content, dict)) and ('Packages' in proc_content): _deepCompDic(proc_content['Packages'], final_pckgs['Packages']) for item in proc_content['Packages']: if item not in final_pckgs['Packages']: final_pckgs['Packages'][item] = proc_content['Packages'][ item] with open(proc_config, 'w', encoding='utf8') as stream: yaml.dump(final_pckgs, stream, default_flow_style=False, allow_unicode=True) else: print('\n- mia use already the installed version of nipype and ' 'mia_processes ({0} and {1} respectively)'.format( nipypeVer, miaProcVer))
def main(): """Make basic configuration check then actual launch of mia. Checks if MIA is called from the site/dist packages (user mode) or from a cloned git repository (developer mode). Tries to update the dev_mode parameter accordingly and the mia_path if necessary, in the ~/.populse_mia/configuration.yml file (this file must be available from the ~/.populse_mia directory). Launches the verify_processes() function, then the launch_mia() function (mia's real launch !!). When mia is exited, if the ~/.populse_mia/configuration.yml exists, sets the dev_mode parameter to False. - If launched from a cloned git repository ('developer mode'): - if the ~/.populse_mia/configuration.yml exists, updates the dev_mode parameter to True - if the ~/.populse_mia/configuration.yml is not existing nothing is done (in developer mode, the mia_path is the cloned git repository. - If launched from the site/dist packages ('user mode'): - if the ~/.populse_mia/configuration.yml exists, updates the dev_mode parameter to False and, if not found, update the mia_path parameter. - if the file ~/.populse_mia/configuration.yml file does not exist or if the returned mia_path parameter is incorrect, a valid mia_path path is requested from the user, in order to try to fix a corruption of this file. :Contains: :Private function: - *_browse_mia_path()* The user define the mia_path parameter. This method goes with the _ok_mia_path function, the latter will use the value of the mia_path parameter, defined here. :Parameters dialog: QtWidgets.QDialog object ('msg' in the main function) - *_ok_mia_path(dialog)* Check the mia_path parameter then if it is valid close the 'MIA path selection' window. This method goes with the _browse_mia_path function, the latter having allowed the definition of the mia_path parameter, the objective here is to check if the value of this parameter is valid. The dev_mode=False and mia_path parameters are saved in the, mandatory in user mode, ~/.populse_mia/configuration.yml file. Then the verify_processes function is used through a try/except blocks to check if the mia_path parameter value is valid. If an exception is raised during the verify_processes function, the "MIA path selection" window is not closed and the user is prompted again to set the mia_path parameter. :Parameters dialog: QtWidgets.QDialog object ('msg' in the main function) """ def _browse_mia_path(dialog): dname = QFileDialog.getExistingDirectory(dialog, "Please select MIA " "path") dialog.file_line_edit.setText(dname) def _ok_mia_path(dialog): mia_home_config = dict() mia_home_config["dev_mode"] = False mia_home_config["mia_path"] = dialog.file_line_edit.text() print('\nNew values in ~/.populse_mia/configuration.yml: ', mia_home_config) with open(dot_mia_config, 'w', encoding='utf8') as configfile: yaml.dump(mia_home_config, configfile, default_flow_style=False, allow_unicode=True) try: verify_processes() dialog.close() except Exception as e: print('\nCould not fetch the ' 'configuration file: {0} ...'.format(e)) msg = QMessageBox() msg.setIcon(QMessageBox.Warning) msg.setWindowTitle("populse_mia - Error: " "mia path directory incorrect") msg.setText("Error : Please select the MIA path (directory with\n " "the processes, properties & resources directories): ") msg.setStandardButtons(QMessageBox.Ok) msg.buttonClicked.connect(msg.close) msg.exec() dot_mia_config = os.path.join(os.path.expanduser("~"), ".populse_mia", "configuration.yml") if DEV_MODE: # "developer" mode if os.path.isfile(dot_mia_config): print('\n{0} has been detected.'.format(dot_mia_config)) with open(dot_mia_config, 'r') as stream: if verCmp(yaml.__version__, '5.1', 'sup'): mia_home_config = yaml.load(stream, Loader=yaml.FullLoader) else: mia_home_config = yaml.load(stream) mia_home_config["dev_mode"] = True with open(dot_mia_config, 'w', encoding='utf8') as configfile: yaml.dump(mia_home_config, configfile, default_flow_style=False, allow_unicode=True) else: print('\n{0} has not been detected.'.format(dot_mia_config)) verify_processes() else: # "user" mode try: if not os.path.exists(os.path.dirname(dot_mia_config)): os.mkdir(os.path.dirname(dot_mia_config)) print('\nThe {0} directory is created ...'.format( os.path.exists(os.path.dirname(dot_mia_config)))) with open(dot_mia_config, 'r') as stream: if verCmp(yaml.__version__, '5.1', 'sup'): mia_home_config = yaml.load(stream, Loader=yaml.FullLoader) else: mia_home_config = yaml.load(stream) mia_home_config["dev_mode"] = False with open(dot_mia_config, 'w', encoding='utf8') as configfile: yaml.dump(mia_home_config, configfile, default_flow_style=False, allow_unicode=True) verify_processes() except Exception as e: # the configuration.yml file does not exist # or has not been correctly read ... print( '\nA probleme has been detected when opening' ' the ~/.populse_mia/configuration.yml file' ' or with the parameters returned from this file: ', e) mia_home_config = dict() mia_home_config["dev_mode"] = False # open popup, user choose the path to .populse_mia/populse_mia app = QApplication(sys.argv) msg = QDialog() msg.setWindowTitle("populse_mia - mia path selection") vbox_layout = QVBoxLayout() hbox_layout = QHBoxLayout() file_label = QLabel("Please select the MIA path (directory with\n " "the processes, properties & resources " "directories): ") msg.file_line_edit = QLineEdit() msg.file_line_edit.setFixedWidth(400) file_button = QPushButton("Browse") file_button.clicked.connect(partial(_browse_mia_path, msg)) vbox_layout.addWidget(file_label) hbox_layout.addWidget(msg.file_line_edit) hbox_layout.addWidget(file_button) vbox_layout.addLayout(hbox_layout) hbox_layout = QHBoxLayout() msg.ok_button = QPushButton("Ok") msg.ok_button.clicked.connect(partial(_ok_mia_path, msg)) hbox_layout.addWidget(msg.ok_button) vbox_layout.addLayout(hbox_layout) msg.setLayout(vbox_layout) msg.exec() del app check_python_version() launch_mia() # set the dev_mode to False when exiting mia, # if ~/.populse_mia/configuration.yml file exists if os.path.isfile(dot_mia_config): with open(dot_mia_config, 'r') as stream: if verCmp(yaml.__version__, '5.1', 'sup'): mia_home_config = yaml.load(stream, Loader=yaml.FullLoader) else: mia_home_config = yaml.load(stream) mia_home_config["dev_mode"] = False with open(dot_mia_config, 'w', encoding='utf8') as configfile: yaml.dump(mia_home_config, configfile, default_flow_style=False, allow_unicode=True)