diff --git a/fsl/data/__init__.py b/fsl/data/__init__.py index 7372461c5ab3705582c6f696d5c17bb07d3106d9..bb4e2bda970b806e6d807c9e4c853a62fb865418 100644 --- a/fsl/data/__init__.py +++ b/fsl/data/__init__.py @@ -11,10 +11,14 @@ models, constants, and other data-like things used throughout ``fslpy``. .. autosummary:: :nosignatures: + ~fsl.data.image.Nifti1 ~fsl.data.image.Image ~fsl.data.featimage.FEATImage + ~fsl.data.melodicimage.MelodicImage + ~fsl.data.tensorimage.TensorImage ~fsl.data.model.Model ~fsl.data.featresults + ~fsl.data.melodicresults ~fsl.data.atlases ~fsl.data.strings ~fsl.data.constants diff --git a/fsl/data/strings.py b/fsl/data/strings.py index eadd41cbb46feb1a66a7761392fdcbbc19bde0ad..a6d22107f9980a5beb2f16b45b5d58880d0eb9f9 100644 --- a/fsl/data/strings.py +++ b/fsl/data/strings.py @@ -395,10 +395,18 @@ labels = TypeDict({ 'CanvasSettingsPanel.lightbox' : 'Lightbox settings', 'OverlayInfoPanel.general' : 'General information', + 'OverlayInfoPanel.overlayType' : 'Displayed as', + 'OverlayInfoPanel.displaySpace' : 'Display space', 'OverlayInfoPanel.Nifti1.dimensions' : 'Dimensions', 'OverlayInfoPanel.Nifti1.transform' : 'Transform/space', 'OverlayInfoPanel.Nifti1.orient' : 'Orientation', + + 'OverlayInfoPanel.Nifti1.displaySpace.id' : 'Raw voxels', + 'OverlayInfoPanel.Nifti1.displaySpace.pixdim' : 'Scaled voxels', + 'OverlayInfoPanel.Nifti1.displaySpace.affine' : 'World coordinates', + 'OverlayInfoPanel.Nifti1.displaySpace.world' : 'World coordinates', + 'OverlayInfoPanel.Nifti1.displaySpace.custom' : 'Scaled voxels ({})', 'OverlayInfoPanel.Image' : 'NIFTI1 image', 'OverlayInfoPanel.FEATImage' : 'NIFTI1 image ' diff --git a/fsl/fsleyes/controls/overlayinfopanel.py b/fsl/fsleyes/controls/overlayinfopanel.py index c5f3a026671e173e097f33e05938c3c71cf8d3d6..afcbd49d7b66480092dd20137305a00d82ff8b5d 100644 --- a/fsl/fsleyes/controls/overlayinfopanel.py +++ b/fsl/fsleyes/controls/overlayinfopanel.py @@ -9,6 +9,8 @@ panel which displays information about the currently selected overlay. """ +import logging + import collections import wx @@ -16,11 +18,15 @@ import wx.html2 as wxhtml import numpy as np +import fsl.data.image as fslimage import fsl.data.strings as strings import fsl.data.constants as constants import fsl.fsleyes.panel as fslpanel +log = logging.getLogger(__name__) + + class OverlayInfoPanel(fslpanel.FSLEyesPanel): """An ``OverlayInfoPanel`` is a :class:`.FSLEyesPanel` which displays information about the currently selected overlay in a @@ -39,6 +45,7 @@ class OverlayInfoPanel(fslpanel.FSLEyesPanel): :class:`.Image` :meth:`__getImageInfo` :class:`.FEATImage` :meth:`__getFEATImageInfo` :class:`.MelodicImage` :meth:`__getMelodicImageInfo` + :class:`.TensorImage` :meth:`__getTensorImageInfo` :class:`.Model` :meth:`__getModelInfo` ====================== ============================= """ @@ -69,6 +76,7 @@ class OverlayInfoPanel(fslpanel.FSLEyesPanel): self.__currentOverlay = None self.__currentDisplay = None + self.__currentOpts = None self.__selectedOverlayChanged() self.SetMinSize((350, 500)) @@ -106,17 +114,17 @@ class OverlayInfoPanel(fslpanel.FSLEyesPanel): self.__info.SetPage('', '') self.__info.Refresh() return - - # Info for this overlay - # is already being shown - if overlay == self.__currentOverlay: - return if self.__currentDisplay is not None: - self.__currentDisplay.removeListener('name', self._name) + self.__currentDisplay.removeListener('name', self._name) + self.__currentDisplay.removeListener('overlayType', self._name) + + if self.__currentOpts is not None: + self.__currentOpts.removeListener('transform', self._name) self.__currenOverlay = None self.__currenDisplay = None + self.__currenOpts = None if overlay is not None: self.__currentOverlay = overlay @@ -125,16 +133,41 @@ class OverlayInfoPanel(fslpanel.FSLEyesPanel): self.__currentDisplay.addListener('name', self._name, self.__overlayNameChanged) - + self.__currentDisplay.addListener('overlayType', + self._name, + self.__selectedOverlayChanged) + + if isinstance(overlay, fslimage.Nifti1): + self.__currentOpts = self.__currentDisplay.getDisplayOpts() + + self.__currentOpts.addListener('transform', + self._name, + self.__overlayTransformChanged) + self.__updateInformation() + def __overlayTypeChanged(self, *a): + """Called when the :attr:`.Display.overlayType` for the current + overlay changes. Re-registers with the ``Display`` and + ``DisplayOpts`` instances associated with the overlay. + """ + self.__selectedOverlayChanged() + + def __overlayNameChanged(self, *a): """Called when the :attr:`.Display.name` for the current overlay changes. Updates the information display. """ self.__updateInformation() + + def __overlayTransformChanged(self, *a): + """Called when the :attr:`.Nifti1Opts.transform` for the current + overlay changes. Updates the information display. + """ + self.__updateInformation() + def __updateInformation(self): """Refreshes the information shown on this ``OverlayInfoPanel``. @@ -191,6 +224,23 @@ class OverlayInfoPanel(fslpanel.FSLEyesPanel): info.addSection(xformSect) info.addSection(orientSect) + displaySpace = strings.labels[self, + overlay, + 'displaySpace', + opts.transform] + + if opts.transform == 'custom': + dsImg = self._displayCtx.displaySpace + if isinstance(dsImg, fslimage.Nifti1): + dsDisplay = self._displayCtx.getDisplay(dsImg) + displaySpace = displaySpace.format(dsDisplay.name) + else: + log.warn('{} transform ({}) seems to be out ' + 'of date (display space: {})'.format( + overlay, + opts.transform, + self._displayCtx.displaySpace)) + info.addInfo(strings.labels[self, 'dataSource'], overlay.dataSource, section=generalSect) @@ -206,6 +256,14 @@ class OverlayInfoPanel(fslpanel.FSLEyesPanel): info.addInfo(strings.nifti['intent_name'], hdr['intent_name'], section=generalSect) + + info.addInfo(strings.labels[self, 'overlayType'], + strings.choices[display, 'overlayType'][ + display.overlayType], + section=generalSect) + info.addInfo(strings.labels[self, 'displaySpace'], + displaySpace, + section=generalSect) info.addInfo(strings.nifti['dimensions'], '{}D'.format(len(overlay.shape)), @@ -354,6 +412,13 @@ class OverlayInfoPanel(fslpanel.FSLEyesPanel): def __getTensorImageInfo(self, overlay, display): + """Creates and returns an :class:`OverlayInfo` object containing + information about the given :class:`.TensorImage` overlay. + + :arg overlay: A :class:`.TensorImage` instance. + :arg display: The :class:`.Display` instance assocated with the + ``TensorImage``. + """ return self.__getImageInfo(overlay, display) @@ -364,7 +429,7 @@ class OverlayInfoPanel(fslpanel.FSLEyesPanel): lines = [] - lines.append('<table border="0">') + lines.append('<table border="0" style="font-size: small;>') for rowi in range(array.shape[0]): diff --git a/fsl/fsleyes/overlay.py b/fsl/fsleyes/overlay.py index fd980b30e0fffdd089dc0de4d7e219092fce7435..f1411e49170e1dd5c49d68101d581d4d2967c9e3 100644 --- a/fsl/fsleyes/overlay.py +++ b/fsl/fsleyes/overlay.py @@ -41,6 +41,8 @@ Currently (``fslpy`` version |version|) the only overlay types in existence ~fsl.data.image.Image ~fsl.data.featimage.FEATImage + ~fsl.data.melodicimage.MelodicImage + ~fsl.data.tensorimage.TensorImage ~fsl.data.model.Model