예제 #1
0
    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': []}
예제 #2
0
    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
예제 #3
0
    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)
예제 #4
0
    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)
예제 #5
0
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))
예제 #6
0
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)