diff --git a/fsl/data/image.py b/fsl/data/image.py index 81bbf2e84e27c1194682cfa007f01930a7cfbb0d..15326071ddd2c68ebcb6b7780b376a66a6ed8910 100644 --- a/fsl/data/image.py +++ b/fsl/data/image.py @@ -9,7 +9,6 @@ See the :mod:`fsl.data.imageio` module for image loading/saving functionality. - """ import logging @@ -52,7 +51,7 @@ class Image(props.HasProperties): for transforming real world coordinates into voxel coordinates. - :ivar imageFile: The name of the file that the image was loaded from. + :ivar dataSource: The name of the file that the image was loaded from. :ivar tempFile: The name of the temporary file which was created (in the event that the image was large and was gzipped - @@ -105,9 +104,9 @@ class Image(props.HasProperties): via the :meth:`loadData` method. """ - self.nibImage = None - self.imageFile = None - self.tempFile = None + self.nibImage = None + self.dataSource = None + self.tempFile = None if header is not None: header = header.copy() @@ -117,16 +116,16 @@ class Image(props.HasProperties): nibImage, filename = iio.loadImage(iio.addExt(image)) self.nibImage = nibImage - self.imageFile = image + self.dataSource = image # if the returned file name is not the same as # the provided file name, that means that the # image was opened from a temporary file if filename != image: - self.name = iio.removeExt(op.basename(self.imageFile)) + self.name = iio.removeExt(op.basename(self.dataSource)) self.tempFile = nibImage.get_filename() else: - self.name = iio.removeExt(op.basename(self.imageFile)) + self.name = iio.removeExt(op.basename(self.dataSource)) self.saved = True @@ -255,7 +254,7 @@ class Image(props.HasProperties): """Return a string representation of this :class:`Image`.""" return '{}({}, {})'.format(self.__class__.__name__, self.name, - self.imageFile) + self.dataSource) def __repr__(self): diff --git a/fsl/data/imageio.py b/fsl/data/imageio.py index 58d23169c7ae9032840a8cab9f7593842ed70e55..662272c469b84e1acd38fef6c80fafafec35d983 100644 --- a/fsl/data/imageio.py +++ b/fsl/data/imageio.py @@ -281,13 +281,13 @@ def saveImage(image, imageList=None, fromDir=None): lastDir = getattr(saveImage, 'lastDir', None) if lastDir is None: - if image.imageFile is None: lastDir = os.getcwd() - else: lastDir = op.dirname(image.imageFile) + if image.dataSource is None: lastDir = os.getcwd() + else: lastDir = op.dirname(image.dataSource) # TODO make image.name safe (spaces to # underscores, filter non-alphanumeric) - if image.imageFile is None: filename = image.name - else: filename = op.basename(image.imageFile) + if image.dataSource is None: filename = image.name + else: filename = op.basename(image.dataSource) filename = removeExt(filename) @@ -338,7 +338,7 @@ def saveImage(image, imageList=None, fromDir=None): log.debug('Saving image ({}) to {}'.format(image, path)) nib.save(nibImage, path) - image.imageFile = path + image.dataSource = path except Exception as e: diff --git a/fsl/data/model.py b/fsl/data/model.py index 31327fa2445d961a6e3272c898aa1598a5e45580..86370c79afc0caebaac3adc189c597f5a7e5ed8c 100644 --- a/fsl/data/model.py +++ b/fsl/data/model.py @@ -5,8 +5,8 @@ # Author: Paul McCarthy <pauldmccarthy@gmail.com> # - -import numpy as np +import os.path as op +import numpy as np def loadVTKPolydataFile(infile): @@ -62,6 +62,12 @@ class PolygonModel(object): if np.any(lengths != 3): raise RuntimeError('All polygons in VTK file must be ' 'triangles ({})'.format(infile)) + + self.name = op.basename(infile) + self.dataSource = infile + else: + self.name = 'Model' + self.dataSource = 'Model' if indices is None: indices = np.arange(data.shape[0], dtype=np.uint32) diff --git a/fsl/fslview/controls/overlaylistpanel.py b/fsl/fslview/controls/overlaylistpanel.py index 17e453a4b57c3709a458064513f4d36d4085b99b..c8e8c399fc5a63a53a50a4d6ab85237dc8948f35 100644 --- a/fsl/fslview/controls/overlaylistpanel.py +++ b/fsl/fslview/controls/overlaylistpanel.py @@ -54,7 +54,7 @@ class ListItemWidget(wx.Panel): self.name, self._saveStateChanged) else: - log.warn('No support for non-volumetric overlays yet') + log.warn('No save button support for non-volumetric overlays') self.saveButton.Enable(False) self.saveButton.Bind(wx.EVT_BUTTON, self._onSaveButton) @@ -81,8 +81,12 @@ class ListItemWidget(wx.Panel): def _saveStateChanged(self, *a): - idx = self.listBox.IndexOf(self.overlay) + if not isinstance(self.overlay, fslimage.Image): + return + + idx = self.listBox.IndexOf(self.overlay) + self.saveButton.Enable(not self.overlay.saved) if self.overlay.saved: @@ -192,8 +196,8 @@ class OverlayListPanel(fslpanel.FSLViewPanel): def _overlayNameChanged(self, value, valid, overlay, propName): - idx = self._displayCtx.getOverlayOrder( overlay) - display = self._displayCtx.getDisplay(overlay) + idx = self._displayCtx.getOverlayOrder(overlay) + display = self._displayCtx.getDisplay( overlay) name = display.name if name is None: @@ -218,12 +222,7 @@ class OverlayListPanel(fslpanel.FSLViewPanel): name = display.name if name is None: name = '' - if isinstance(overlay, fslimage.Image): - tooltip = overlay.imageFile - - else: - log.warn('No support for non-volumetric overlays yet') - tooltip = 'Non-volumetric overlay - I don\'t know what to do' + tooltip = overlay.dataSource self._listBox.Append(name, overlay, tooltip) diff --git a/fsl/fslview/overlay.py b/fsl/fslview/overlay.py index 9f8d665a91ea241e1f506aa98c1d320c399625e0..407f058f24f88a028ffb3e80c59e8ce11be54578 100644 --- a/fsl/fslview/overlay.py +++ b/fsl/fslview/overlay.py @@ -4,7 +4,12 @@ # # Author: Paul McCarthy <pauldmccarthy@gmail.com> # +"""This module defines the :class:`OverlayList` class, which is a simple but +fundamental class in FSLView - it is a container for all displayed overlays. +Only one ``OverlayList`` ever exists, and it is shared throughout the entire +application. +""" import logging @@ -27,12 +32,23 @@ class OverlayList(props.HasProperties): :attr:`overlays` property, allowing the :class:`OverlayList` to be used as if it were a list itself. + There are no restrictions on the type of objects which may be contained + in the ``OverlayList``, but all objects must have a few attributes: - An overlay object must have an attribute called ``name``. + - ``name`` ... + + - ``dataSoruce`` .. """ - overlays = props.List() + def __validateOverlay(self, atts, overlay): + return (hasattr(overlay, 'name') and + hasattr(overlay, 'dataSource')) + + + overlays = props.List( + listType=props.Object(allowInvalid=False, + validateFunc=__validateOverlay)) """A list of overlay objects to be displayed""" diff --git a/fsl/fslview/views/canvaspanel.py b/fsl/fslview/views/canvaspanel.py index 5f213a9fe00ee8b3c0c2383de237adf2362cc1a6..a42cbe481fd084cc2d3c7cb5da16766cc2fcd0eb 100644 --- a/fsl/fslview/views/canvaspanel.py +++ b/fsl/fslview/views/canvaspanel.py @@ -135,7 +135,7 @@ def _takeScreenShot(overlayList, displayCtx, canvas): continue display = displayCtx.getDisplay(overlay) - fname = overlay.imageFile + fname = overlay.dataSource # Skip invisible/unsaved/in-memory images if not (display.enabled and overlay.saved): diff --git a/fsl/tools/fslview_parseargs.py b/fsl/tools/fslview_parseargs.py index 7be92e8231ff804a46b26c0051ea135dab495b70..fbcf7fbcf0e5b9884ef5cec14e2c19c8b52c741b 100644 --- a/fsl/tools/fslview_parseargs.py +++ b/fsl/tools/fslview_parseargs.py @@ -884,7 +884,9 @@ def applyOverlayArgs(args, overlayList, displayCtx, **kwargs): # per-overlay display arguments for i, overlay in enumerate(overlayList): - display = displayCtx.getDisplay(overlay) + display = displayCtx.getDisplay(overlay) + display.name = overlay.name + _applyArgs(args.overlays[i], display) # Retrieve the DisplayOpts instance