def __action(self):
        """This method is the guts of the ``DiagnosticReportAction``. It does
        the following:

          1. Prompts the user to select a location to save the report file.

          2. Generates the report.

          3. Formats the report as JSON.

          4. Saves the report to the specified location.
        """

        import wx

        dlg = wx.FileDialog(self.__frame,
                            message=strings.titles[self, 'saveReport'],
                            defaultDir=os.getcwd(),
                            defaultFile='report.txt',
                            style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)

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

        path = dlg.GetPath()

        status.update('Writing diagnostic report to {}...'.format(path))

        report = self.__generateReport()
        report = self.__formatReport(report)

        log.debug('Diagnostic report:\n{}'.format(report))

        with open(path, 'wt') as f:
            f.write(report)
Beispiel #2
0
        def addLabel(startIdx):

            # If the user closes this panel while the
            # label list is being created, wx will
            # complain when we try to append things
            # to a widget that has been destroyed.
            try:

                for labelIdx in range(startIdx,
                                      min(startIdx + blockSize, nlabels)):

                    # A new request to re-create the list has
                    # been made - cancel this creation chain.
                    if self.__labelListCreateKey != myCreateKey:
                        return

                    label = lut[labelIdx]
                    widget = LabelWidget(self, lut, label)

                    self.__labelList.Append(str(label.value),
                                            clientData=label.value,
                                            extraWidget=widget)

                if labelIdx == nlabels - 1:
                    status.update('Lookup table label list created.')
                    self.Enable()
                    self.__labelList.Enable()
                else:
                    idle.idle(addLabel, labelIdx + 1)

            except RuntimeError:
                pass
        def changeAtlasList():

            # See comment above about
            # suppressing wx complaints
            try:

                filterStr = self.__regionFilter.GetValue().lower().strip()
                regionList.ApplyFilter(filterStr, ignoreCase=True)

                self.__updateAtlasState(atlasDesc)

                status.update(strings.messages[self, 'regionsLoaded'].format(
                    atlasDesc.name))
                log.debug('Showing region list for {} ({})'.format(
                    atlasDesc.atlasID, id(regionList)))

                # Hide the currently
                # shown region list
                old = self.__regionSizer.GetItem(1).GetWindow()
                if old is not None:
                    old.Show(False)

                regionList.Show(True)
                self.__regionSizer.Remove(1)
                self.__regionSizer.Insert(1,
                                          regionList,
                                          flag=wx.EXPAND,
                                          proportion=1)
                self.__regionSizer.Layout()

                if atlasPanelDisabled:
                    self.__atlasPanel.enableAtlasPanel()

            except wx.PyDeadObjectError:
                pass
Beispiel #4
0
        def onFinish():

            if not fwidgets.isalive(grid):
                return

            status.update('All clusters loaded.')
            self.Enable()
            grid.Show()
            grid.Refresh()
Beispiel #5
0
def test_clearStatus():
    target = MockTarget()
    status.setTarget(target)

    status.update('Status1', None)
    assert target.msg == 'Status1'
    status.clearStatus()
    assert target.msg == ''

    status.update('Status1', 0.25)
    assert target.msg == 'Status1'
    status.clearStatus()
    assert target.msg == ''
Beispiel #6
0
            def load():

                atlas = atlases.loadAtlas(atlasID, summary, resolution=res)

                # The atlas panel may be destroyed
                # before the atlas is loaded.
                if not self or self.destroyed():
                    return

                self.__loadedAtlases[atlasID, summary, res] = atlas

                status.update('Atlas {} loaded.'.format(atlasID))

                if onLoad is not None:
                    idle.idle(onLoad, atlas)
def test_ClearThread_die():
    target = MockTarget()
    status.setTarget(target)

    # make sure the clearthread is running
    status.update('Status1', 1.0)
    time.sleep(1.1)

    # and can be killed
    status._clearThread.die()
    status._clearThread.clear(0.1)
    status._clearThread.join()
    status._clearThread = None

    # and then gets restarted again
    status.update('Status1', 0.25)
    assert target.msg == 'Status1'
    time.sleep(0.5)
    assert target.msg == ''
Beispiel #8
0
def test_update():

    # Test without a target
    status.setTarget(None)
    status.update('Status')

    target = MockTarget()

    status.setTarget(target)

    assert target.msg is None
    status.update('Status1', None)
    assert target.msg == 'Status1'
    status.update('Status2', None)
    assert target.msg == 'Status2'
Beispiel #9
0
def test_update_timeout():

    status.setTarget(None)
    status.clearStatus()

    target = MockTarget()
    status.setTarget(target)

    # Test that the message gets cleared
    status.update('Status1', 0.25)
    assert target.msg == 'Status1'
    time.sleep(0.5)
    assert target.msg == ''

    # If a timed update is followed by an untimed
    # update, the untimed one should persist
    status.update('Status2', 0.25)
    status.update('Status3', None)
    time.sleep(0.5)
    assert target.msg == 'Status3'
Beispiel #10
0
    def __runCorrelateAction(self):
        """Called when this :class:`.Action` is invoked. Calculates correlation
        values from the voxel at the current :attr:`.DisplayContext.location`
        (relative to the currently selected overlay) to all other voxels, and
        updates the correlate overlay.

        The correlation calculation and overlay update is performed on a
        separate thread (via :meth:`.idle.run`), with a call to
        :meth:`calculateCorrelation`.
        """

        # Because of the multi-threaded/asynchronous
        # way that this function does its job,
        # allowing it to be called multiple times
        # before prior calls have completed would be
        # very dangerous indeed.
        if self.__correlateFlag.is_set():
            log.debug('Correlate action is already '
                      'running - ignoring request')
            return

        # See if a correlate overlay already exists
        # for the currently selected overlay
        ovl = self.displayCtx.getSelectedOverlay()
        corrOvl = self.__correlateOverlays.get(ovl, None)

        # If not, check to see if it is a correlate
        # overlay that is selected and, if it is,
        # look up the corresponding source overlay.
        if corrOvl is None:
            if ovl in self.__overlayCorrelates:
                corrOvl = ovl
                ovl = self.__overlayCorrelates[corrOvl]

        # If corrOvl is still None, it means that
        # there is no correlate overlay for the
        # currently selected overlay. In this case,
        # we'll create a new correlate overlay and
        # add it to the overlay list after the
        # correlation values have been calculated.

        opts = self.displayCtx.getOpts(ovl)
        xyz = opts.getVoxel(vround=True)

        if xyz is None:
            return

        data = ovl.data[opts.index(atVolume=False)]

        # The correlation calculation is performed
        # on a separate thread. This thread then
        # schedules a function on idle.idle to
        # update the correlation overlay back on the
        # main thread.
        def calcCorr():

            correlations = self.calculateCorrelation(xyz, data)

            # The correlation overlay is updated/
            # created on the main thread.
            def update():

                try:

                    # A correlation overlay already
                    # exists for the source overlay
                    # - update its data
                    if corrOvl is not None:
                        corrOvl[:] = correlations

                    # The correlation overlay hasn't
                    # been created yet - create a
                    # new overlay with the correlation
                    # values.
                    else:
                        self.__createCorrelateOverlay(ovl, correlations)

                finally:
                    fslstatus.clearStatus()
                    self.__correlateFlag.clear()

            idle.idle(update)

        # Protect against more calls
        # while this job is running.
        self.__correlateFlag.set()
        fslstatus.update(strings.messages[self, 'calculating'].format(*xyz))
        idle.run(calcCorr)
Beispiel #11
0
    def __createLabelList(self):
        """Refreshes the contents of the class:`.LookupTable` label list, from
        the currently selected ``LookupTable``.
        """

        # The label list is created asynchronously on
        # the wx.Idle loop, because it can take some
        # time for big lookup tables. In the event
        # that the list needs to be re-created (e.g.
        # the current lookup table is changed), this
        # attribute is used so that scheduled creation
        # routines (the addLabel function defined
        # below) can tell whether they should cancel.
        myCreateKey = (self.__labelListCreateKey + 1) % 65536
        self.__labelListCreateKey = myCreateKey

        lut = self.__selectedLut
        nlabels = len(lut)

        self.__labelList.Clear()

        # If this is a new lut, it
        # won't have any labels
        if nlabels == 0:
            return

        # The label widgets are created via consecutive
        # calls to addLabel, which is scheduled on the
        # idle.idle loop. We create blockSize labels
        # in each asynchronous call.
        blockSize = 100

        def addLabel(startIdx):

            # If the user closes this panel while the
            # label list is being created, wx will
            # complain when we try to append things
            # to a widget that has been destroyed.
            try:

                for labelIdx in range(startIdx,
                                      min(startIdx + blockSize, nlabels)):

                    # A new request to re-create the list has
                    # been made - cancel this creation chain.
                    if self.__labelListCreateKey != myCreateKey:
                        return

                    label = lut[labelIdx]
                    widget = LabelWidget(self, lut, label)

                    self.__labelList.Append(str(label.value),
                                            clientData=label.value,
                                            extraWidget=widget)

                if labelIdx == nlabels - 1:
                    status.update('Lookup table label list created.')
                    self.Enable()
                    self.__labelList.Enable()
                else:
                    idle.idle(addLabel, labelIdx + 1)

            except RuntimeError:
                pass

        log.debug('Creating lookup table label list')
        status.update('Creating lookup table label list...', timeout=None)

        self.__labelList.Disable()
        self.Disable()
        idle.idle(addLabel, 0)
Beispiel #12
0
 def defaultLoadFunc(s):
     msg = strings.messages['loadOverlays.loading'].format(s)
     status.update(msg)
Beispiel #13
0
    def __reloadOverlay(self):
        """Reloads the currently selected overlay from disk.
        """
        ovl = self.displayCtx.getSelectedOverlay()

        if ovl is None or type(ovl) != fslimage.Image:
            raise RuntimeError('Only Image overlays can be reloaded')

        index      = self.overlayList.index(ovl)
        order      = self.displayCtx.overlayOrder[:]
        dataSource = ovl.dataSource

        status.update('Reloading {}...'.format(dataSource))

        # Get refs to all DisplayContexts -
        # the master one, and the one for
        # every view panel.
        displayCtxs  = [self.displayCtx]
        viewPanels   = self.__frame.viewPanels
        displayCtxs += [vp.displayCtx for vp in viewPanels]

        # Now get refs to all Display and
        # DisplayOpts instances for this
        # overlay.
        displays = []
        opts     = []

        for dctx in displayCtxs:
            displays.append(self.displayCtx.getDisplay(ovl))
            opts    .append(self.displayCtx.getOpts(   ovl))

        # Turn those references into
        # {prop : value} dictionaries
        for i in range(len(displays)):

            d = displays[i]
            o = opts[    i]

            displayProps = d.getAllProperties()[0]
            optProps     = o.getAllProperties()[0]

            displays[i] = {p : getattr(d, p) for p in displayProps}
            opts[    i] = {p : getattr(o, p) for p in optProps}

        # Now that we've got all the settings
        # for this overlay, we'll remove it
        # from the list. If removeOverlay
        # returns False, it probably means
        # the user cancelled the action.
        if not removeoverlay.removeOverlay(self.overlayList,
                                           self.displayCtx,
                                           ovl,
                                           'reloadoverlay.unsaved'):
            return

        # Now we re-load the overlay, and add it
        # back in to the list at the same location
        ovl = fslimage.Image(dataSource)
        self.overlayList.insert(index, ovl)

        # Make sure the overlay is selected,
        # and the display order is preserved
        self.displayCtx.selectOverlay(ovl)
        self.displayCtx.overlayOrder = order

        # The last step is to re-apply all of the
        # Display/DisplayOpts settings to the
        # newly created Display/DisplayOpts
        # instances.
        for i, dctx in enumerate(displayCtxs):

            displayProps = displays[i]
            optProps     = opts[    i]
            d            = dctx.getDisplay(ovl)

            for prop, val in displayProps.items():

                if not d.propertyIsEnabled(prop):
                    continue

                try:
                    setattr(d, prop, val)
                except props.DisabledError:
                    continue

            # Get a ref to the DisplayOpts instance
            # after we have configured the Display,
            # as its overlay type may have changed
            # (and hence the DisplayOpts instance
            # may have been re-created).
            o = dctx.getOpts(ovl)
            for prop, val in optProps.items():

                if not o.propertyIsEnabled(prop):
                    continue

                setattr(o, prop, val)

        status.update('{} reloaded.'.format(dataSource))
    def __onAtlasSelect(self, ev=None, atlasDesc=None):
        """Called when the user selects an atlas in the atlas list, or
        the :meth:`selectAtlas` method is called.

        If a region list (a list of :class:`OverlayListWidget` items for every
        region in the atlas, to be displayed in the region list) has not yet
        been created, it is created - this is done asynchronously (via the
        :func:`idle.idle` function), as it can take quite a long time for
        some of the atlases (e.g. the Talairach and Juelich).

        Then the region list is updated to show the regions for the newly
        selected atlas.
        """

        if ev is not None:
            atlasDesc = ev.data

        regionList = self.__regionLists.get(atlasDesc.atlasID, None)
        atlasPanelDisabled = regionList is None

        # This function changes the displayed region
        # list. We schedule it on the wx idle loop,
        # so it will get called after the region list
        # has been populated (if it has not been
        # displayed before).
        def changeAtlasList():

            # See comment above about
            # suppressing wx complaints
            try:

                filterStr = self.__regionFilter.GetValue().lower().strip()
                regionList.ApplyFilter(filterStr, ignoreCase=True)

                self.__updateAtlasState(atlasDesc)

                status.update(strings.messages[self, 'regionsLoaded'].format(
                    atlasDesc.name))
                log.debug('Showing region list for {} ({})'.format(
                    atlasDesc.atlasID, id(regionList)))

                # Hide the currently
                # shown region list
                old = self.__regionSizer.GetItem(1).GetWindow()
                if old is not None:
                    old.Show(False)

                regionList.Show(True)
                self.__regionSizer.Remove(1)
                self.__regionSizer.Insert(1,
                                          regionList,
                                          flag=wx.EXPAND,
                                          proportion=1)
                self.__regionSizer.Layout()

                if atlasPanelDisabled:
                    self.__atlasPanel.enableAtlasPanel()

            except wx.PyDeadObjectError:
                pass

        if regionList is None:

            # The region list for this atlas has not yet been
            # created. So we create the list, and then create
            # a widget for every region in the atlas. Some of
            # the atlases (Juelich and Talairach in particular)
            # have a large number of regions, so we create the
            # widgets asynchronously on the wx idle loop.
            regionList = elistbox.EditableListBox(
                self.__regionPanel,
                style=(elistbox.ELB_NO_ADD | elistbox.ELB_NO_REMOVE
                       | elistbox.ELB_NO_MOVE))
            regionList.Show(False)

            self.__regionLists[atlasDesc.atlasID] = regionList

            # Add blockSize labels, starting from label[i],
            # to the region list. Then, if necessary,
            # schedule more labels be added, starting from
            # label[i + blockSize].
            blockSize = 20
            nlabels = len(atlasDesc.labels)

            def addToRegionList(start):

                # If the user kills this panel while
                # the region list is being updated,
                # suppress wx complaints.
                try:

                    for i in range(start, min(start + blockSize, nlabels)):
                        label = atlasDesc.labels[i]
                        widget = OverlayListWidget(regionList,
                                                   atlasDesc.atlasID,
                                                   self.__atlasPanel, self,
                                                   label.index)
                        regionList.Append(label.name, extraWidget=widget)

                    if i < nlabels - 1: idle.idle(addToRegionList, i + 1)
                    else: idle.idle(changeAtlasList)

                except wx.PyDeadObjectError:
                    pass

            log.debug('Creating region list for {} ({})'.format(
                atlasDesc.atlasID, id(regionList)))

            status.update(strings.messages[self, 'loadRegions'].format(
                atlasDesc.name),
                          timeout=None)

            # Schedule addToRegionList on the
            # wx idle loop for the first region.
            # The function will recursively
            # schedule itself to run for subsequent
            # regions.
            #
            # Disable the panel while this is
            # occurring.

            atlasPanelDisabled = True

            self.__atlasPanel.enableAtlasPanel(False)
            idle.idle(addToRegionList, 0)
        else:
            idle.idle(changeAtlasList)
Beispiel #15
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))
Beispiel #16
0
def makeFrame(namespace, displayCtx, overlayList, splash):
    """Creates the *FSLeyes* interface.

    This function does the following:

     1. Creates the :class:`.FSLeyesFrame` the top-level frame for ``fsleyes``.

     2. Configures the frame according to the command line arguments (e.g.
        ortho or lightbox view).

     3. Destroys the splash screen that was created by the :func:`context`
        function.

    :arg namespace:   Parsed command line arguments, as returned by
                      :func:`parseArgs`.

    :arg displayCtx:  The  :class:`.DisplayContext`, as created and returned
                      by :func:`makeDisplayContext`.

    :arg overlayList: The :class:`.OverlayList`, as created and returned by
                      :func:`makeDisplayContext`.

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

    :returns: the :class:`.FSLeyesFrame` that was created.
    """

    import fsl.utils.idle as idle
    import fsleyes_widgets.utils.status as status
    import fsleyes.parseargs as parseargs
    import fsleyes.frame as fsleyesframe
    import fsleyes.displaycontext as fsldisplay
    import fsleyes.layouts as layouts
    import fsleyes.views.canvaspanel as canvaspanel

    # Set up the frame scene (a.k.a. layout)
    # The scene argument can be:
    #
    #   - The name of a saved (or built-in) layout
    #
    #   - None, in which case the default or previous
    #     layout is restored, unless a custom script
    #     has been provided.
    script = namespace.runscript
    scene = namespace.scene

    # If a scene/layout or custom script
    # has not been specified, the default
    # behaviour is to restore the previous
    # frame layout.
    restore = (scene is None) and (script is None)

    status.update('Creating FSLeyes interface...')

    frame = fsleyesframe.FSLeyesFrame(None,
                                      overlayList,
                                      displayCtx,
                                      restore,
                                      True,
                                      fontSize=namespace.fontSize)

    # Allow files to be dropped
    # onto FSLeyes to open them
    dt = fsleyesframe.OverlayDropTarget(overlayList, displayCtx)
    frame.SetDropTarget(dt)

    # Make sure the new frame is shown
    # before destroying the splash screen
    frame.Show(True)
    frame.Refresh()
    frame.Update()

    # In certain instances under Linux/GTK,
    # closing the splash screen will crash
    # the application. No idea why. So we
    # leave the splash screen hidden, but
    # not closed, and close it when the main
    # frame is closed. This also works under
    # OSX.
    splash.Hide()
    splash.Refresh()
    splash.Update()

    def onFrameDestroy(ev):
        ev.Skip()

        # splash screen may already
        # have been destroyed
        try:
            splash.Close()
        except Exception:
            pass

    frame.Bind(wx.EVT_WINDOW_DESTROY, onFrameDestroy)

    status.update('Setting up scene...')

    # Set the default SceneOpts.performance
    # level so that all created SceneOpts
    # instances will default to it
    if namespace.performance is not None:
        fsldisplay.SceneOpts.performance.setAttribute(None, 'default',
                                                      namespace.performance)

    # If a layout has been specified,
    # we load the layout
    if namespace.scene is not None:
        layouts.loadLayout(frame, namespace.scene)

    # Apply any view-panel specific arguments
    viewPanels = frame.viewPanels
    for viewPanel in viewPanels:

        if not isinstance(viewPanel, canvaspanel.CanvasPanel):
            continue

        displayCtx = viewPanel.displayCtx
        viewOpts = viewPanel.sceneOpts

        parseargs.applySceneArgs(namespace, overlayList, displayCtx, viewOpts)

    # If a script has been specified, we run
    # the script. This has to be done on the
    # idle loop, because overlays specified
    # on the command line are loaded on the
    # idle loop. Therefore, if we schedule the
    # script on idle (which is a queue), the
    # script can assume that all overlays have
    # already been loaded.
    from fsleyes.actions.runscript import RunScriptAction
    if script is not None:
        idle.idle(frame.menuActions[RunScriptAction], script)

    return frame
Beispiel #17
0
    def loadAtlas(self,
                  atlasID,
                  summary,
                  onLoad=None,
                  onError=None,
                  matchResolution=True):
        """Loads the atlas image with the specified ID. The atlas is loaded
        asynchronously (via the :mod:`.idle` module), as it can take some
        time. Use the `onLoad` argument if you need to do something when the
        atlas has been loaded.

        :arg onLoad:          Optional. A function which is called when the
                              atlas has been loaded, and which is passed the
                              loaded :class:`.Atlas` image.

        :arg onError:         Optional. A function which is called if the
                              atlas loading job raises an error. Passed the
                              ``Exception`` that was raised.

        :arg matchResolution: If ``True`` (the default), the version of the
                              atlas with the most suitable resolution, based
                              on the current contents of the
                              :class:`.OverlayList`, is loaded.

        See the :func:`.atlases.loadAtlas` function for details on the other
        arguments.
        """

        # Get the atlas description, and the
        # most suitable resolution to load.
        desc = atlases.getAtlasDescription(atlasID)
        res = self.__getSuitableResolution(desc, matchResolution)

        if desc.atlasType == 'label':
            summary = True

        atlas = self.__loadedAtlases.get((atlasID, summary, res), None)

        if atlas is None:

            log.debug('Loading atlas {}/{}'.format(
                atlasID, 'label' if summary else 'prob'))

            status.update('Loading atlas {}...'.format(atlasID), timeout=None)

            def load():

                # the panel might get destroyed
                # before this function is called
                if self.destroyed:
                    return

                atlas = atlases.loadAtlas(atlasID, summary, resolution=res)

                # The atlas panel may be destroyed
                # before the atlas is loaded.
                if not self or self.destroyed:
                    return

                self.__loadedAtlases[atlasID, summary, res] = atlas

                status.update('Atlas {} loaded.'.format(atlasID))

                if onLoad is not None:
                    idle.idle(onLoad, atlas)

            idle.run(load, onError=onError)

        # If the atlas has already been loaded,
        # pass it straight to the onload function
        elif onLoad is not None:
            onLoad(atlas)
Beispiel #18
0
    def __genClusterGrid(self, overlay, featImage, contrast, clusters):
        """Creates and returns a :class:`.WidgetGrid` which contains the given
        list of clusters, which are related to the given contrast.


        .. note:: This method assumes that the given ``overlay`` is an
                  :class:`.Image` which has the same voxel dimensions as,
                  and shares the the same world coordinate system as the
                  ``featImage``.


        :arg overlay:   The overlay for which clusters are currently being
                        displayed.

        :arg featImage: The :class:`.FEATImage` to which the clusters are
                        related.

        :arg contrast:  The (0-indexed) number of the contrast to which the
                        clusters are related.

        :arg clusters:  A sequence of objects, each representing one cluster.
                        See the :meth:`.FEATImage.clusterResults` method.
        """

        cols = {
            'index': 0,
            'nvoxels': 1,
            'p': 2,
            'logp': 3,
            'zmax': 4,
            'zmaxcoords': 5,
            'zcogcoords': 6,
            'copemax': 7,
            'copemaxcoords': 8,
            'copemean': 9
        }

        grid = widgetgrid.WidgetGrid(self)
        conName = featImage.contrastNames()[contrast]
        opts = self.displayCtx.getOpts(overlay)

        # We hide the grid and disable
        # this panle while the grid is
        # being created.
        grid.Hide()
        self.Disable()

        grid.SetGridSize(len(clusters), 10)

        grid.ShowRowLabels(False)
        grid.ShowColLabels(True)

        for col, i in cols.items():
            grid.SetColLabel(i, strings.labels[self, col])

        def makeCoordButton(coords):

            label = wx.StaticText(grid, label='[{} {} {}]'.format(*coords))
            btn = wx.Button(grid, label=six.u('\u2192'), style=wx.BU_EXACTFIT)

            sizer = wx.BoxSizer(wx.HORIZONTAL)
            sizer.Add(label, flag=wx.EXPAND, proportion=1)
            sizer.Add(btn)

            def onClick(ev):
                dloc = opts.transformCoords([coords], 'voxel', 'display')[0]
                self.displayCtx.location = dloc

            btn.Bind(wx.EVT_BUTTON, onClick)

            return sizer

        # Creating all of the widgets could
        # take a bit of time, so we'll
        # do it asynchronously via idle.idle
        # display a message while doing so.
        status.update(strings.messages[self, 'loadingCluster'].format(
            contrast + 1, conName),
                      timeout=None)

        def addCluster(i, clust):

            if not fwidgets.isalive(grid):
                return

            zmaxbtn = makeCoordButton((clust.zmaxx, clust.zmaxy, clust.zmaxz))
            zcogbtn = makeCoordButton((clust.zcogx, clust.zcogy, clust.zcogz))
            copemaxbtn = makeCoordButton(
                (clust.copemaxx, clust.copemaxy, clust.copemaxz))

            def fmt(v):
                return '{}'.format(v)

            grid.SetText(i, cols['index'], fmt(clust.index))
            grid.SetText(i, cols['nvoxels'], fmt(clust.nvoxels))
            grid.SetText(i, cols['p'], fmt(clust.p))
            grid.SetText(i, cols['logp'], fmt(clust.logp))
            grid.SetText(i, cols['zmax'], fmt(clust.zmax))
            grid.SetWidget(i, cols['zmaxcoords'], zmaxbtn)
            grid.SetWidget(i, cols['zcogcoords'], zcogbtn)
            grid.SetText(i, cols['copemax'], fmt(clust.copemax))
            grid.SetWidget(i, cols['copemaxcoords'], copemaxbtn)
            grid.SetText(i, cols['copemean'], fmt(clust.copemean))

        # Refresh the grid widget when all
        # clusters have been added.
        def onFinish():

            if not fwidgets.isalive(grid):
                return

            status.update('All clusters loaded.')
            self.Enable()
            grid.Show()
            grid.Refresh()

        for i, clust in enumerate(clusters):
            idle.idle(addCluster, i, clust)

        idle.idle(onFinish)

        return grid
Beispiel #19
0
def applyLayout(frame, name, layout, message=None):
    """Applies the given serialised layout string to the given
    :class:`.FSLeyesFrame`.

    :arg frame:   The :class:`.FSLeyesFrame` instance.
    :arg name:    The layout name.
    :arg layout:  The serialised layout string.
    :arg message: A message to display (using the :mod:`.status` module).
    """

    import fsleyes.views.canvaspanel as canvaspanel

    layout = deserialiseLayout(layout)
    frameChildren = layout[0]
    frameLayout = layout[1]
    vpChildrens = layout[2]
    vpLayouts = layout[3]
    vpPanelProps = layout[4]
    vpSceneProps = layout[5]

    # Show a message while re-configuring the frame
    if message is None:
        message = strings.messages['layout.applyingLayout'].format(
            strings.layouts.get(name, name))

    status.update(message)

    # Clear all existing view
    # panels from the frame
    frame.removeAllViewPanels()

    # Add all of the view panels
    # specified in the layout
    for vp in frameChildren:
        log.debug('Adding view panel {} to frame'.format(vp.__name__))
        frame.addViewPanel(vp)

    # Apply the layout to those view panels
    frame.auiManager.LoadPerspective(frameLayout)

    # For each view panel, add all of the
    # control panels, and lay them out
    viewPanels = frame.viewPanels
    for i in range(len(viewPanels)):

        vp = viewPanels[i]
        children = vpChildrens[i]
        vpLayout = vpLayouts[i]
        panelProps = vpPanelProps[i]
        sceneProps = vpSceneProps[i]

        for child in children:
            log.debug('Adding control panel {} to {}'.format(
                child.__name__,
                type(vp).__name__))
            _addControlPanel(vp, child)

        vp.auiManager.LoadPerspective(vpLayout)

        # Apply saved property values
        # to the view panel.
        for name, val in panelProps.items():
            log.debug('Setting {}.{} = {}'.format(
                type(vp).__name__, name, val))
            vp.deserialise(name, val)

        # And, if it is a CanvasPanel,
        # to its SceneOpts instance.
        if isinstance(vp, canvaspanel.CanvasPanel):
            opts = vp.sceneOpts
            for name, val in sceneProps.items():
                log.debug('Setting {}.{} = {}'.format(
                    type(opts).__name__, name, val))
                opts.deserialise(name, val)
Beispiel #20
0
    def __histPropsChanged(self, *a):
        """Called internally, and when any histogram settings change.
        Re-calculates the histogram data.
        """

        log.debug('Calculating histogram for '
                  'overlay {}'.format(self.overlay.name))

        status.update('Calculating histogram for '
                      'overlay {}'.format(self.overlay.name))

        if np.isclose(self.dataRange.xhi, self.dataRange.xlo):
            self.__xdata = np.array([])
            self.__ydata = np.array([])
            self.__nvals = 0
            return

        if self.ignoreZeros:
            if self.includeOutliers: data = self.__nonZeroData
            else: data = self.__clippedNonZeroData
        else:
            if self.includeOutliers: data = self.__finiteData
            else: data = self.__clippedFiniteData

        # Figure out the number of bins to use
        if self.autoBin: nbins = autoBin(data, self.dataRange.x)
        else: nbins = self.nbins

        # nbins is unclamped, but
        # we don't allow < 10
        if nbins < 10:
            nbins = 10

        # Update the nbins property
        with props.skip(self, 'nbins', self.__name):
            self.nbins = nbins

        # We cache calculated bins and counts
        # for each combination of parameters,
        # as histogram calculation can take
        # time.
        hrange = (self.dataRange.xlo, self.dataRange.xhi)
        drange = (self.dataRange.xmin, self.dataRange.xmax)
        histkey = ((self.__opts.volumeDim, self.__opts.volume),
                   self.includeOutliers, hrange, drange, self.nbins)
        cached = self.__histCache.get(histkey, None)

        if cached is not None:
            histX, histY, nvals = cached
        else:
            histX, histY, nvals = histogram(data, self.nbins, hrange, drange,
                                            self.includeOutliers, True)
            self.__histCache.put(histkey, (histX, histY, nvals))

        self.__xdata = histX
        self.__ydata = histY
        self.__nvals = nvals

        status.update('Histogram for {} calculated.'.format(self.overlay.name))

        log.debug('Calculated histogram for overlay '
                  '{} (number of values: {}, number '
                  'of bins: {})'.format(self.overlay.name, self.__nvals,
                                        self.nbins))