Esempio n. 1
0
def installLookupTable(key):
    """Attempts to install/save a previously registered lookup table into
    the ``[settingsbase]/luts`` directory.
    """

    # keyerror if not registered
    lut = _luts[key]
    destFile = op.join('luts', '{}.lut'.format(key))
    destFile = fslsettings.filePath(destFile)
    destDir = op.dirname(destFile)

    log.debug('Installing lookup table {} to {}'.format(key, destFile))

    if not op.exists(destDir):
        os.makedirs(destDir)

    lut.mapObj.save(destFile)

    # Update user-added settings
    lutNames = fslsettings.read('fsleyes.luts', OrderedDict())
    lutNames[key] = lut.name
    fslsettings.write('fsleyes.luts', lutNames)

    lut.mapFile = destFile
    lut.installed = True
Esempio n. 2
0
    def realOnLoad(*a):

        if saveDir and len(paths) > 0:
            fslsettings.write('loadSaveOverlayDir', op.dirname(paths[-1]))

        if onLoad is not None:
            onLoad(pathIdxs, overlays)
Esempio n. 3
0
def fslDirWarning(parent):
    """Checks to see if the ``$FSLDIR`` environment variable is set, or
    if a FSL installation directory has been saved previously. If not,
    displays a warning via a :class:`.FSLDirDialog`.

    :arg parent: A ``wx`` parent object.
    """

    if fslplatform.fsldir is not None:
        return

    import fsl.utils.settings as fslsettings

    # Check settings before
    # prompting the user
    fsldir = fslsettings.read('fsldir')

    if fsldir is not None:
        fslplatform.fsldir = fsldir
        return

    from fsleyes_widgets.dialog import FSLDirDialog

    dlg = FSLDirDialog(parent, 'FSLeyes', fslplatform.os == 'Darwin')

    if dlg.ShowModal() == wx.ID_OK:

        fsldir = dlg.GetFSLDir()

        log.debug('Setting $FSLDIR to {} (specified '
                  'by user)'.format(fsldir))

        fslplatform.fsldir = fsldir
        fslsettings.write('fsldir', fsldir)
Esempio n. 4
0
def test_initialise():

    # Assuming that initialise()
    # has not yet been called
    assert settings.read('nothing') is None
    assert settings.read('nothing', 'default') == 'default'
    settings.write('nothing', 'nothing')
    settings.delete('nothing')
    assert settings.readFile('nothing') is None
    settings.writeFile('nothing', 'nothing')
    settings.deleteFile('nothing')
    assert settings.filePath() is None
    assert settings.readAll() == {}
    assert settings.listFiles() == []
    settings.clear()

    with tests.testdir() as testdir:

        settings.initialise(cfgid='test', cfgdir=testdir, writeOnExit=False)

        assert settings.settings.configID == 'test'
        assert settings.settings.configDir == testdir

        settings.write('setting', 'value')

        assert settings.read('setting') == 'value'
        assert settings.read('nothing') is None
Esempio n. 5
0
    def __onOk(self, ev):
        """Called when the *Ok* button is pushed. Prompts the user to select a
        directory, and then downloads the files.
        """

        files = self.__panel.GetSelectedFiles()

        if len(files) == 0:
            self.EndModal(wx.ID_OK)

        destdir = fslsettings.read('fsleyes.xnat.downloaddir', os.getcwd())
        dlg = wx.DirDialog(self,
                           strings.labels[self, 'choosedir'],
                           defaultPath=destdir)

        if dlg.ShowModal() != wx.ID_OK:
            return

        destdir = dlg.GetPath()

        for f in files:
            dest = self.__panel.DownloadFile(f, op.join(destdir, f.id))

            if dest is not None:
                self.__paths.append(dest)

        fslsettings.write('fsleyes.xnat.downloaddir', destdir)

        self.__panel.EndSession()
        self.EndModal(wx.ID_OK)
Esempio n. 6
0
def installColourMap(key):
    """Attempts to install a previously registered colourmap into the
    ``[settingsbase]/colourmaps/`` directory.
    """

    # keyerror if not registered
    cmap = _cmaps[key]

    # TODO I think the colors attribute is only
    #      available on ListedColormap instances,
    #      so if you ever start using different
    #      mpl types, you might need to revisit
    #      this.
    data = cmap.mapObj.colors

    destFile = op.join('colourmaps', '{}.cmap'.format(key))

    log.debug('Installing colour map {} to {}'.format(key, destFile))

    # Numpy under python 3 will break if
    # we give it a file opened with mode='wt'.
    with fslsettings.writeFile(destFile, mode='b') as f:
        np.savetxt(f, data, '%0.6f')

    # Update user-added settings
    cmapNames = fslsettings.read('fsleyes.colourmaps', OrderedDict())
    cmapNames[key] = cmap.name

    fslsettings.write('fsleyes.colourmaps', cmapNames)

    cmap.installed = True
Esempio n. 7
0
def setFSLDIR(self, *args, **kwargs):
    """Opens a directory dialog allowing the user to select a value for
    ``$FSLDIR``.
    """

    import wx
    from fsl.utils.platform import platform
    import fsl.utils.settings as settings

    fsldir = platform.fsldir

    if fsldir is None:
        fsldir = os.getcwd()

    msg = strings.titles[self, 'setFSLDIR']

    dlg = wx.DirDialog(self,
                       message=msg,
                       defaultPath=fsldir,
                       style=wx.DD_DEFAULT_STYLE | wx.DD_DIR_MUST_EXIST)

    if dlg.ShowModal() != wx.ID_OK:
        return

    fsldir = dlg.GetPath()
    platform.fsldir = fsldir
    settings.write('fsldir', fsldir)
Esempio n. 8
0
    def __doExport(self):

        import wx

        # Ask the user if they want to save the
        # x axis values as the first column
        dlg = wx.MessageDialog(
            wx.GetTopLevelWindows()[0],
            message=strings.messages[self, 'saveXColumn'],
            caption=strings.titles[  self, 'saveXColumn'],
            style=(wx.ICON_QUESTION |
                   wx.YES_NO        |
                   wx.CANCEL        |
                   wx.YES_DEFAULT))

        result = dlg.ShowModal()

        if result == wx.ID_CANCEL:
            return

        savex = result == wx.ID_YES

        # Ask the user where they want to save the data
        msg     = strings.messages[self, 'selectFile']
        fromDir = fslsettings.read('loadSaveOverlayDir', os.getcwd())
        dlg     = wx.FileDialog(wx.GetApp().GetTopWindow(),
                                message=msg,
                                defaultDir=fromDir,
                                defaultFile='dataseries.txt',
                                style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)

        if dlg.ShowModal() != wx.ID_OK:
            return

        filePath = dlg.GetPath()

        dss = self.__plotPanel.getDrawnDataSeries()
        xs  = [ds[1] for ds in dss]
        ys  = [ds[2] for ds in dss]
        dss = [ds[0] for ds in dss]

        # Create some x data, unified
        # across all data series
        xdata = np.unique(np.concatenate(xs))

        # Linearly interpolate each data series
        # according to the merged x data
        ydata = [np.interp(xdata, x, y, left=np.nan, right=np.nan)
                 for x, y in zip(xs, ys)]

        # Turn it all into one big
        # array and save it out
        if savex: data = np.vstack([xdata] + ydata)
        else:     data = np.vstack( ydata)

        np.savetxt(filePath, data.T, fmt='% 0.8f')

        fslsettings.write('loadSaveOverlayDir', filePath)
Esempio n. 9
0
    def __doAction(self, script=None):
        """Called when this :class:`.Action` is invoked. If the ``script``
        argument is ``None``, the user is prompted  to select a script file.
        The script is then compiled and executed.
        """

        import wx

        if script is None:

            lastDir = fslsettings.read('runScriptLastDir')

            if lastDir is None:
                lastDir = os.getcwd()

            msg = strings.messages[self, 'runScript']

            # Ask the user what script
            # they want to run
            dlg    = wx.FileDialog(self.__frame,
                                   message=msg,
                                   defaultDir=lastDir,
                                   wildcard='*.py',
                                   style=wx.FD_OPEN)

            if dlg.ShowModal() != wx.ID_OK:
                return

            script = dlg.GetPath()

            # Save the script directory for the
            # next time the user is prompted
            fslsettings.write('runScriptLastDir', op.dirname(script))

        # Run the script, show an
        # error if it crashes
        try:
            runScript(self.__frame,
                      self.__overlayList,
                      self.__displayCtx,
                      script)

        except Exception as e:
            log.warning('Script ({}) could not be executed: {}'.format(
                        script,
                        str(e)),
                        exc_info=True)

            msg = strings.messages[self, 'crash'].format(
                script,
                str(e))

            wx.MessageDialog(self.__frame,
                             message=msg,
                             style=wx.OK | wx.ICON_ERROR).ShowModal()

            return
Esempio n. 10
0
def _addToPerspectivesList(persp):
    """Adds the given perspective name to the list of saved perspectives. """

    persp = persp.strip()
    perspectives = getAllPerspectives()

    if persp not in perspectives:
        perspectives.append(persp)

    log.debug('Updating stored perspective list: {}'.format(perspectives))
    fslsettings.write('fsleyes.perspectives', perspectives)
Esempio n. 11
0
def _addToLayoutList(layout):
    """Adds the given layout name to the list of saved layouts. """

    layout = layout.strip()
    layouts = getAllLayouts()

    if layout not in layouts:
        layouts.append(layout)

    log.debug('Updating stored layout list: {}'.format(layout))
    fslsettings.write('fsleyes.layouts', layouts)
Esempio n. 12
0
    def __onLoadLut(self, ev):
        """Called when the user presses the *Load LUT* button.  Does the
        following:

          - Prompts the user to select a LUT file with a ``wx.FileDialog``

          - Prompts the user to enter a name for the LUT via the
            :func:`promptForLutName` function.

          - Creates and registers a new :class:`.LookupTable` instance,
            initialising it with the selected file.

          - Updates this ``LookupTablePanel`` via the
            :meth:`__updateLutChoices` and :meth:`__setLut` methods.
        """

        parent = wx.GetApp().GetTopWindow()
        loadDir = fslsettings.read('fsleyes.loadlutdir', os.getcwd())

        # Prompt the user to select a lut file
        fileDlg = wx.FileDialog(parent,
                                defaultDir=loadDir,
                                message=strings.titles[self, 'loadLut'],
                                style=wx.FD_OPEN)

        if fileDlg.ShowModal() != wx.ID_OK:
            return

        lutFile = fileDlg.GetPath()
        lutDir = op.dirname(lutFile)
        lutName = op.splitext(op.basename(lutFile))[0]

        # Prompt the user to enter a name
        lutKey, lutName = promptForLutName(lutName)
        if lutKey is None:
            return

        # Register the lut
        errTitle = strings.titles[self, 'loadError']
        errMsg = strings.messages[self, 'loadError'].format(lutFile)
        with status.reportIfError(errTitle, errMsg):
            lut = fslcmaps.registerLookupTable(lutFile,
                                               self.overlayList,
                                               self.displayCtx,
                                               key=lutKey,
                                               name=lutName)

        # Save the directory for next time
        fslsettings.write('fsleyes.loadlutdir', lutDir)

        # Select the lut in the panel
        self.__updateLutChoices()
        self.__setLut(lut)
Esempio n. 13
0
def _removeFromPerspectivesList(persp):
    """Removes the given perspective name from the list of saved perspectives.
    """

    perspectives = getAllPerspectives()

    try:
        perspectives.remove(persp)
    except ValueError:
        return

    log.debug('Updating stored perspective list: {}'.format(perspectives))
    fslsettings.write('fsleyes.perspectives', perspectives)
Esempio n. 14
0
def _removeFromLayoutList(layout):
    """Removes the given layout name from the list of saved layouts.
    """

    layouts = getAllLayouts()

    try:
        layouts.remove(layout)
    except ValueError:
        return

    log.debug('Updating stored layout list: {}'.format(layouts))
    fslsettings.write('fsleyes.layouts', layouts)
Esempio n. 15
0
    def __openBrowser(self):
        """Opens a :class:`XNATBrowser`, then adds any files that the user
        selected to the :class:`.OverlayList`.
        """

        hosts = fslsettings.read('fsleyes.xnat.hosts', [])
        accounts = fslsettings.read('fsleyes.xnat.accounts', {})

        dlg = XNATBrowser(self.__frame, hosts, accounts)

        dlg.Layout()
        dlg.Fit()
        dlg.SetSize((-1, 400))
        dlg.CentreOnParent()

        if dlg.ShowModal() != wx.ID_OK:
            return

        paths = dlg.GetPaths()
        hosts = dlg.GetHosts()
        accounts = dlg.GetAccounts()

        # No files downloaded
        if len(paths) == 0:
            return

        # Save successful hosts/credentials
        fslsettings.write('fsleyes.xnat.hosts', hosts)
        fslsettings.write('fsleyes.xnat.accounts', accounts)

        def onLoad(paths, overlays):

            if len(overlays) == 0:
                return

            self.__overlayList.extend(overlays)

            self.__displayCtx.selectedOverlay = \
                self.__displayCtx.overlayOrder[-1]

            if self.__displayCtx.autoDisplay:
                for overlay in overlays:
                    autodisplay.autoDisplay(overlay, self.__overlayList,
                                            self.__displayCtx)

        loadoverlay.loadOverlays(paths,
                                 onLoad=onLoad,
                                 saveDir=False,
                                 inmem=self.__displayCtx.loadInMemory)
Esempio n. 16
0
    def __loadPlugin(self, *args, **kwargs):
        """Prompts the user to select a plugin file, asks them whether they
        would like to install it permanently, and then passes it to either
        :func:`.loadPlugin` or :func:`.installPlugin`.
        """

        lastDir = fslsettings.read('loadPluginLastDir')

        if lastDir is None:
            lastDir = os.getcwd()

        msg = strings.messages[self, 'loadPlugin']
        dlg = wx.FileDialog(self.__frame,
                            message=msg,
                            defaultDir=lastDir,
                            wildcard='*.py',
                            style=wx.FD_OPEN)

        if dlg.ShowModal() != wx.ID_OK:
            return

        fname = dlg.GetPath()

        fslsettings.write('loadPluginLastDir', op.dirname(fname))

        dlg = wx.MessageDialog(self.__frame,
                               caption=strings.titles[self, 'installPlugin'],
                               message=strings.messages[self, 'installPlugin'],
                               style=wx.YES_NO | wx.CANCEL)

        result = dlg.ShowModal()

        if result == wx.ID_YES:
            etitle = strings.titles[self, 'installError']
            emsg = strings.messages[self, 'installError']
            func = plugins.installPlugin
        elif result == wx.ID_NO:
            etitle = strings.titles[self, 'loadError']
            emsg = strings.messages[self, 'loadError']
            func = plugins.loadPlugin
        else:
            return

        with status.reportIfError(title=etitle, msg=emsg, raiseError=False):
            func(fname)

        self.__frame.refreshViewMenu()
        self.__frame.refreshToolsMenu()
        self.__frame.refreshSettingsMenu()
Esempio n. 17
0
def saveLayout(frame, name):
    """Serialises the layout of the given :class:`.FSLeyesFrame` and saves
    it as a layout with the given name.
    """

    if name in BUILT_IN_LAYOUTS.keys():
        raise ValueError('A built-in layout named "{}" '
                         'already exists'.format(name))

    log.debug('Saving current layout with name {}'.format(name))

    layout = serialiseLayout(frame)
    fslsettings.write('fsleyes.layouts.{}'.format(name), layout)

    _addToLayoutList(name)

    log.debug('Serialised layout:\n{}'.format(layout))
Esempio n. 18
0
def savePerspective(frame, name):
    """Serialises the layout of the given :class:`.FSLeyesFrame` and saves
    it as a perspective with the given name.
    """

    if name in BUILT_IN_PERSPECTIVES.keys():
        raise ValueError('A built-in perspective named "{}" '
                         'already exists'.format(name))

    log.debug('Saving current perspective with name {}'.format(name))

    persp = serialisePerspective(frame)
    fslsettings.write('fsleyes.perspectives.{}'.format(name), persp)

    _addToPerspectivesList(name)

    log.debug('Serialised perspective:\n{}'.format(persp))
Esempio n. 19
0
    def __saveKnownAtlases(self):
        """Saves the IDs and paths of all atlases which are currently in
        the registry. The atlases are saved via the :mod:`.settings` module.
        """

        if self.__atlasDescs is None:
            return

        atlases = []

        for desc in self.__atlasDescs:
            atlases.append((desc.atlasID, desc.specPath))

        atlases = ['{}={}'.format(name, path) for name, path in atlases]
        atlases = op.pathsep.join(atlases)

        fslsettings.write('fsl.data.atlases', atlases)
Esempio n. 20
0
    def recordPath(self, path):
        """Adds the given ``path`` to the recent files list. """

        recent = self.listRecentPaths()

        if path in recent:
            return

        recent.append(path)

        if len(recent) > 10:
            recent = recent[-10:]

        recent = op.pathsep.join(recent)

        fslsettings.write('fsleyes.recentFiles', recent)

        self.notify()
Esempio n. 21
0
    def postLoad(completed):

        # Did the user cancel the progress dialog?
        if not completed:
            return

        # Did an error occur in the load step above?
        if isinstance(images[0], Exception):
            errTitle = strings.titles['loadDicom.loadError']
            errMsg = strings.messages['loadDicom.loadError']
            status.reportError(errTitle, errMsg, images[0])
            return

        fslsettings.write('loadSaveOverlayDir',
                          op.dirname(dcmdir.rstrip(op.sep)))

        if callback is not None:
            callback(images)
Esempio n. 22
0
    def _onLoadDir(self, ev=None):
        """Called when the user pushes the *load data directory* button.

        Prompts the user to select a directory, then calls the
        :meth:`__loadTree` method.
        """

        msg = strings.messages[self, 'loadDir']
        fromDir = fslsettings.read('loadSaveOverlayDir')

        dlg = wx.DirDialog(wx.GetApp().GetTopWindow(),
                           message=msg,
                           defaultPath=fromDir,
                           style=wx.DD_DEFAULT_STYLE | wx.DD_DIR_MUST_EXIST)

        if dlg.ShowModal() != wx.ID_OK:
            return

        dirname = dlg.GetPath()
        treename = self._getTreeChoice()

        fslsettings.write('loadSaveOverlayDir', dirname)

        self._loadTree(treename, dirname)
Esempio n. 23
0
    def __onLoadButton(self, ev):
        """Called when the *Load labels* button is pushed.  Prompts the user
        to select a label file to load, then does the following:

        1. If the selected label file refers to the currently selected
           melodic_IC overlay, the labels are applied to the overlay.

        2. If the selected label file refers to a different melodic_IC
           overlay, the user is asked whether they want to load the
           different melodic_IC file (the default), or whether they
           want the labels applied to the existing overlay.

        3. If the selected label file does not refer to any overlay
           (it only contains the bad component list), the user is asked
           whether they want the labels applied to the current melodic_IC
           overlay.

        If the number of labels in the file is less than the number of
        melodic_IC components, the remaining components are labelled
        as unknown. If the number of labels in the file is greater than
        the number of melodic_IC components, an error is shown, and
        nothing is done.
        """

        # The aim of the code beneath the
        # applyLabels function is to load
        # a set of component labels, and
        # to figure out which overlay
        # they should be added to.

        # When it has done this, it calls
        # applyLabels, which applies the
        # loaded labels to the overlay.
        def applyLabels(labelFile, overlay, allLabels, newOverlay):
            # labelFile:  Path to the loaded label file
            # overlay:    Overlay to apply them to
            # allLabels:  Loaded labels (list of (component, [label]) tuples)
            # newOverlay: True if the selected overlay has changed, False
            #             otherwise

            lut       = self.__lut
            volLabels = self.overlayList.getData(overlay, 'VolumeLabels')

            ncomps  = volLabels.numComponents()
            nlabels = len(allLabels)

            # Error: number of labels in the
            # file is greater than the number
            # of components in the overlay.
            if ncomps < nlabels:
                msg   = strings.messages[self, 'wrongNComps'].format(
                    labelFile, overlay.dataSource)
                title = strings.titles[  self, 'loadError']
                wx.MessageBox(msg, title, wx.ICON_ERROR | wx.OK)
                return

            # Number of labels in the file is
            # less than number of components
            # in the overlay - we pad the
            # labels with 'Unknown'
            elif ncomps > nlabels:
                for i in range(nlabels, ncomps):
                    allLabels.append(['Unknown'])

            # Disable notification while applying
            # labels so the component/label grids
            # don't confuse themselves.
            with volLabels.skip(self.__componentGrid.name), \
                 volLabels.skip(self.__labelGrid    .name):

                volLabels.clear()

                for comp, lbls in enumerate(allLabels):
                    for lbl in lbls:
                        volLabels.addLabel(comp, lbl)

                # Make sure a colour in the melodic
                # lookup table exists for all labels
                for label in volLabels.getAllLabels():

                    label    = volLabels.getDisplayLabel(label)
                    lutLabel = lut.getByName(label)

                    if lutLabel is None:
                        log.debug('New melodic classification '
                                  'label: {}'.format(label))
                        lut.new(label, colour=fslcm.randomBrightColour())

            # New overlay was loaded
            if newOverlay:

                # Make sure the new image is selected.
                with props.skip(self.displayCtx,
                                'selectedOverlay',
                                self.name):
                    self.displayCtx.selectOverlay(overlay)

                self.__componentGrid.setOverlay(overlay)
                self.__labelGrid    .setOverlay(overlay)

            # Labels were applied to
            # already selected overlay.
            else:
                self.__componentGrid.refreshTags()
                self.__labelGrid    .refreshTags()

        # If the current overlay is a compatible
        # Image, the open file dialog starting
        # point will be its directory.
        overlay = self.__overlay

        if overlay is not None and overlay.dataSource is not None:
            loadDir = op.dirname(overlay.dataSource)

        # Otherwise it will be the most
        # recent overlay load directory.
        else:
            loadDir = fslsettings.read('loadSaveOverlayDir', os.getcwd())

        # Ask the user to select a label file
        dlg = wx.FileDialog(
            self,
            message=strings.titles[self, 'loadDialog'],
            defaultDir=loadDir,
            style=wx.FD_OPEN)

        # User cancelled the dialog
        if dlg.ShowModal() != wx.ID_OK:
            return

        # Load the specified label file
        filename = dlg.GetPath()
        emsg     = strings.messages[self, 'loadError'].format(filename)
        etitle   = strings.titles[  self, 'loadError']
        try:
            with status.reportIfError(msg=emsg, title=etitle):
                melDir, allLabels = fixlabels.loadLabelFile(filename)
        except Exception:
            return

        # Ok we've got the labels, now
        # we need to figure out which
        # overlay to add them to.

        # If the label file does not refer
        # to a Melodic directory, and the
        # current overlay is a compatible
        # image, apply the labels to the
        # image.
        if overlay is not None and melDir is None:
            applyLabels(filename, overlay, allLabels, False)
            return

        # If the label file refers to a
        # Melodic directory, and the
        # current overlay is a compatible
        # image.
        if overlay is not None and melDir is not None:

            if isinstance(overlay, fslmelimage.MelodicImage):
                overlayDir = overlay.getMelodicDir()
            elif overlay.dataSource is not None:
                overlayDir = op.dirname(overlay.dataSource)
            else:
                overlayDir = 'none'

            # And both the current overlay and
            # the label file refer to the same
            # directory, then we apply the
            # labels to the curent overlay.
            if op.abspath(melDir) == op.abspath(overlayDir):

                applyLabels(filename, overlay, allLabels, False)
                return

            # Otherwise, if the overlay and the
            # label file refer to different
            # directories...

            # Ask the user whether they want to load
            # the image specified in the label file,
            # or apply the labels to the currently
            # selected image.
            dlg = wx.MessageDialog(
                self,
                strings.messages[self, 'diffMelDir'].format(
                    melDir, overlayDir),
                style=wx.ICON_QUESTION | wx.YES_NO | wx.CANCEL)
            dlg.SetYesNoLabels(
                strings.messages[self, 'diffMelDir.labels'],
                strings.messages[self, 'diffMelDir.overlay'])

            response = dlg.ShowModal()

            # User cancelled the dialog
            if response == wx.ID_CANCEL:
                return

            # User chose to load the melodic
            # image specified in the label
            # file. We'll carry on with this
            # processing below.
            elif response == wx.ID_YES:
                pass

            # Apply the labels to the current
            # overlay, even though they are
            # from different analyses.
            else:
                applyLabels(filename, overlay, allLabels, False)
                return

        # If we've reached this far, we are
        # going to attempt to identify the
        # image associated with the label
        # file, load that image, and then
        # apply the labels.

        # The label file does not
        # specify a melodic directory
        if melDir is None:

            msg   = strings.messages[self, 'noMelDir'].format(filename)
            title = strings.titles[  self, 'loadError']
            wx.MessageBox(msg, title, wx.ICON_ERROR | wx.OK)
            return

        # Try loading the melodic_IC image
        # specified in the label file.
        try:
            overlay = fslmelimage.MelodicImage(melDir)

            log.debug('Adding {} to overlay list'.format(overlay))

            with props.skip(self.overlayList, 'overlays',        self.name),\
                 props.skip(self.displayCtx,  'selectedOverlay', self.name):

                self.overlayList.append(overlay)

            if self.displayCtx.autoDisplay:
                autodisplay.autoDisplay(overlay,
                                        self.overlayList,
                                        self.displayCtx)

            fslsettings.write('loadSaveOverlayDir', op.abspath(melDir))

        except Exception as e:

            e     = str(e)
            msg   = strings.messages[self, 'loadError'].format(filename, e)
            title = strings.titles[  self, 'loadError']
            log.debug('Error loading classification file '
                      '({}), ({})'.format(filename, e), exc_info=True)
            wx.MessageBox(msg, title, wx.ICON_ERROR | wx.OK)

        # Apply the loaded labels
        # to the loaded overlay.
        applyLabels(filename, overlay, allLabels, True)
Esempio n. 24
0
    def __copyOverlay(self):
        """Creates a copy of the currently selected overlay, and inserts it
        into the :class:`.OverlayList`.
        """

        import wx

        overlay = self.__displayCtx.getSelectedOverlay()

        if overlay is None:
            return

        # TODO support for other overlay types
        if type(overlay) != fslimage.Image:
            raise RuntimeError('Currently, only {} instances can be '
                               'copied'.format(fslimage.Image.__name__))

        display = self.__displayCtx.getDisplay(overlay)

        # We ask the user questions three:
        #  - Copy data, or create an empty (a.k.a. mask) image?
        #  - Copy display settings?
        #  - For 4D, copy 4D, or just the current 3D volume?
        #
        # Here we build a list of
        # questions and initial states.
        options = []
        states = []

        createMaskSetting = 'fsleyes.actions.copyoverlay.createMask'
        copyDisplaySetting = 'fsleyes.actions.copyoverlay.copyDisplay'
        copy4DSetting = 'fsleyes.actions.copyoverlay.copy4D'

        createMask = fslsettings.read(createMaskSetting, False)
        copyDisplay = fslsettings.read(copyDisplaySetting, False)
        copy4D = fslsettings.read(copy4DSetting, False)
        is4D = len(overlay.shape) > 3 and overlay.shape[3] > 1

        options.append(strings.messages['actions.copyoverlay.createMask'])
        states.append(createMask)

        options.append(strings.messages['actions.copyoverlay.copyDisplay'])
        states.append(copyDisplay)

        if is4D:
            options.append(strings.messages['actions.copyoverlay.copy4D'])
            states.append(copy4D)

        # Ask the user what they want to do
        dlg = fsldlg.CheckBoxMessageDialog(self.__frame,
                                           title=strings.actions[self],
                                           message='Copy {}'.format(
                                               display.name),
                                           cbMessages=options,
                                           cbStates=states,
                                           yesText='OK',
                                           cancelText='Cancel',
                                           focus='yes')

        if dlg.ShowModal() != wx.ID_YES:
            return

        createMask = dlg.CheckBoxState(0)
        copyDisplay = dlg.CheckBoxState(1)
        if is4D:
            copy4D = dlg.CheckBoxState(2)

        fslsettings.write(createMaskSetting, createMask)
        fslsettings.write(copyDisplaySetting, copyDisplay)
        if is4D:
            fslsettings.write(copy4DSetting, copy4D)

        copyImage(self.__overlayList,
                  self.__displayCtx,
                  overlay,
                  createMask=createMask,
                  copy4D=copy4D,
                  copyDisplay=copyDisplay)
Esempio n. 25
0
def initialise(splash, namespace, callback):
    """Called by :func:`main`. Bootstraps/Initialises various parts of
    *FSLeyes*.

    The ``callback`` function is asynchronously called when the initialisation
    is complete.

    :arg splash:    The :class:`.FSLeyesSplash` screen.

    :arg namespace: The ``argparse.Namespace`` object containing parsed
                    command line arguments.

    :arg callback:  Function which is called when initialisation is done.
    """

    import fsl.utils.settings as fslsettings
    import fsleyes_props as props
    import fsleyes.gl as fslgl

    props.initGUI()

    # The save/load directory defaults
    # to the current working directory.
    curDir = op.normpath(os.getcwd())

    # But if we are running as a frozen application, check to
    # see if FSLeyes has been started by the system (e.g.
    # double-clicking instead of being called from the CLI).
    #
    # If so, we set the save/load directory
    # to the user's home directory instead.
    if fwidgets.frozen():

        fsleyesDir = op.dirname(__file__)

        # If we're a frozen OSX application,
        # we need to adjust the FSLeyes dir
        # (which will be:
        #   [install_dir]/FSLeyes.app/Contents/MacOS/fsleyes/),
        #
        # Because the cwd will default to:
        #   [install_dir]/

        if fslplatform.os == 'Darwin':

            fsleyesDir = op.normpath(
                op.join(fsleyesDir, '..', '..', '..', '..'))

        # Similar adjustment for linux
        elif fslplatform.os == 'Linux':
            fsleyesDir = op.normpath(op.join(fsleyesDir, '..'))

        if curDir == fsleyesDir:
            curDir = op.expanduser('~')

    fslsettings.write('loadSaveOverlayDir', curDir)

    # Initialise silly things
    if namespace.bumMode:
        import fsleyes.icons as icons
        icons.BUM_MODE = True

    # Set notebook server port
    fslsettings.write('fsleyes.notebook.port', namespace.notebookPort)

    # This is called by fsleyes.gl.getGLContext
    # when the GL context is ready to be used.
    def realCallback():
        fslgl.bootstrap(namespace.glversion)
        callback()

    try:
        # Force the creation of a wx.glcanvas.GLContext object,
        # and initialise OpenGL version-specific module loads.
        fslgl.getGLContext(ready=realCallback)

    except Exception:
        log.error('Unable to initialise OpenGL!', exc_info=True)
        splash.Destroy()
        sys.exit(1)
Esempio n. 26
0
    def __loadColourMap(self):
        """This method does the following:

        1. Prompts the user to select a colour map file

        2. Prompts the user to name the new colour map.

        3. Registers the colour map with the :mod:`.colourmaps` module.

        4. Asks the user if they want the colour map installed, and installs
           it if they do.
        """

        import wx

        app = wx.GetApp()

        # Get the most recent colour map
        # directory if there is one
        loadDir = fslsettings.read('fsleyes.loadcolourmapdir', os.getcwd())

        # prompt the user to choose a colour map file
        dlg = wx.FileDialog(app.GetTopWindow(),
                            defaultDir=loadDir,
                            message=strings.messages[self, 'loadcmap'],
                            style=wx.FD_OPEN)

        if dlg.ShowModal() != wx.ID_OK:
            return

        # prompt the user to choose  a name for the colour
        # map (using the filename prefix as the default)
        cmapFile = dlg.GetPath()
        cmapDir = op.dirname(cmapFile)
        cmapName = op.splitext(op.basename(cmapFile))[0]

        cmapNameMsg = strings.messages[self, 'namecmap']
        cmapNameTitle = strings.titles[self, 'namecmap']

        while True:

            dlg = wx.TextEntryDialog(app.GetTopWindow(), cmapNameMsg,
                                     cmapNameTitle, cmapName)

            if dlg.ShowModal() != wx.ID_OK:
                return

            cmapName = dlg.GetValue()
            cmapKey = fslcmap.makeValidMapKey(cmapName)

            # a colour map with the specified name already exists
            if fslcmap.isColourMapRegistered(cmapKey):
                cmapNameMsg = strings.messages[self, 'alreadyinstalled']
                continue

            break

        # register the selected colour map file
        fslcmap.registerColourMap(cmapFile,
                                  self.overlayList,
                                  self.displayCtx,
                                  key=cmapKey,
                                  name=cmapName)

        # Save the directory for next time
        fslsettings.write('fsleyes.loadcolourmapdir', cmapDir)

        # ask the user if they want to install
        # the colour map for future use
        dlg = wx.MessageDialog(app.GetTopWindow(),
                               caption=strings.titles[self, 'installcmap'],
                               message=strings.messages[self, 'installcmap'],
                               style=wx.YES_NO)

        if dlg.ShowModal() != wx.ID_YES:
            return

        # install the colour map
        etitle = strings.titles[self, 'installerror']
        emsg = strings.messages[self, 'installerror']

        with status.reportIfError(title=etitle, msg=emsg, raiseError=False):
            fslcmap.installColourMap(cmapKey)
Esempio n. 27
0
def saveOverlay(overlay, display=None):
    """Saves the currently selected overlay (only if it is a :class:`.Image`),
    by a call to :meth:`.Image.save`. If a ``display`` is provided, the
    :attr:`.Display.name` may be updated to match the new overlay file name.

    :arg overlay: The :class:`.Image` overlay to save
    :arg display: The :class:`.Display` instance associated with the overlay.
    """

    import wx

    # TODO support for other overlay types
    if not isinstance(overlay, fslimage.Image):
        raise RuntimeError('Non-volumetric types not supported yet')

    # If this image has been loaded from a file,
    # ask the user whether they want to overwrite
    # that file, or save the image to a new file.
    #
    if overlay.dataSource is not None:

        # If the data source is not nifti (e.g.
        # mgz), we are not going to overwrite it,
        # so we don't ask.
        if fslimage.looksLikeImage(overlay.dataSource):

            msg = strings.messages['SaveOverlayAction.overwrite'].format(
                overlay.dataSource)
            title = strings.titles['SaveOverlayAction.overwrite'].format(
                overlay.dataSource)

            dlg = wx.MessageDialog(wx.GetTopLevelWindows()[0],
                                   message=msg,
                                   caption=title,
                                   style=(wx.ICON_WARNING | wx.YES_NO
                                          | wx.CANCEL | wx.NO_DEFAULT))
            dlg.SetYesNoCancelLabels(
                strings.labels['SaveOverlayAction.overwrite'],
                strings.labels['SaveOverlayAction.saveNew'],
                strings.labels['SaveOverlayAction.cancel'])

            response = dlg.ShowModal()

            # Cancel == cancel the save
            # Yes    == overwrite the existing file
            # No     == save to a new file (prompt the user for the file name)
            if response == wx.ID_CANCEL:
                return

            if response == wx.ID_YES:
                doSave(overlay)
                return

        fromDir = op.dirname(overlay.dataSource)
        filename = fslimage.removeExt(op.basename(overlay.dataSource))
        filename = '{}_copy'.format(filename)
    else:
        fromDir = fslsettings.read('loadSaveOverlayDir', os.getcwd())

        if display is not None: filename = display.name
        else: filename = overlay.name

    filename = filename.replace('/', '_')
    filename = filename.replace('\\', '_')

    # Ask the user where they
    # want to save the image
    msg = strings.titles['SaveOverlayAction.saveFile']
    dlg = wx.FileDialog(wx.GetApp().GetTopWindow(),
                        message=msg,
                        defaultDir=fromDir,
                        defaultFile=filename,
                        style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)

    if dlg.ShowModal() != wx.ID_OK:
        return

    # Make sure that the user chose a supported
    # extension. If not, use the default extension.
    savePath = dlg.GetPath()
    prefix, suffix = fslimage.splitExt(savePath)

    if suffix == '':
        savePath = '{}{}'.format(prefix, fslimage.defaultExt())

    oldPath = overlay.dataSource
    saveDir = op.dirname(savePath)

    if doSave(overlay, savePath):

        # Cache the save directory for next time.
        fslsettings.write('loadSaveOverlayDir', saveDir)

        # If image was in memory, or its old
        # name equalled the old datasource
        # base name, update its name.
        if oldPath is None or \
           fslimage.removeExt(op.basename(oldPath)) == overlay.name:

            overlay.name = fslimage.removeExt(op.basename(savePath))

            if display is not None:
                display.name = overlay.name
Esempio n. 28
0
    def __doImport(self):

        import wx

        frame = wx.GetApp().GetTopWindow()

        # Ask the user where to get the data
        msg = strings.messages[self, 'selectFile']
        fromDir = fslsettings.read('loadSaveOverlayDir', os.getcwd())
        dlg = wx.FileDialog(frame,
                            message=msg,
                            defaultDir=fromDir,
                            style=wx.FD_OPEN)

        if dlg.ShowModal() != wx.ID_OK:
            return

        filePath = dlg.GetPath()
        fileName = op.basename(filePath)

        # Load the file, show an
        # error if it fails
        try:

            # Assuming that the data series
            # to plot are stored as columns
            data = np.loadtxt(filePath, dtype=np.float).T

            # Make sure the data is 2D, to
            # make code below easier and
            # happier.
            if len(data.shape) == 1:
                data = data.reshape((1, -1))

        except Exception as e:
            title = strings.titles[self, 'error']
            msg = strings.messages[self, 'error'].format(
                filePath, '{}: {}'.format(type(e).__name__, str(e)))

            wx.MessageBox(msg, title, wx.ICON_ERROR | wx.OK)
            return

        fslsettings.write('loadSaveOverlayDir', filePath)

        # Ask the user the x axis scaling factor.
        # If the currently selected overlay is
        # Nifti and 4D, default to its pixdim[3]
        overlay = self.__displayCtx.getSelectedOverlay()

        if overlay is not None                 and \
           isinstance(overlay, fslimage.Nifti) and \
           len(overlay.shape) == 4             and \
           self.__plotPanel.usePixdim:
            xscale = overlay.pixdim[3]

        else:
            xscale = 1

        title = strings.titles[self, 'selectXScale']
        msg = strings.messages[self, 'selectXScale']

        # If the user pushes 'Ok', the entered value
        # is used as a fixed X axis interval. Otherwise,
        # it is assumed that the first column in the
        # file is the x axis data.
        dlg = numdlg.NumberDialog(frame,
                                  title=title,
                                  message=msg,
                                  initial=xscale,
                                  minValue=1e-5,
                                  cancelText=strings.labels[self,
                                                            'firstColumnIsX'])

        firstColumnIsX = dlg.ShowModal() != wx.ID_OK
        xscale = dlg.GetValue()

        # Add the data series
        series = []

        if firstColumnIsX:
            xdata = data[0, :]
            ydata = data[1:, :]
        else:
            xdata = np.arange(0,
                              data.shape[1] * xscale,
                              xscale,
                              dtype=np.float)
            ydata = data

        for i, ydata in enumerate(ydata):

            x = np.array(xdata)
            y = np.array(ydata)
            fin = np.isfinite(x) & np.isfinite(y)
            x = x[fin]
            y = y[fin]

            ds = plotting.DataSeries(None, self.__overlayList,
                                     self.__displayCtx, self.__plotPanel)
            ds.setData(x, y)

            # If we recognise the file name,
            # we can give it a useful label.
            label = strings.plotLabels.get('{}.{}'.format(fileName, i),
                                           '{} [{}]'.format(fileName, i))

            ds.label = label
            ds.lineWidth = 1
            ds.colour = fslcm.randomDarkColour()

            series.append(ds)

        self.__plotPanel.dataSeries.extend(series)
Esempio n. 29
0
    def __doScreenshot(self):
        """Capture a screenshot. Prompts the user to select a file to save
        the screenshot to, and then calls the :func:`screenshot` function.
        """

        lastDirSetting = 'fsleyes.actions.screenshot.lastDir'

        # Ask the user where they want
        # the screenshot to be saved
        fromDir = fslsettings.read(lastDirSetting, os.getcwd())

        # We can get a list of supported output
        # types via a matplotlib figure object
        fig = plt.figure()
        fmts = fig.canvas.get_supported_filetypes()

        # Default to png if
        # it is available
        if 'png' in fmts:
            fmts = [('png', fmts['png'])] + \
                   [(k, v) for k, v in fmts.items() if k != 'png']
        else:
            fmts = list(fmts.items())

        wildcard = [
            '[*.{}] {}|*.{}'.format(fmt, desc, fmt) for fmt, desc in fmts
        ]
        wildcard = '|'.join(wildcard)
        filename = 'screenshot'

        dlg = wx.FileDialog(self.__panel,
                            message=strings.messages[self, 'screenshot'],
                            wildcard=wildcard,
                            defaultDir=fromDir,
                            defaultFile=filename,
                            style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)

        if dlg.ShowModal() != wx.ID_OK:
            return

        filename = dlg.GetPath()

        # Make the dialog go away before
        # the screenshot gets taken
        dlg.Close()
        dlg.Destroy()
        wx.GetApp().Yield()

        # Show an error if the screenshot
        # function raises an error
        doScreenshot = status.reportErrorDecorator(
            strings.titles[self, 'error'],
            strings.messages[self, 'error'])(screenshot)

        # We do the screenshot asynchronously,
        # to make sure it is performed on
        # the main thread, during idle time
        idle.idle(doScreenshot, self.__panel, filename)

        status.update(strings.messages[self, 'pleaseWait'].format(filename))

        fslsettings.write(lastDirSetting, op.dirname(filename))
Esempio n. 30
0
    def __copyOverlay(self):
        """Creates a copy of the currently selected overlay, and inserts it
        into the :class:`.OverlayList`.
        """

        import wx

        overlay = self.displayCtx.getSelectedOverlay()

        if overlay is None:
            return

        # TODO support for other overlay types
        if type(overlay) != fslimage.Image:
            raise RuntimeError('Currently, only {} instances can be '
                               'copied'.format(fslimage.Image.__name__))

        display = self.displayCtx.getDisplay(overlay)

        # We ask the user questions four:
        #  - Copy data, or create an empty (a.k.a. mask) image?
        #  - Copy display settings?
        #  - For 4D, copy 4D, or just the current 3D volume?
        #  - For complex/RGB(A), copy as single channel, or multi-channel?
        #
        # Here we build a list of
        # questions and initial states.
        options = []
        states = []

        createMaskSetting = 'fsleyes.actions.copyoverlay.createMask'
        copyDisplaySetting = 'fsleyes.actions.copyoverlay.copyDisplay'
        copy4DSetting = 'fsleyes.actions.copyoverlay.copy4D'
        copyMultiSetting = 'fsleyes.actions.copyoverlay.copyMulti'

        createMask = fslsettings.read(createMaskSetting, False)
        copyDisplay = fslsettings.read(copyDisplaySetting, False)
        copy4D = fslsettings.read(copy4DSetting, False)
        copyMulti = fslsettings.read(copy4DSetting, True)
        is4D = len(overlay.shape) > 3 and overlay.shape[3] > 1
        isMulti = overlay.iscomplex or overlay.nvals > 1

        options.append(strings.messages['actions.copyoverlay.createMask'])
        states.append(createMask)

        options.append(strings.messages['actions.copyoverlay.copyDisplay'])
        states.append(copyDisplay)

        if is4D:
            options.append(strings.messages['actions.copyoverlay.copy4D'])
            states.append(copy4D)

        if isMulti:
            options.append(strings.messages['actions.copyoverlay.copyMulti'])
            states.append(copyMulti)

        # Ask the user what they want to do
        dlg = fsldlg.CheckBoxMessageDialog(self.__frame,
                                           title=strings.actions[self],
                                           message='Copy {}'.format(
                                               display.name),
                                           cbMessages=options,
                                           cbStates=states,
                                           yesText='OK',
                                           cancelText='Cancel',
                                           focus='yes')

        if dlg.ShowModal() != wx.ID_YES:
            return

        createMask = dlg.CheckBoxState(0)
        copyDisplay = dlg.CheckBoxState(1)
        if is4D: copy4D = dlg.CheckBoxState(2)
        if isMulti: copyMulti = dlg.CheckBoxState(3 if is4D else 2)

        fslsettings.write(createMaskSetting, createMask)
        fslsettings.write(copyDisplaySetting, copyDisplay)
        if is4D: fslsettings.write(copy4DSetting, copy4D)
        if isMulti: fslsettings.write(copyMultiSetting, copyMulti)

        # If the user de-selected copy all channels,
        # ask them which channel they want to copy
        channel = None
        if isMulti and (not copyMulti):
            if overlay.iscomplex:
                choices = ['real', 'imag']
            else:
                choices = ['R', 'G', 'B', 'A'][:overlay.nvals]

            labels = [strings.choices[self, 'component'][c] for c in choices]
            title = strings.titles['actions.copyoverlay.component']
            msg = strings.messages['actions.copyoverlay.component']
            dlg = wx.SingleChoiceDialog(self.__frame,
                                        msg,
                                        title,
                                        choices=labels)

            if dlg.ShowModal() != wx.ID_OK:
                return

            channel = choices[dlg.GetSelection()]

        copyImage(self.overlayList,
                  self.displayCtx,
                  overlay,
                  createMask=createMask,
                  copy4D=copy4D,
                  channel=channel,
                  copyDisplay=copyDisplay)