From 064608c6833b0342fb17f562fcfcd30e354f831f Mon Sep 17 00:00:00 2001 From: Paul McCarthy <pauld.mccarthy@gmail.com> Date: Mon, 4 Apr 2016 13:45:52 +0100 Subject: [PATCH] Python 2-3 compatibility. --- fsl/data/atlases.py | 6 +++--- fsl/data/featimage.py | 8 ++++---- fsl/data/featresults.py | 22 +++++++++------------- fsl/data/image.py | 7 ++++--- fsl/data/melodicimage.py | 4 ++-- fsl/data/model.py | 6 +++++- fsl/data/tensorimage.py | 14 ++++++++------ fsl/utils/async.py | 7 ++++--- fsl/utils/dialog.py | 23 +++++++++++++++++++---- fsl/utils/imagepanel.py | 19 +++++++++++++++---- fsl/utils/memoize.py | 4 +++- fsl/utils/notifier.py | 2 +- fsl/utils/path.py | 14 +++++++------- fsl/utils/platform.py | 25 ++++++++++++++++++++----- fsl/utils/runwindow.py | 4 +++- fsl/utils/typedict.py | 6 ++++-- 16 files changed, 111 insertions(+), 60 deletions(-) diff --git a/fsl/data/atlases.py b/fsl/data/atlases.py index 4c53811dc..a17ca031b 100644 --- a/fsl/data/atlases.py +++ b/fsl/data/atlases.py @@ -100,7 +100,7 @@ log = logging.getLogger(__name__) def listAtlases(refresh=False): - """Returns a dictionary containing :class:`AtlasDescription` objects for + """Returns a list containing :class:`AtlasDescription` objects for all available atlases. :arg refresh: If ``True``, or if the atlas desriptions have not @@ -119,7 +119,7 @@ def listAtlases(refresh=False): refresh = True if not refresh: - return ATLAS_DESCRIPTIONS.values() + return list(ATLAS_DESCRIPTIONS.values()) atlasFiles = glob.glob(op.join(ATLAS_DIR, '*.xml')) atlasDescs = map(AtlasDescription, atlasFiles) @@ -131,7 +131,7 @@ def listAtlases(refresh=False): desc.index = i ATLAS_DESCRIPTIONS[desc.atlasID] = desc - return atlasDescs + return list(atlasDescs) def getAtlasDescription(atlasID): diff --git a/fsl/data/featimage.py b/fsl/data/featimage.py index 59cc9ce89..7befd0d06 100644 --- a/fsl/data/featimage.py +++ b/fsl/data/featimage.py @@ -9,12 +9,12 @@ """ -import os.path as op +import os.path as op -import numpy as np +import numpy as np -import image as fslimage -import featresults +from . import image as fslimage +from . import featresults class FEATImage(fslimage.Image): diff --git a/fsl/data/featresults.py b/fsl/data/featresults.py index 4772f05b0..df2acbe50 100644 --- a/fsl/data/featresults.py +++ b/fsl/data/featresults.py @@ -414,7 +414,7 @@ def loadClusterResults(featdir, settings, contrast): # empty lines lines = f.readlines() lines = [l.strip() for l in lines] - lines = filter(lambda l: l != '', lines) + lines = [l for l in lines if l != ''] # the first line should contain column # names, and each other line should @@ -554,19 +554,15 @@ def getEVNames(settings): :arg settings: A FEAT settings dictionary (see :func:`loadSettings`). """ - numEVs = int(settings['evs_real']) + numEVs = int(settings['evs_real']) + titleKeys = [s for s in settings.keys() if s.startswith('evtitle')] + derivKeys = [s for s in settings.keys() if s.startswith('deriv_yn')] - titleKeys = filter(lambda s: s.startswith('evtitle'), settings.keys()) - derivKeys = filter(lambda s: s.startswith('deriv_yn'), settings.keys()) - - def _cmp(key1, key2): - key1 = ''.join([c for c in key1 if c.isdigit()]) - key2 = ''.join([c for c in key2 if c.isdigit()]) - - return cmp(int(key1), int(key2)) - - titleKeys = sorted(titleKeys, cmp=_cmp) - derivKeys = sorted(derivKeys, cmp=_cmp) + def key(k): + return int(''.join([c for c in k if c.isdigit()])) + + titleKeys = sorted(titleKeys, key=key) + derivKeys = sorted(derivKeys, key=key) evnames = [] for titleKey, derivKey in zip(titleKeys, derivKeys): diff --git a/fsl/data/image.py b/fsl/data/image.py index cc8ab09f8..a9975f5a7 100644 --- a/fsl/data/image.py +++ b/fsl/data/image.py @@ -38,6 +38,7 @@ import os import os.path as op import subprocess as sp +import six import numpy as np import nibabel as nib @@ -117,7 +118,7 @@ class Nifti1(object): header = header.copy() # The image parameter may be the name of an image file - if isinstance(image, basestring): + if isinstance(image, six.string_types): nibImage, filename = loadImage(addExt(image)) self.nibImage = nibImage @@ -380,7 +381,7 @@ class Image(Nifti1, props.HasProperties): # Or, if this image was loaded # from disk, use the file name - elif isinstance(image, basestring): + elif isinstance(image, six.string_types): self.name = removeExt(op.basename(self.dataSource)) self.saved = True @@ -594,7 +595,7 @@ def looksLikeImage(filename, allowedExts=None): # TODO A much more robust approach would be # to try loading the file using nibabel. - return any(map(lambda ext: filename.endswith(ext), allowedExts)) + return any([filename.endswith(ext) for ext in allowedExts]) def removeExt(filename): diff --git a/fsl/data/melodicimage.py b/fsl/data/melodicimage.py index 21bc8005e..fc0cb0670 100644 --- a/fsl/data/melodicimage.py +++ b/fsl/data/melodicimage.py @@ -14,8 +14,8 @@ import os.path as op import props -import image as fslimage -import melodicresults as melresults +from . import image as fslimage +from . import melodicresults as melresults class MelodicImage(fslimage.Image): diff --git a/fsl/data/model.py b/fsl/data/model.py index 3def2dff3..ededc41a5 100644 --- a/fsl/data/model.py +++ b/fsl/data/model.py @@ -21,6 +21,8 @@ import logging import os.path as op import numpy as np +import six + log = logging.getLogger(__name__) @@ -43,7 +45,7 @@ class Model(object): :arg indices: A list of indices into the vertex data. """ - if isinstance(data, basestring): + if isinstance(data, six.string_types): infile = data data, lengths, indices = loadVTKPolydataFile(infile) @@ -131,6 +133,7 @@ def loadVTKPolydataFile(infile): for i in range(nVertices): vertLine = lines[i + 5] vertices[i, :] = map(float, vertLine.split()) + vertices[i, :] = [float(w) for w in vertLine.split()] indexOffset = 0 for i in range(nPolygons): @@ -141,6 +144,7 @@ def loadVTKPolydataFile(infile): start = indexOffset end = indexOffset + polygonLengths[i] indices[start:end] = map(int, polyLine[1:]) + indices[start:end] = [int(w) for w in polyLine[1:]] indexOffset += polygonLengths[i] diff --git a/fsl/data/tensorimage.py b/fsl/data/tensorimage.py index 4d194b480..86ae221cb 100644 --- a/fsl/data/tensorimage.py +++ b/fsl/data/tensorimage.py @@ -9,12 +9,14 @@ the diffusion tensor data generated by the FSL ``dtifit`` tool. """ -import logging -import re -import glob -import os.path as op +import logging +import re +import glob +import os.path as op -import fsl.data.image as fslimage +import six + +from . import image as fslimage log = logging.getLogger(__name__) @@ -101,7 +103,7 @@ class TensorImage(fslimage.Nifti1): eigenvalues. """ - dtifitDir = isinstance(path, basestring) + dtifitDir = isinstance(path, six.string_types) if dtifitDir: diff --git a/fsl/utils/async.py b/fsl/utils/async.py index 801d0debb..672102102 100644 --- a/fsl/utils/async.py +++ b/fsl/utils/async.py @@ -33,11 +33,12 @@ the task (via :func:`idle`). import time -import Queue import logging import threading import collections +try: import queue +except: import Queue as queue log = logging.getLogger(__name__) @@ -114,7 +115,7 @@ in the :func:`idle` function. """ -_idleQueue = Queue.Queue() +_idleQueue = queue.Queue() """A ``Queue`` of functions which are to be run on the ``wx.EVT_IDLE`` loop. """ @@ -129,7 +130,7 @@ def _wxIdleLoop(ev): try: task, schedtime, timeout, args, kwargs = _idleQueue.get_nowait() - except Queue.Empty: + except queue.Empty: return name = getattr(task, '__name__', '<unknown>') diff --git a/fsl/utils/dialog.py b/fsl/utils/dialog.py index a6e277f63..0a482defc 100644 --- a/fsl/utils/dialog.py +++ b/fsl/utils/dialog.py @@ -22,7 +22,7 @@ import threading import wx -from fsl.utils.platform import platform as fslplatform +from .platform import platform as fslplatform class SimpleMessageDialog(wx.Dialog): @@ -455,7 +455,12 @@ class TextEditDialog(wx.Dialog): if icon is not None: icon = wx.ArtProvider.GetMessageBoxIcon(icon) - bmp = wx.EmptyBitmap(icon.GetWidth(), icon.GetHeight()) + + if fslplatform.wxFlavour == fslplatform.WX_PHOENIX: + bmp = wx.Bitmap() + else: + bmp = wx.EmptyBitmap(icon.GetWidth(), icon.GetHeight()) + bmp.CopyFromIcon(icon) self.__icon = wx.StaticBitmap(self) self.__icon.SetBitmap(bmp) @@ -634,7 +639,12 @@ class FSLDirDialog(wx.Dialog): self.__skip = wx.Button( self, id=wx.ID_CANCEL) icon = wx.ArtProvider.GetMessageBoxIcon(wx.ICON_EXCLAMATION) - bmp = wx.EmptyBitmap(icon.GetWidth(), icon.GetHeight()) + + if fslplatform.wxFlavour == fslplatform.WX_PYTHON: + bmp = wx.EmptyBitmap(icon.GetWidth(), icon.GetHeight()) + else: + bmp = wx.Bitmap() + bmp.CopyFromIcon(icon) self.__icon.SetBitmap(bmp) @@ -756,7 +766,12 @@ class CheckBoxMessageDialog(wx.Dialog): if icon is not None: icon = wx.ArtProvider.GetMessageBoxIcon(icon) self.__icon = wx.StaticBitmap(self) - bmp = wx.EmptyBitmap(icon.GetWidth(), icon.GetHeight()) + + if fslplatform.wxFlavour == fslplatform.WX_PYTHON: + bmp = wx.EmptyBitmap(icon.GetWidth(), icon.GetHeight()) + else: + bmp = wx.Bitmap() + bmp.CopyFromIcon(icon) self.__icon.SetBitmap(bmp) else: diff --git a/fsl/utils/imagepanel.py b/fsl/utils/imagepanel.py index cf74b453f..5352bd100 100644 --- a/fsl/utils/imagepanel.py +++ b/fsl/utils/imagepanel.py @@ -12,12 +12,18 @@ import logging import wx +from fsl.utils.platform import platform as fslplatform + log = logging.getLogger(__name__) -class ImagePanel(wx.PyPanel): - """A :class:`wx.PyPanel` which may be used to display a resizeable +if fslplatform.wxFlavour == fslplatform.WX_PHOENIX: ImagePanelBase = wx.Panel +else: ImagePanelBase = wx.PyPanel + + +class ImagePanel(ImagePanelBase): + """A :class:`wx.Panel` which may be used to display a resizeable :class:`wx.Image`. The image is scaled to the size of the panel. """ @@ -32,7 +38,7 @@ class ImagePanel(wx.PyPanel): :arg image: The :class:`wx.Image` object to display. """ - wx.PyPanel.__init__(self, parent) + ImagePanelBase.__init__(self, parent) self.Bind(wx.EVT_PAINT, self.Draw) self.Bind(wx.EVT_SIZE, self.__onSize) @@ -46,6 +52,11 @@ class ImagePanel(wx.PyPanel): :arg image: The :class:`wx.Image` object to display. """ self.__image = image + + + if image is not None: self.SetMinSize(image.GetSize()) + else: self.SetMinSize((0, 0)) + self.Refresh() @@ -86,6 +97,6 @@ class ImagePanel(wx.PyPanel): if width == 0 or height == 0: return - bitmap = wx.BitmapFromImage(self.__image.Scale(width, height)) + bitmap = self.__image.Scale(width, height).ConvertToBitmap() dc.DrawBitmap(bitmap, 0, 0, False) diff --git a/fsl/utils/memoize.py b/fsl/utils/memoize.py index 07f6f1242..9de2ef726 100644 --- a/fsl/utils/memoize.py +++ b/fsl/utils/memoize.py @@ -18,6 +18,7 @@ a function: import hashlib import functools +import six def memoizeMD5(func): @@ -35,7 +36,8 @@ def memoizeMD5(func): hashobj = hashlib.md5() for arg in args: - hashobj.update(str(arg)) + arg = six.u(arg).encode('utf-8') + hashobj.update(arg) digest = hashobj.hexdigest() cached = cache.get(digest) diff --git a/fsl/utils/notifier.py b/fsl/utils/notifier.py index 154f67521..faa3fb7e6 100644 --- a/fsl/utils/notifier.py +++ b/fsl/utils/notifier.py @@ -34,7 +34,7 @@ class Notifier(object): """Initialises a dictionary of listeners on a new ``Notifier`` instance. """ - new = object.__new__(cls, *args, **kwargs) + new = object.__new__(cls) new.__listeners = collections.OrderedDict() return new diff --git a/fsl/utils/path.py b/fsl/utils/path.py index cacc25b0f..408b77c23 100644 --- a/fsl/utils/path.py +++ b/fsl/utils/path.py @@ -91,8 +91,8 @@ def addExt(prefix, allowedExts, mustExist=True, defaultExt=None): if not mustExist: # the provided file name already - # ends with a supported extension - if any(map(lambda ext: prefix.endswith(ext), allowedExts)): + # ends with a supported extension + if any([prefix.endswith(ext) for ext in allowedExts]): return prefix if defaultExt is not None: return prefix + defaultExt @@ -100,16 +100,16 @@ def addExt(prefix, allowedExts, mustExist=True, defaultExt=None): # If the provided prefix already ends with a # supported extension , check to see that it exists - if any(map(lambda ext: prefix.endswith(ext), allowedExts)): + if any([prefix.endswith(ext) for ext in allowedExts]): extended = [prefix] # Otherwise, make a bunch of file names, one per # supported extension, and test to see if exactly # one of them exists. else: - extended = map(lambda ext: prefix + ext, allowedExts) + extended = [prefix + ext for ext in allowedExts] - exists = map(op.isfile, extended) + exists = [op.isfile(e) for e in extended] # Could not find any supported file # with the specified prefix @@ -119,7 +119,7 @@ def addExt(prefix, allowedExts, mustExist=True, defaultExt=None): # Ambiguity! More than one supported # file with the specified prefix - if len(filter(bool, exists)) > 1: + if sum(exists) > 1: raise ValueError('More than one file with prefix {}'.format(prefix)) # Return the full file name of the @@ -139,7 +139,7 @@ def removeExt(filename, allowedExts): """ # figure out the extension of the given file - extMatches = map(lambda ext: filename.endswith(ext), allowedExts) + extMatches = [filename.endswith(ext) for ext in allowedExts] # the file does not have a supported extension if not any(extMatches): diff --git a/fsl/utils/platform.py b/fsl/utils/platform.py index 2510f8830..9b95faf96 100644 --- a/fsl/utils/platform.py +++ b/fsl/utils/platform.py @@ -8,17 +8,14 @@ of information about the current platform we are running on. A single ``Platform`` instance is created when this module is first imported, and is available as a module attribute called :attr:`platform`. - -.. note:: The ``Platform`` class only contains information which is not - already accessible from the built-in ``platform`` module - (e.g. operating system information), or the ``six`` module (e.g. - python 2 vs 3). """ import logging import os +import sys +import platform as builtin_platform import fsl.utils.notifier as notifier @@ -67,6 +64,8 @@ class Platform(notifier.Notifier): .. autosummary:: + os + frozen fsldir haveGui wxPlatform @@ -125,6 +124,22 @@ class Platform(notifier.Notifier): log.warning('Could not determine wx platform from ' 'information: {}'.format(pi)) + + @property + def os(self): + """The operating system name. Whatever is returned by the built-in + ``platform.system`` function. + """ + return builtin_platform.system() + + + @property + def frozen(self): + """``True`` if we are running in a compiled/frozen application, + ``False`` otherwise. + """ + return getattr(sys, 'frozen', False) + @property def haveGui(self): diff --git a/fsl/utils/runwindow.py b/fsl/utils/runwindow.py index f344c1483..a26969e9f 100644 --- a/fsl/utils/runwindow.py +++ b/fsl/utils/runwindow.py @@ -26,7 +26,9 @@ import logging import subprocess as subp import threading as thread -import Queue as queue + +try: import queue +except: import Queue as queue import wx diff --git a/fsl/utils/typedict.py b/fsl/utils/typedict.py index 0a498ca74..0ac4cdcde 100644 --- a/fsl/utils/typedict.py +++ b/fsl/utils/typedict.py @@ -8,6 +8,8 @@ """ +import six + import collections @@ -164,7 +166,7 @@ class TypeDict(object): string, or a combination of strings and types, into a tuple. """ - if isinstance(key, basestring): + if isinstance(key, six.string_types): if '.' in key: return tuple(key.split('.')) else: return key @@ -174,7 +176,7 @@ class TypeDict(object): key = [] for tk in tKeys: - if isinstance(tk, basestring): key.append(tk) + if isinstance(tk, six.string_types): key.append(tk) elif isinstance(tk, collections.Sequence): key += list(tk) else: key.append(tk) -- GitLab