From b0c69ede51f92b8de115c9850d567fb83246deb8 Mon Sep 17 00:00:00 2001 From: Paul McCarthy <pauld.mccarthy@gmail.com> Date: Wed, 7 Jan 2015 17:23:20 +0000 Subject: [PATCH] Move strings module from fsl.fslview into fsl.data package, as it needs to be available to non-fslview things. Moved orientation constants from fsl.data.image to (new module) fsl.data.constants, so circular imports are not necessary. There could still be some bugs lurking .. --- fsl/data/constants.py | 25 +++ fsl/data/image.py | 43 ++---- fsl/data/imageio.py | 92 +++++------ fsl/data/strings.py | 210 ++++++++++++++++++++++++++ fsl/fslview/controls/locationpanel.py | 4 +- fsl/fslview/displaycontext.py | 19 ++- fsl/fslview/frame.py | 9 +- fsl/fslview/gl/colourbarcanvas.py | 8 +- fsl/fslview/layouts.py | 6 +- fsl/fslview/profiles/__init__.py | 7 +- fsl/fslview/strings.py | 168 --------------------- fsl/fslview/views/canvaspanel.py | 9 +- fsl/fslview/views/orthopanel.py | 18 +-- fsl/fslview/views/spacepanel.py | 10 +- fsl/tools/render.py | 14 +- fsl/utils/typedict.py | 22 ++- 16 files changed, 373 insertions(+), 291 deletions(-) create mode 100644 fsl/data/constants.py create mode 100644 fsl/data/strings.py delete mode 100644 fsl/fslview/strings.py diff --git a/fsl/data/constants.py b/fsl/data/constants.py new file mode 100644 index 000000000..d2fa0bfde --- /dev/null +++ b/fsl/data/constants.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python +# +# constants.py - +# +# Author: Paul McCarthy <pauldmccarthy@gmail.com> +# + +# Constants which represent the orientation +# of an axis, in either voxel or world space. +ORIENT_UNKNOWN = -1 +ORIENT_L2R = 0 +ORIENT_R2L = 1 +ORIENT_P2A = 2 +ORIENT_A2P = 3 +ORIENT_I2S = 4 +ORIENT_S2I = 5 + + +# Constants from the NIFTI1 specification that define +# the 'space' in which an image is assumed to be. +NIFTI_XFORM_UNKNOWN = 0 +NIFTI_XFORM_SCANNER_ANAT = 1 +NIFTI_XFORM_ALIGNED_ANAT = 2 +NIFTI_XFORM_TALAIRACH = 3 +NIFTI_XFORM_MNI_152 = 4 diff --git a/fsl/data/image.py b/fsl/data/image.py index 58b9667c2..254083360 100644 --- a/fsl/data/image.py +++ b/fsl/data/image.py @@ -23,30 +23,12 @@ import props import fsl.utils.transform as transform import fsl.data.imageio as iio +import fsl.data.constants as constants log = logging.getLogger(__name__) -# Constants which represent the orientation -# of an axis, in either voxel or world space. -ORIENT_UNKNOWN = -1 -ORIENT_L2R = 0 -ORIENT_R2L = 1 -ORIENT_P2A = 2 -ORIENT_A2P = 3 -ORIENT_I2S = 4 -ORIENT_S2I = 5 - - -# Constants from the NIFTI1 specification that define -# the 'space' in which an image is assumed to be. -NIFTI_XFORM_UNKNOWN = 0 -NIFTI_XFORM_SCANNER_ANAT = 1 -NIFTI_XFORM_ALIGNED_ANAT = 2 -NIFTI_XFORM_TALAIRACH = 3 -NIFTI_XFORM_MNI_152 = 4 - class Image(props.HasProperties): """Class which represents a 3D/4D image. Internally, the image is @@ -251,8 +233,8 @@ class Image(props.HasProperties): sform_code = self.nibImage.get_header()['sform_code'] # Invalid values - if sform_code > 4: code = NIFTI_XFORM_UNKNOWN - elif sform_code < 0: code = NIFTI_XFORM_UNKNOWN + if sform_code > 4: code = constants.NIFTI_XFORM_UNKNOWN + elif sform_code < 0: code = constants.NIFTI_XFORM_UNKNOWN # All is well else: code = sform_code @@ -283,12 +265,12 @@ class Image(props.HasProperties): to superior). """ - if self.getXFormCode() == NIFTI_XFORM_UNKNOWN: + if self.getXFormCode() == constants.NIFTI_XFORM_UNKNOWN: return -1 - if axis == 0: return ORIENT_L2R - elif axis == 1: return ORIENT_P2A - elif axis == 2: return ORIENT_I2S + if axis == 0: return constants.ORIENT_L2R + elif axis == 1: return constants.ORIENT_P2A + elif axis == 2: return constants.ORIENT_I2S else: return -1 @@ -301,16 +283,17 @@ class Image(props.HasProperties): of the return value. """ - if self.getXFormCode() == NIFTI_XFORM_UNKNOWN: + if self.getXFormCode() == constants.NIFTI_XFORM_UNKNOWN: return -1 # the aff2axcodes returns one code for each # axis in the image array (i.e. in voxel space), # which denotes the real world direction - code = nib.orientations.aff2axcodes(self.nibImage.get_affine(), - ((ORIENT_R2L, ORIENT_L2R), - (ORIENT_A2P, ORIENT_P2A), - (ORIENT_S2I, ORIENT_I2S)))[axis] + code = nib.orientations.aff2axcodes( + self.nibImage.get_affine(), + ((constants.ORIENT_R2L, constants.ORIENT_L2R), + (constants.ORIENT_A2P, constants.ORIENT_P2A), + (constants.ORIENT_S2I, constants.ORIENT_I2S)))[axis] return code diff --git a/fsl/data/imageio.py b/fsl/data/imageio.py index 88cb0a9f7..f2f96626f 100644 --- a/fsl/data/imageio.py +++ b/fsl/data/imageio.py @@ -5,15 +5,16 @@ # Author: Paul McCarthy <pauldmccarthy@gmail.com> # -import logging -import os -import os.path as op -import subprocess as sp -import tempfile +import logging +import os +import os.path as op +import subprocess as sp +import tempfile -import nibabel as nib +import nibabel as nib -import image as fslimage +import fsl.data.strings as strings +import image as fslimage log = logging.getLogger(__name__) @@ -29,13 +30,11 @@ if if the ``allowedExts`` parameter is not passed to any of the functions in this module. """ -ALLOWED_EXTENSIONS = [';'.join(ALLOWED_EXTENSIONS)] + ALLOWED_EXTENSIONS -EXTENSION_DESCRIPTIONS = ['All supported files' - 'Compressed NIFTI1 images', +EXTENSION_DESCRIPTIONS = ['Compressed NIFTI1 images', 'NIFTI1 images', 'ANALYZE75 images', 'NIFTI1/ANALYZE75 headers', - 'Compressed NIFTI1/ANALYZE75 images' + 'Compressed NIFTI1/ANALYZE75 images', 'Compressed images'] """Descriptions for each of the extensions in :data:`ALLOWED_EXTENSIONS`. """ @@ -58,9 +57,12 @@ def makeWildcard(allowedExts=None): else: descs = allowedExts - exts = ['*{}'.format(ext) for ext in allowedExts] + exts = ['*{}'.format(ext) for ext in allowedExts] + exts = [';'.join(exts)] + exts + descs = ['All supported files'] + descs + wcParts = ['|'.join((desc, ext)) for (desc, ext) in zip(descs, exts)] - + return '|'.join(wcParts) @@ -77,7 +79,7 @@ def isSupported(filename, allowedExts=None): if allowedExts is None: allowedExts = ALLOWED_EXTENSIONS - return any(map(lambda ext: filename.endswith(ext, allowedExts))) + return any(map(lambda ext: filename.endswith(ext), allowedExts)) def removeExt(filename, allowedExts=None): @@ -208,10 +210,8 @@ def loadImage(filename): unzipped = os.fdopen(unzipped) - msg = '{} is a large file ({} MB) - decompressing ' \ - 'to {}, to allow memory mapping...'.format(realFilename, - mbytes, - filename) + msg = strings.messages['imageio.loadImage.decompress'] + msg = msg.format(realFilename, mbytes, filename) if not haveGui: log.info(msg) @@ -297,14 +297,11 @@ def saveImage(image, imageList=None, fromDir=None): saveLastDir = True dlg = wx.FileDialog(app.GetTopWindow(), - message='Save image file', + message=strings.titles['imageio.saveImage.dialog'], defaultDir=fromDir, defaultFile=filename, - wildcard=makeWildcard(), style=wx.FD_SAVE) - dlg.SetFilterIndex(ALLOWED_EXTENSIONS.index(DEFAULT_EXTENSION)) - if dlg.ShowModal() != wx.ID_OK: return False if saveLastDir: saveImage.lastDir = lastDir @@ -312,37 +309,48 @@ def saveImage(image, imageList=None, fromDir=None): path = dlg.GetPath() nibImage = image.nibImage + if not isSupported(path): + path = addExt(path, False) + # this is an image which has been # loaded from a file, and ungzipped # to a temporary location - if image.tempFile is not None: + try: + if image.tempFile is not None: - # if selected path is same as original path, - # save to both temp file and to path + # if selected path is same as original path, + # save to both temp file and to path - # else, if selected path is different from - # original path, save to temp file and to - # new path, and update the path + # else, if selected path is different from + # original path, save to temp file and to + # new path, and update the path - # actually, the two behaviours just described - # are identical - pass + # actually, the two behaviours just described + # are identical + pass + # TODO handle error - # TODO handle error + # this is just a normal image + # which has been loaded from + # a file, or an in-memory image + else: - # this is just a normal image - # which has been loaded from - # a file, or an in-memory image - else: + log.debug('Saving image ({}) to {}'.format(image, path)) - log.debug('Saving image ({}) to {}'.format(image, path)) - - nib.save(nibImage, path) - image.imageFile = path + nib.save(nibImage, path) + image.imageFile = path + + except Exception as e: - image.saved = True + msg = strings.messages['imageio.saveImage.error'].format(e.msg) + log.warn(msg) + wx.MessageDialog(app.GetTopWindow(), + message=msg, + style=wx.OK | wx.ICON_ERROR) + return + image.saved = True def addImages(imageList, fromDir=None, addToEnd=True): @@ -389,7 +397,7 @@ def addImages(imageList, fromDir=None, addToEnd=True): saveLastDir = True dlg = wx.FileDialog(app.GetTopWindow(), - message='Open image file', + message=strings.titles['imageio.addImages.dialog'], defaultDir=fromDir, wildcard=makeWildcard(), style=wx.FD_OPEN | wx.FD_MULTIPLE) diff --git a/fsl/data/strings.py b/fsl/data/strings.py new file mode 100644 index 000000000..5f154593f --- /dev/null +++ b/fsl/data/strings.py @@ -0,0 +1,210 @@ +#!/usr/bin/env python +# +# strings.py - +# +# Author: Paul McCarthy <pauldmccarthy@gmail.com> +# + +from fsl.utils.typedict import TypeDict +import fsl.data.constants as constants + +messages = TypeDict({ + + 'imageio.saveImage.error' : 'An error occurred saving the file. ' + 'Details: {}', + + 'imageio.loadImage.decompress' : '{} is a large file ({} MB) - ' + 'decompressing to {}, to allow memory ' + 'mapping...', +}) + + + +titles = TypeDict({ + 'imageio.saveImage.dialog' : 'Save image file', + 'imageio.addImages.dialog' : 'Open image files', + + 'OrthoPanel' : 'Ortho View', + 'LightBoxPanel' : 'Lightbox View', + 'TimeSeriesPanel' : 'Time series', + 'SpacePanel' : 'Space inspector', + +}) + + +actions = TypeDict({ + + 'OpenFileAction' : 'Add image file', + 'OpenStandardAction' : 'Add standard', + 'CopyImageAction' : 'Copy image', + 'SaveImageAction' : 'Save image', + 'LoadColourMapAction' : 'Load custom colour map', + + 'CanvasPanel.screenshot' : 'Take screenshot', + 'CanvasPanel.toggleColourBar' : 'Show/hide colour bar', + 'CanvasPanel.toggleImageList' : 'Show/hide image list', + 'CanvasPanel.toggleDisplayProperties' : 'Show/hide display properties', + 'CanvasPanel.toggleLocationPanel' : 'Show/hide location panel', + 'CanvasPanel.toggleCanvasProperties' : 'Show/hide canvas properties', + + + 'OrthoViewProfile.centreCursor' : 'Centre cursor', + 'OrthoViewProfile.resetZoom' : 'Reset zoom', + + + 'OrthoEditProfile.undo' : 'Undo', + 'OrthoEditProfile.redo' : 'Redo', + 'OrthoEditProfile.fillSelection' : 'Fill selected region', + 'OrthoEditProfile.clearSelection' : 'Clear selection', + 'OrthoEditProfile.createMaskFromSelection' : 'Create mask from ' + 'selected region', + 'OrthoEditProfile.createROIFromSelection' : 'Create ROI from ' + 'selected region', +}) + +labels = TypeDict({ + 'LocationPanel.worldLabel' : 'World location (mm)', + 'LocationPanel.voxelLabel' : 'Voxel location', + 'LocationPanel.volumeLabel' : 'Volume', + 'LocationPanel.spaceLabel' : 'Space', + 'LocationPanel.outOfBounds' : 'Out of bounds', +}) + + +properties = TypeDict({ + + 'Profile.mode' : 'Mode', + + 'CanvasPanel.showCursor' : 'Show location cursor', + 'CanvasPanel.syncLocation' : 'Sync location', + 'CanvasPanel.syncImageOrder' : 'Sync image order', + 'CanvasPanel.syncVolume' : 'Sync volume', + 'CanvasPanel.profile' : 'Profile', + 'CanvasPanel.zoom' : 'Zoom', + 'CanvasPanel.colourBarLocation' : 'Colour bar location', + 'CanvasPanel.colourBarLabelSide' : 'Colour bar label side', + + 'LightBoxPanel.zax' : 'Z axis', + 'LightBoxPanel.highlightSlice' : 'Highlight slice', + 'LightBoxPanel.showGridLines' : 'Show grid lines', + 'LightBoxPanel.sliceSpacing' : 'Slice spacing', + 'LightBoxPanel.zrange' : 'Z range', + + 'OrthoPanel.showXCanvas' : 'Show X canvas', + 'OrthoPanel.showYCanvas' : 'Show Y canvas', + 'OrthoPanel.showZCanvas' : 'Show Z canvas', + 'OrthoPanel.showLabels' : 'Show labels', + 'OrthoPanel.layout' : 'Layout', + 'OrthoPanel.xzoom' : 'X zoom', + 'OrthoPanel.yzoom' : 'Y zoom', + 'OrthoPanel.zzoom' : 'Z zoom', + + + 'OrthoEditProfile.selectionMode' : 'Selection mode', + 'OrthoEditProfile.selectionSize' : 'Selection size', + 'OrthoEditProfile.selectionIs3D' : '3D selection', + 'OrthoEditProfile.fillValue' : 'Fill value', + 'OrthoEditProfile.intensityThres' : 'Intensity threshold', + 'OrthoEditProfile.localFill' : 'Only select adjacent voxels', + 'OrthoEditProfile.searchRadius' : 'Limit search to radius (mm)', + + + 'ImageDisplay.name' : 'Image name', + 'ImageDisplay.enabled' : 'Enabled', + 'ImageDisplay.displayRange' : 'Display range', + 'ImageDisplay.alpha' : 'Opacity', + 'ImageDisplay.clipLow' : 'Low clipping', + 'ImageDisplay.clipHigh' : 'High clipping', + 'ImageDisplay.interpolation' : 'Interpolation', + 'ImageDisplay.resolution' : 'Resolution', + 'ImageDisplay.volume' : 'Volume', + 'ImageDisplay.syncVolume' : 'Synchronise volume', + 'ImageDisplay.transform' : 'Image transform', + 'ImageDisplay.imageType' : 'Image data type', + 'ImageDisplay.cmap' : 'Colour map', +}) + + +profiles = TypeDict({ + 'CanvasPanel.view' : 'View', + 'OrthoPanel.edit' : 'Edit', +}) + +modes = TypeDict({ + ('OrthoViewProfile', 'nav') : 'Navigate', + ('OrthoViewProfile', 'pan') : 'Pan', + ('OrthoViewProfile', 'zoom') : 'Zoom', + + ('OrthoEditProfile', 'nav') : 'Navigate', + ('OrthoEditProfile', 'pan') : 'Pan', + ('OrthoEditProfile', 'zoom') : 'Zoom', + ('OrthoEditProfile', 'sel') : 'Select', + ('OrthoEditProfile', 'desel') : 'Deselect', + ('OrthoEditProfile', 'selint') : 'Select by intensity', + +}) + + + +choices = TypeDict({ + + 'CanvasPanel.colourBarLocation.top' : 'Top', + 'CanvasPanel.colourBarLocation.bottom' : 'Bottom', + 'CanvasPanel.colourBarLocation.left' : 'Left', + 'CanvasPanel.colourBarLocation.right' : 'Right', + + 'ColourBarCanvas.orientation.horizontal' : 'Horizontal', + 'ColourBarCanvas.orientation.vertical' : 'Vertical', + + 'ColourBarCanvas.labelSide.top-left' : 'Top / Left', + 'ColourBarCanvas.labelSide.bottom-right' : 'Bottom / Right', + + 'ImageDisplay.displayRange.min' : 'Min.', + 'ImageDisplay.displayRange.max' : 'Max.', + + 'ImageDisplay.transform.affine' : 'Use qform/sform transformation matrix', + 'ImageDisplay.transform.pixdim' : 'Use pixdims only', + 'ImageDisplay.transform.id' : 'Do not use qform/sform or pixdims', + + 'ImageDisplay.interpolation.none' : 'No interpolation', + 'ImageDisplay.interpolation.linear' : 'Linear interpolation', + 'ImageDisplay.interpolation.spline' : 'Spline interpolation', +}) + + +anatomy = TypeDict({ + + ('Image', 'lowlong', constants.ORIENT_A2P) : 'Anterior', + ('Image', 'lowlong', constants.ORIENT_P2A) : 'Posterior', + ('Image', 'lowlong', constants.ORIENT_L2R) : 'Left', + ('Image', 'lowlong', constants.ORIENT_R2L) : 'Right', + ('Image', 'lowlong', constants.ORIENT_I2S) : 'Inferior', + ('Image', 'lowlong', constants.ORIENT_S2I) : 'Superior', + ('Image', 'lowlong', constants.ORIENT_UNKNOWN) : 'Unknown', + ('Image', 'highlong', constants.ORIENT_A2P) : 'Posterior', + ('Image', 'highlong', constants.ORIENT_P2A) : 'Anterior', + ('Image', 'highlong', constants.ORIENT_L2R) : 'Right', + ('Image', 'highlong', constants.ORIENT_R2L) : 'Left', + ('Image', 'highlong', constants.ORIENT_I2S) : 'Superior', + ('Image', 'highlong', constants.ORIENT_S2I) : 'Inferior', + ('Image', 'highlong', constants.ORIENT_UNKNOWN) : 'Unknown', + ('Image', 'lowshort', constants.ORIENT_A2P) : 'A', + ('Image', 'lowshort', constants.ORIENT_P2A) : 'P', + ('Image', 'lowshort', constants.ORIENT_L2R) : 'L', + ('Image', 'lowshort', constants.ORIENT_R2L) : 'R', + ('Image', 'lowshort', constants.ORIENT_I2S) : 'I', + ('Image', 'lowshort', constants.ORIENT_S2I) : 'S', + ('Image', 'lowshort', constants.ORIENT_UNKNOWN) : '?', + ('Image', 'highshort', constants.ORIENT_A2P) : 'P', + ('Image', 'highshort', constants.ORIENT_P2A) : 'A', + ('Image', 'highshort', constants.ORIENT_L2R) : 'R', + ('Image', 'highshort', constants.ORIENT_R2L) : 'L', + ('Image', 'highshort', constants.ORIENT_I2S) : 'S', + ('Image', 'highshort', constants.ORIENT_S2I) : 'I', + ('Image', 'highshort', constants.ORIENT_UNKNOWN) : '?', + ('Image', 'space', constants.NIFTI_XFORM_UNKNOWN) : 'Unknown', + ('Image', 'space', constants.NIFTI_XFORM_SCANNER_ANAT) : 'Scanner anatomical', + ('Image', 'space', constants.NIFTI_XFORM_ALIGNED_ANAT) : 'Aligned anatomical', + ('Image', 'space', constants.NIFTI_XFORM_TALAIRACH) : 'Talairach', + ('Image', 'space', constants.NIFTI_XFORM_MNI_152) : 'MNI152', +}) diff --git a/fsl/fslview/controls/locationpanel.py b/fsl/fslview/controls/locationpanel.py index 80ff567c4..2fd06b3c7 100644 --- a/fsl/fslview/controls/locationpanel.py +++ b/fsl/fslview/controls/locationpanel.py @@ -24,7 +24,7 @@ import numpy as np import props import fsl.utils.transform as transform -import fsl.fslview.strings as strings +import fsl.data.strings as strings import fsl.fslview.panel as fslpanel import imageselectpanel as imageselect @@ -335,7 +335,7 @@ class LocationPanel(fslpanel.FSLViewPanel): # Update the label which # displays the image space - spaceLabel = strings.labels['Image', 'space', image.getXFormCode()] + spaceLabel = strings.anatomy['Image', 'space', image.getXFormCode()] spaceLabel = '{} {}'.format(spaceLabel, strings.labels[self, 'spaceLabel']) self._spaceLabel.SetLabel(spaceLabel) diff --git a/fsl/fslview/displaycontext.py b/fsl/fslview/displaycontext.py index 05180137b..34f6cdefe 100644 --- a/fsl/fslview/displaycontext.py +++ b/fsl/fslview/displaycontext.py @@ -6,18 +6,16 @@ # import sys -import collections -from collections import OrderedDict - -import numpy as np +import numpy as np import props import fsl.data.image as fslimage import fsl.utils.transform as transform -import fsl.fslview.strings as strings +import fsl.data.strings as strings import fsl.fslview.colourmaps as fslcm + class ImageDisplay(props.SyncableHasProperties): """A class which describes how an :class:`~fsl.data.image.Image` should be displayed. @@ -39,7 +37,8 @@ class ImageDisplay(props.SyncableHasProperties): displayRange = props.Bounds( ndims=1, editLimits=True, - labels=strings.labels['ImageDisplay', 'displayRange', 'labels']) + labels=[strings.choices['ImageDisplay.displayRange.min'], + strings.choices['ImageDisplay.displayRange.max']]) """Image values which map to the minimum and maximum colour map colours.""" @@ -76,7 +75,9 @@ class ImageDisplay(props.SyncableHasProperties): transform = props.Choice( ('affine', 'pixdim', 'id'), - labels=strings.labels['ImageDisplay', 'transform', 'labels'], + labels=[strings.choices['ImageDisplay.transform.affine'], + strings.choices['ImageDisplay.transform.pixdim'], + strings.choices['ImageDisplay.transform.id']], default='pixdim') """This property defines how the image should be transformd into the display coordinate system. @@ -95,7 +96,9 @@ class ImageDisplay(props.SyncableHasProperties): interpolation = props.Choice( ('none', 'linear', 'spline'), - labels=strings.labels['ImageDisplay', 'interpolation', 'labels']) + labels=[strings.choices['ImageDisplay.interpolation.none'], + strings.choices['ImageDisplay.interpolation.linear'], + strings.choices['ImageDisplay.interpolation.spline']]) """How the value shown at a real world location is derived from the corresponding voxel value(s). 'No interpolation' is equivalent to nearest neighbour interpolation. diff --git a/fsl/fslview/frame.py b/fsl/fslview/frame.py index f802f5213..8db7da3d1 100644 --- a/fsl/fslview/frame.py +++ b/fsl/fslview/frame.py @@ -49,9 +49,10 @@ log = logging.getLogger(__name__) import wx import wx.aui as aui +import fsl.data.strings as strings + import views import actions -import strings import displaycontext @@ -110,7 +111,7 @@ class FSLViewFrame(wx.Frame): allowing the user to configure the view. """ - title = strings.labels[panelCls] + title = strings.titles[panelCls] childDC = displaycontext.DisplayContext(self._imageList, self._displayCtx) panel = panelCls(self._centrePane, @@ -240,14 +241,14 @@ class FSLViewFrame(wx.Frame): actionz = actions .listGlobalActions() for action in actionz: - menuItem = fileMenu.Append(wx.ID_ANY, strings.labels[action]) + menuItem = fileMenu.Append(wx.ID_ANY, strings.actions[action]) actionObj = action(self._imageList, self._displayCtx) actionObj.bindToWidget(self, wx.EVT_MENU, menuItem) for viewPanel in viewPanels: - viewAction = viewMenu.Append(wx.ID_ANY, strings.labels[viewPanel]) + viewAction = viewMenu.Append(wx.ID_ANY, strings.titles[viewPanel]) self.Bind(wx.EVT_MENU, lambda ev, vp=viewPanel: self.addViewPanel(vp), viewAction) diff --git a/fsl/fslview/gl/colourbarcanvas.py b/fsl/fslview/gl/colourbarcanvas.py index e40548df8..45792404e 100644 --- a/fsl/fslview/gl/colourbarcanvas.py +++ b/fsl/fslview/gl/colourbarcanvas.py @@ -23,7 +23,7 @@ import numpy as np import props import fsl.utils.colourbarbitmap as cbarbmp -import fsl.fslview.strings as strings +import fsl.data.strings as strings class ColourBarCanvas(props.HasProperties): @@ -44,13 +44,15 @@ class ColourBarCanvas(props.HasProperties): orientation = props.Choice( ('horizontal', 'vertical'), - labels=strings.labels['ColourBarCanvas', 'orientation', 'labels']) + labels=[strings.choices['ColourBarCanvas.orientation.horizontal'], + strings.choices['ColourBarCanvas.orientation.vertical']]) """Whether the colour bar should be vertical or horizontal. """ labelSide = props.Choice( ('top-left', 'bottom-right'), - labels=strings.labels['ColourBarCanvas', 'labelSide', 'labels']) + labels=[strings.choices['ColourBarCanvas.labelSide.top-left'], + strings.choices['ColourBarCanvas.labelSide.bottom-right']]) """Whether the colour bar labels should be on the top/left, or bottom/right of the colour bar (depending upon whether the colour bar orientation is horizontal/vertical). diff --git a/fsl/fslview/layouts.py b/fsl/fslview/layouts.py index fe1a732a5..b87dd0ebc 100644 --- a/fsl/fslview/layouts.py +++ b/fsl/fslview/layouts.py @@ -12,7 +12,7 @@ import props import fsl.utils.typedict as td -import fsl.fslview.strings as strings +import fsl.data.strings as strings from fsl.fslview.profiles.orthoviewprofile import OrthoViewProfile from fsl.fslview.profiles.orthoeditprofile import OrthoEditProfile @@ -23,7 +23,7 @@ from fsl.fslview.displaycontext import ImageDisplay def widget(name, labelCls, *args, **kwargs): return props.Widget(name, - label=strings.labels[labelCls, name], + label=strings.properties[labelCls, name], *args, **kwargs) @@ -32,7 +32,7 @@ def actionButton(name, labelCls, *args, **kwargs): def callback(i, *a): i.run(name) return props.Button(name, - text=strings.labels[labelCls, name], + text=strings.actions[labelCls, name], callback=callback) diff --git a/fsl/fslview/profiles/__init__.py b/fsl/fslview/profiles/__init__.py index fcc81194e..7c9bb4408 100644 --- a/fsl/fslview/profiles/__init__.py +++ b/fsl/fslview/profiles/__init__.py @@ -31,7 +31,7 @@ import inspect import wx import props -import fsl.fslview.strings as strings +import fsl.data.strings as strings import fsl.fslview.actions as actions @@ -168,7 +168,7 @@ class Profile(actions.ActionProvider): modeProp = self.getProp('mode') for mode in modes: - modeProp.addChoice(mode, strings.labels[self, mode], self) + modeProp.addChoice(mode, strings.modes[self, mode], self) if len(modes) > 0: self.mode = modes[0] @@ -542,7 +542,6 @@ class ProfileManager(object): displayed. """ import fsl.fslview.profilemap as profilemap - import fsl.fslview.strings as strings self._canvasPanel = canvasPanel self._canvasCls = canvasPanel.__class__ @@ -556,7 +555,7 @@ class ProfileManager(object): for profile in profilez: profileProp.addChoice( profile, - strings.labels[canvasPanel, 'profile', profile], + strings.profiles[canvasPanel, profile], canvasPanel) canvasPanel.profile = profilez[0] diff --git a/fsl/fslview/strings.py b/fsl/fslview/strings.py deleted file mode 100644 index d7d7fbc99..000000000 --- a/fsl/fslview/strings.py +++ /dev/null @@ -1,168 +0,0 @@ -#!/usr/bin/env python -# -# strings.py - Labels used throughout various parts of FSLView. -# -# Author: Paul McCarthy <pauldmccarthy@gmail.com> -# -"""This module is a home for labels and tooltips used throughout FSLView. - - - labels - - - tooltips -""" - -import logging - -log = logging.getLogger(__name__) - -import fsl.utils.typedict as td -import fsl.data.image as fslimage - -labels = td.TypeDict({ - - 'OrthoPanel' : 'Ortho View', - 'LightBoxPanel' : 'Lightbox View', - 'TimeSeriesPanel' : 'Time series', - 'SpacePanel' : 'Space inspector', - - 'OpenFileAction' : 'Add image file', - 'OpenStandardAction' : 'Add standard', - 'CopyImageAction' : 'Copy image', - 'SaveImageAction' : 'Save image', - 'LoadColourMapAction' : 'Load custom colour map', - - ('Profile', 'mode') : 'Mode', - - ('CanvasPanel', 'profile', 'view') : 'View', - ('OrthoPanel', 'profile', 'edit') : 'Edit', - - ('CanvasPanel', 'screenshot') : 'Take screenshot', - ('CanvasPanel', 'toggleColourBar') : 'Show/hide colour bar', - ('CanvasPanel', 'toggleImageList') : 'Show/hide image list', - ('CanvasPanel', 'toggleDisplayProperties') : 'Show/hide display properties', - ('CanvasPanel', 'toggleLocationPanel') : 'Show/hide location panel', - ('CanvasPanel', 'toggleCanvasProperties') : 'Show/hide canvas properties', - - ('CanvasPanel', 'colourBarLocation', 'labels') : ('Top', 'Bottom', 'Left', 'Right'), - ('ColourBarCanvas', 'orientation', 'labels') : ('Horizontal', 'Vertical'), - ('ColourBarCanvas', 'labelSide', 'labels') : ('Top / Left', 'Bottom / Right'), - - ('CanvasPanel', 'zax') : 'Z axis', - ('CanvasPanel', 'showCursor') : 'Show location cursor', - ('CanvasPanel', 'syncLocation') : 'Sync location', - ('CanvasPanel', 'syncImageOrder') : 'Sync image order', - ('CanvasPanel', 'syncVolume') : 'Sync volume', - ('CanvasPanel', 'profile') : 'Profile', - ('CanvasPanel', 'zoom') : 'Zoom', - ('CanvasPanel', 'colourBarLocation') : 'Colour bar location', - ('CanvasPanel', 'colourBarLabelSide') : 'Colour bar label side', - - ('LightBoxPanel', 'highlightSlice') : 'Highlight slice', - ('LightBoxPanel', 'showGridLines') : 'Show grid lines', - ('LightBoxPanel', 'sliceSpacing') : 'Slice spacing', - ('LightBoxPanel', 'zrange') : 'Z range', - - ('OrthoPanel', 'showXCanvas') : 'Show X canvas', - ('OrthoPanel', 'showYCanvas') : 'Show Y canvas', - ('OrthoPanel', 'showZCanvas') : 'Show Z canvas', - ('OrthoPanel', 'showLabels') : 'Show labels', - ('OrthoPanel', 'layout') : 'Layout', - ('OrthoPanel', 'xzoom') : 'X zoom', - ('OrthoPanel', 'yzoom') : 'Y zoom', - ('OrthoPanel', 'zzoom') : 'Z zoom', - - ('Image', 'lowlong', fslimage.ORIENT_A2P) : 'Anterior', - ('Image', 'lowlong', fslimage.ORIENT_P2A) : 'Posterior', - ('Image', 'lowlong', fslimage.ORIENT_L2R) : 'Left', - ('Image', 'lowlong', fslimage.ORIENT_R2L) : 'Right', - ('Image', 'lowlong', fslimage.ORIENT_I2S) : 'Inferior', - ('Image', 'lowlong', fslimage.ORIENT_S2I) : 'Superior', - ('Image', 'lowlong', fslimage.ORIENT_UNKNOWN) : 'Unknown', - ('Image', 'highlong', fslimage.ORIENT_A2P) : 'Posterior', - ('Image', 'highlong', fslimage.ORIENT_P2A) : 'Anterior', - ('Image', 'highlong', fslimage.ORIENT_L2R) : 'Right', - ('Image', 'highlong', fslimage.ORIENT_R2L) : 'Left', - ('Image', 'highlong', fslimage.ORIENT_I2S) : 'Superior', - ('Image', 'highlong', fslimage.ORIENT_S2I) : 'Inferior', - ('Image', 'highlong', fslimage.ORIENT_UNKNOWN) : 'Unknown', - ('Image', 'lowshort', fslimage.ORIENT_A2P) : 'A', - ('Image', 'lowshort', fslimage.ORIENT_P2A) : 'P', - ('Image', 'lowshort', fslimage.ORIENT_L2R) : 'L', - ('Image', 'lowshort', fslimage.ORIENT_R2L) : 'R', - ('Image', 'lowshort', fslimage.ORIENT_I2S) : 'I', - ('Image', 'lowshort', fslimage.ORIENT_S2I) : 'S', - ('Image', 'lowshort', fslimage.ORIENT_UNKNOWN) : '?', - ('Image', 'highshort', fslimage.ORIENT_A2P) : 'P', - ('Image', 'highshort', fslimage.ORIENT_P2A) : 'A', - ('Image', 'highshort', fslimage.ORIENT_L2R) : 'R', - ('Image', 'highshort', fslimage.ORIENT_R2L) : 'L', - ('Image', 'highshort', fslimage.ORIENT_I2S) : 'S', - ('Image', 'highshort', fslimage.ORIENT_S2I) : 'I', - ('Image', 'highshort', fslimage.ORIENT_UNKNOWN) : '?', - ('Image', 'space', fslimage.NIFTI_XFORM_UNKNOWN) : 'Unknown', - ('Image', 'space', fslimage.NIFTI_XFORM_SCANNER_ANAT) : 'Scanner anatomical', - ('Image', 'space', fslimage.NIFTI_XFORM_ALIGNED_ANAT) : 'Aligned anatomical', - ('Image', 'space', fslimage.NIFTI_XFORM_TALAIRACH) : 'Talairach', - ('Image', 'space', fslimage.NIFTI_XFORM_MNI_152) : 'MNI152', - - - ('LocationPanel', 'outOfBounds') : 'Out of bounds', - ('LocationPanel', 'spaceLabel') : 'space', - ('LocationPanel', 'worldLabel') : 'World location (mm)', - ('LocationPanel', 'voxelLabel') : 'Voxel coordinates', - ('LocationPanel', 'volumeLabel') : 'Volume (index)' , - - ('OrthoViewProfile', 'nav') : 'Navigate', - ('OrthoViewProfile', 'pan') : 'Pan', - ('OrthoViewProfile', 'zoom') : 'Zoom', - - ('OrthoEditProfile', 'nav') : 'Navigate', - ('OrthoEditProfile', 'pan') : 'Pan', - ('OrthoEditProfile', 'zoom') : 'Zoom', - ('OrthoEditProfile', 'sel') : 'Select', - ('OrthoEditProfile', 'desel') : 'Deselect', - ('OrthoEditProfile', 'selint') : 'Select by intensity', - - ('OrthoViewProfile', 'centreCursor') : 'Centre cursor', - ('OrthoViewProfile', 'resetZoom') : 'Reset zoom', - - - ('OrthoEditProfile', 'undo') : 'Undo', - ('OrthoEditProfile', 'redo') : 'Redo', - ('OrthoEditProfile', 'fillSelection') : 'Fill selected region', - ('OrthoEditProfile', 'clearSelection') : 'Clear selection', - ('OrthoEditProfile', 'createMaskFromSelection') : 'Create mask from selected region', - ('OrthoEditProfile', 'createROIFromSelection') : 'Create ROI from selected region', - - ('OrthoEditProfile', 'selectionMode') : 'Selection mode', - ('OrthoEditProfile', 'selectionSize') : 'Selection size', - ('OrthoEditProfile', 'selectionIs3D') : '3D selection', - ('OrthoEditProfile', 'fillValue') : 'Fill value', - ('OrthoEditProfile', 'intensityThres') : 'Intensity threshold', - ('OrthoEditProfile', 'localFill') : 'Only select adjacent voxels', - ('OrthoEditProfile', 'searchRadius') : 'Limit search to radius (mm)', - - - ('ImageDisplay', 'name') : 'Image name', - ('ImageDisplay', 'enabled') : 'Enabled', - ('ImageDisplay', 'displayRange') : 'Display range', - ('ImageDisplay', 'alpha') : 'Opacity', - ('ImageDisplay', 'clipLow') : 'Low clipping', - ('ImageDisplay', 'clipHigh') : 'High clipping', - ('ImageDisplay', 'interpolation') : 'Interpolation', - ('ImageDisplay', 'resolution') : 'Resolution', - ('ImageDisplay', 'volume') : 'Volume', - ('ImageDisplay', 'syncVolume') : 'Synchronise volume', - ('ImageDisplay', 'transform') : 'Image transform', - ('ImageDisplay', 'imageType') : 'Image data type', - ('ImageDisplay', 'cmap') : 'Colour map', - - ('ImageDisplay', 'displayRange', 'labels') : ['Min.', 'Max.'], - ('ImageDisplay', 'transform', 'labels') : ['Use qform/sform transformation matrix', - 'Use pixdims only', - 'Do not use qform/sform or pixdims'], - - ('ImageDisplay', 'interpolation', 'labels') : ['No interpolation', - 'Linear interpolation', - 'Spline interpolation'], -}) diff --git a/fsl/fslview/views/canvaspanel.py b/fsl/fslview/views/canvaspanel.py index 24f85051a..9b44d0fe3 100644 --- a/fsl/fslview/views/canvaspanel.py +++ b/fsl/fslview/views/canvaspanel.py @@ -23,8 +23,8 @@ import wx import props +import fsl.data.strings as strings import fsl.fslview.panel as fslpanel -import fsl.fslview.strings as strings import fsl.fslview.profiles as profiles import fsl.fslview.displaycontext as displayctx import fsl.fslview.controls.imagelistpanel as imagelistpanel @@ -163,7 +163,12 @@ class CanvasPanel(fslpanel.FSLViewPanel): colourBarLocation = props.Choice( ('top', 'bottom', 'left', 'right'), - labels=strings.labels['CanvasPanel', 'colourBarLocation', 'labels']) + labels=[strings.choices['CanvasPanel.colourBarLocation.top'], + strings.choices['CanvasPanel.colourBarLocation.bottom'], + strings.choices['CanvasPanel.colourBarLocation.left'], + strings.choices['CanvasPanel.colourBarLocation.right']]) + + colourBarLabelSide = colourbarpanel.ColourBarPanel.labelSide diff --git a/fsl/fslview/views/orthopanel.py b/fsl/fslview/views/orthopanel.py index de95cd303..82f6a879b 100644 --- a/fsl/fslview/views/orthopanel.py +++ b/fsl/fslview/views/orthopanel.py @@ -21,9 +21,9 @@ import copy import wx import props -import fsl.data.image as fslimage +import fsl.data.strings as strings +import fsl.data.constants as constants import fsl.utils.layout as fsllayout -import fsl.fslview.strings as strings import fsl.fslview.gl as fslgl import fsl.fslview.gl.wxglslicecanvas as slicecanvas import canvaspanel @@ -358,15 +358,15 @@ class OrthoPanel(canvaspanel.CanvasPanel): yorient = image.getWorldOrientation(1) zorient = image.getWorldOrientation(2) - if fslimage.ORIENT_UNKNOWN in (xorient, yorient, zorient): + if constants.ORIENT_UNKNOWN in (xorient, yorient, zorient): colour = 'red' - xlo = strings.labels['Image', 'lowshort', xorient] - ylo = strings.labels['Image', 'lowshort', yorient] - zlo = strings.labels['Image', 'lowshort', zorient] - xhi = strings.labels['Image', 'highshort', xorient] - yhi = strings.labels['Image', 'highshort', yorient] - zhi = strings.labels['Image', 'highshort', zorient] + xlo = strings.anatomy['Image', 'lowshort', xorient] + ylo = strings.anatomy['Image', 'lowshort', yorient] + zlo = strings.anatomy['Image', 'lowshort', zorient] + xhi = strings.anatomy['Image', 'highshort', xorient] + yhi = strings.anatomy['Image', 'highshort', yorient] + zhi = strings.anatomy['Image', 'highshort', zorient] for lbl in allLabels: lbl.SetForegroundColour(colour) diff --git a/fsl/fslview/views/spacepanel.py b/fsl/fslview/views/spacepanel.py index eb9aec663..9677b43d2 100644 --- a/fsl/fslview/views/spacepanel.py +++ b/fsl/fslview/views/spacepanel.py @@ -16,8 +16,9 @@ import wx from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as Canvas from mpl_toolkits.mplot3d import Axes3D -import fsl.fslview.panel as fslpanel +import fsl.data.strings as strings import fsl.utils.transform as transform +import fsl.fslview.panel as fslpanel class SpacePanel(fslpanel.FSLViewPanel): @@ -125,9 +126,6 @@ class SpacePanel(fslpanel.FSLViewPanel): def _plotImageLabels(self): - # Imported here to avoid circular import issues - import fsl.fslview.strings as strings - image = self._displayCtx.getSelectedImage() display = self._displayCtx.getDisplayProperties(image) @@ -142,8 +140,8 @@ class SpacePanel(fslpanel.FSLViewPanel): orient = image.getVoxelOrientation(ax) - lblLo = strings.imageAxisLowShortLabels[ orient] - lblHi = strings.imageAxisHighShortLabels[orient] + lblLo = strings.anatomy['Image', 'lowshort', orient] + lblHi = strings.anatomy['Image', 'highshort', orient] wldSpan = transform.transform(voxSpan, display.voxToDisplayMat) diff --git a/fsl/tools/render.py b/fsl/tools/render.py index d792678b4..56f2cdfa2 100644 --- a/fsl/tools/render.py +++ b/fsl/tools/render.py @@ -29,7 +29,7 @@ import fslview_parseargs import fsl.utils.layout as fsllayout import fsl.utils.colourbarbitmap as cbarbitmap import fsl.utils.textbitmap as textbitmap -import fsl.fslview.strings as strings +import fsl.data.strings as strings import fsl.data.image as fslimage @@ -75,12 +75,12 @@ def buildLabelBitmaps(imageList, if fslimage.ORIENT_UNKNOWN in [xorient, yorient, zorient]: fgColour = 'red' - xlo = strings.imageAxisLowShortLabels[ xorient] - ylo = strings.imageAxisLowShortLabels[ yorient] - zlo = strings.imageAxisLowShortLabels[ zorient] - xhi = strings.imageAxisHighShortLabels[xorient] - yhi = strings.imageAxisHighShortLabels[yorient] - zhi = strings.imageAxisHighShortLabels[zorient] + xlo = strings.anatomy['Image', 'lowshort', xorient] + ylo = strings.anatomy['Image', 'lowshort', yorient] + zlo = strings.anatomy['Image', 'lowshort', zorient] + xhi = strings.anatomy['Image', 'highshort', xorient] + yhi = strings.anatomy['Image', 'highshort', yorient] + zhi = strings.anatomy['Image', 'highshort', zorient] loLabels = [xlo, ylo, zlo] hiLabels = [xhi, yhi, zhi] diff --git a/fsl/utils/typedict.py b/fsl/utils/typedict.py index 9444cf4c3..69811cd7f 100644 --- a/fsl/utils/typedict.py +++ b/fsl/utils/typedict.py @@ -21,13 +21,28 @@ class TypeDict(object): """ def __init__(self, initial=None): - self._dict = dict(initial) + + if initial is None: + initial = {} + + self.__dict = {} + + for k, v in initial.items(): + self[k] = v def __setitem__(self, key, value): - self._dict[key] = value + self.__dict[self.__tokenifyKey(key)] = value + def __tokenifyKey(self, key): + + if isinstance(key, basestring) and '.' in key: + return tuple(key.split('.')) + + return key + + def get(self, key, default): try: return self.__getitem__(key) except KeyError: return default @@ -36,6 +51,7 @@ class TypeDict(object): def __getitem__(self, key): origKey = key + key = self.__tokenifyKey(key) bases = [] # Make the code a bit easier by @@ -71,7 +87,7 @@ class TypeDict(object): if len(key) == 1: lKey = key[0] else: lKey = tuple(key) - val = self._dict.get(lKey, None) + val = self.__dict.get(lKey, None) if val is not None: return val -- GitLab