コード例 #1
0
def test_class_keys():

    class A(object):
        pass

    class B(object):
        pass

    class C(object):
        pass

    td = typedict.TypeDict()

    a  = A()
    b  = B()
    c  = C()

    # TypeDict [currently] does not allow value
    # assignment with classes/instances as keys -
    # we must use the class name, encoded as a
    # string, when assigning.
    keycomponents = ['A', 'B', 'C', 'att1', 'att2', 'att3']
    keys          = list(it.chain(it.permutations(keycomponents, 1),
                                  it.permutations(keycomponents, 2),
                                  it.permutations(keycomponents, 3),
                                  it.permutations(keycomponents, 4),
                                  it.permutations(keycomponents, 5),
                                  it.permutations(keycomponents, 6)))

    for val, key in enumerate(keys):
        if len(key) == 1: td[key[0]] = val
        else:             td[key]    = val

    for val, key in enumerate(keys):

        # Keys can be passed as tuples
        assert td[key] == val

        # Or as dot-separated strings
        assert td['.'.join(key)] == val

        # But when accessing items,
        # we can use either class names,
        # classes, or instances.
        for toReplace in it.chain(it.combinations(['A', 'B', 'C'], 1),
                                  it.combinations(['A', 'B', 'C'], 2),
                                  it.combinations(['A', 'B', 'C'], 3)):

            clsrep  = {'A' : A, 'B' : B, 'C' : C}
            instrep = {'A' : a, 'B' : b, 'C' : c}

            # use class instead of name
            repkey = [clsrep[k] if k in toReplace else k for k in key]
            assert td[repkey] == val

            # use instance instead of name
            repkey = [instrep[k] if k in toReplace else k for k in key]
            assert td[repkey] == val
コード例 #2
0
def test_create():

    td = typedict.TypeDict()


    assert len(td)          == 0
    assert len(td.keys())   == 0
    assert len(td.values()) == 0
    assert len(td.items())  == 0

    keys   = [0,
              1,
              2,
              'a',
              'b',
              'c',
              'a.b',
              'a.b.c',
              ('a', 'c'),
              ('a',  0)]
    values = list(range(len(keys)))

    td = typedict.TypeDict(        dict(zip(keys, values)))
    td = typedict.TypeDict(initial=dict(zip(keys, values)))

    print(     td)
    print(repr(td))

    tknkeys = [td.tokenifyKey(k) for k in keys]

    assert len(td)                       == len(keys)
    assert sorted(map(str, td.keys()))   == sorted(map(str, tknkeys))
    assert sorted(map(str, td.values())) == sorted(map(str, values))
    assert sorted(map(str, td.items()))  == sorted(map(str, zip(tknkeys, values)))

    for k, v in zip(keys, values):
        assert td[k] == v

    with pytest.raises(KeyError):
        td['non-existent']

    assert td.get('non-existent') is None
    assert td.get('non-existent', 'default') == 'default'
コード例 #3
0
def test_exact():

    class A(object):
        pass

    class B(A):
        pass

    class C(A):
        pass

    td = typedict.TypeDict()

    td['A'] = 'A'
    td['B'] = 'B'

    assert td[C]     == 'A'
    assert td.get(C, allhits=True) == ['A']
    assert td.get(C) == 'A'
    assert td.get(C, allhits=True, exact=True) is None
コード例 #4
0
            if overlay is selOvl:
                title = colourFmt.format(title)
                if info is not None:
                    info = colourFmt.format(info)

            lines.append(title)

            if info is not None:
                lines.append(info)

        self.__info.SetPage('<br>'.join(lines))
        self.__info.Refresh()


DISPLAYOPTS_BOUNDS = td.TypeDict({
    'DisplayOpts' : ['bounds'],
    'MeshOpts'    : ['refImage'],
})
"""Different :class:`.DisplayOpts` types have different properties which
affect the current overlay bounds.  Therefore, when the current overlay
changes (as dictated by the :attr:`.DisplayContext.selectedOverlay`
property),the :meth:`__registerOverlay` method registers property
listeners on the properties specified in this dictionary.
"""


DISPLAYOPTS_INFO = td.TypeDict({
    'NiftiOpts' : ['volume'],
    'MeshOpts'  : ['vertexDataIndex'],
})
"""Different :class:`.DisplayOpts` types have different properties which
affect the current overlay location information.  Therefore, when the current
コード例 #5
0
ファイル: group.py プロジェクト: sanjayankur31/fsleyes
class OverlayGroup(props.HasProperties):
    """An ``OverlayGroup`` is a group of overlays for which the corresponding
    :class:`.Display` and :class:`.DisplayOpts` properties are synchronised.


    The point of the ``OverlayGroup`` is to allow the user to define groups of
    overlays, so he/she can change display properties on the entire group,
    instead of having to change display properties on each overlay one by one.


    Overlays can be added to an ``OverlayGroup`` with the :meth:`addOverlay`,
    and removed with the :meth:`removeOverlay`.


    When an ``OverlayGroup`` is created, it dynamically adds all of the
    properties which could possibly be linked between overlays to itself,
    using the :meth:`props.HasProperties.addProperty` method. When the first
    overlay is added to the group, these group properties are set to the
    display properties of this overlay. Then, the display properties of
    overlays which are subsequently added to the group will be set to the
    group display properties.


    .. note:: Currently, only a subset of display properties are linked
              between the overlays in a group. The properties which are linked
              are hard-coded in the :attr:`_groupBindings` dictionary.

              A possible future *FSLeyes* enhancement will be to allow the
              user to specify which display properties within an
              ``OverlayGroup`` should be linked.
    """

    overlays = props.List()
    """The list of overlays in this ``OverlayGroup``.

    .. warning:: Do not add/remove overlays directly to this list - use the
                :meth:`addOverlay` and :meth:`removeOverlay` methods instead.
    """

    _groupBindings = td.TypeDict({
        'Display': [],
        'NiftiOpts': ['volume', 'volumeDim'],
        'VolumeOpts': ['interpolation'],
        'Volume3DOpts': [
            'numSteps', 'numInnerSteps', 'resolution', 'numClipPlanes',
            'clipMode', 'clipPosition', 'clipAzimuth', 'clipInclination'
        ],
        'LabelOpts': [],
        'MeshOpts': ['refImage', 'coordSpace'],
        'VectorOpts': ['suppressX', 'suppressY', 'suppressZ', 'suppressMode'],
        'LineVectorOpts': [],
        'RGBVectorOpts': ['interpolation'],
        'TensorOpts': ['lighting', 'tensorResolution'],
    })
    """This dictionary defines the properties which are bound across
    :class:`.Display` instances :class:`.DisplayOpts` sub-class instances, for
    overlays which are in the same group.
    """
    def __init__(self, displayCtx, overlayList):
        """Create an ``OverlayGroup``.

        :arg displayCtx:  The :class:`.DisplayContext`.

        :arg overlayList: The :class:`.OverlayList`.
        """

        self.__displayCtx = displayCtx
        self.__overlayList = overlayList
        self.__name = '{}_{}'.format(type(self).__name__, id(self))

        # This dict is used by the __bindDisplayOpts
        # method to keep track of which group properties
        # have already been given a value
        self.__hasBeenSet = {}

        # Import all of the Display/DisplayOpts
        # classes into the local namespace
        from fsleyes.displaycontext import (  # noqa
            Display, NiftiOpts, VolumeOpts, Volume3DOpts, MaskOpts, VectorOpts,
            RGBVectorOpts, LineVectorOpts, MeshOpts, LabelOpts, TensorOpts)

        overlayList.addListener('overlays', self.__name,
                                self.__overlayListChanged)

        # Add all of the properties listed
        # in the _groupBindings dict as
        # properties of this OverlayGroup
        # instance.
        for clsName, propNames in OverlayGroup._groupBindings.items():

            cls = locals()[clsName]

            for propName in propNames:
                prop = copy.copy(getattr(cls, propName))
                self.addProperty('{}_{}'.format(clsName, propName), prop)

                self.__hasBeenSet[clsName, propName] = False

        # Special case - make sure that the NiftiOpts
        # volume property is not constrained
        self.setAttribute('NiftiOpts_volume', 'maxval', six.MAXSIZE)

    def __copy__(self):
        """Create a copy of this ``OverlayGroup``.

        A custom copy operator is needed due to the way that
        the :class:`.props.HasProperties` class works.
        """
        return OverlayGroup(self, self.__displayCtx, self.__overlayList)

    def __str__(self):
        """Returns a string representation of this ``OverlayGroup``."""
        return str([str(o) for o in self.overlays])

    def __repr__(self):
        """Returns a string representation of this ``OverlayGroup``."""
        return '[{}]'.format(', '.join([str(o) for o in self.overlays]))

    def __contains__(self, ovl):
        """Returns ``True`` if ``ovl`` is in this ``OverlayGroup``,
        :attr:`False` otherwise.
        """
        return ovl in self.overlays

    def __len__(self):
        """Returns the number of overlays in this ``OverlayGroup``. """
        return len(self.overlays)

    def destroy(self):
        """Must be called when this ``OverlayGroup`` is no longer needed.
        Removes all overlays from the group, and removes property listeners.
        """
        for overlay in list(self.overlays):
            self.removeOverlay(overlay)

        self.__overlayList.removeListener('overlays', self.__name)

        self.__overlayList = None
        self.__displayCtx = None

    def addOverlay(self, overlay):
        """Add an overlay to this ``OverlayGroup``.

        If this is the first overlay to be added, the properties of this
        ``OverlayGroup`` are set to the overlay display properties. Otherwise,
        the overlay display properties are set to those of this
        ``OverlayGroup``.
        """

        if overlay in self.overlays:
            return

        self.overlays.append(overlay)

        display = self.__displayCtx.getDisplay(overlay)
        opts = display.opts

        log.debug('Adding overlay {} to group {}'.format(
            overlay.name, self.__name))

        self.__bindDisplayOpts(display)
        self.__bindDisplayOpts(opts)

        display.addListener('overlayType', self.__name,
                            self.__overlayTypeChanged)

    def removeOverlay(self, overlay):
        """Remove the given overlay from this ``OverlayGroup``. """

        if overlay not in self.overlays:
            return

        from . import InvalidOverlayError

        self.overlays.remove(overlay)

        try:
            display = self.__displayCtx.getDisplay(overlay)
        except InvalidOverlayError:
            return

        opts = display.opts

        log.debug('Removing overlay {} from group {}'.format(
            overlay.name, self.__name))

        self.__bindDisplayOpts(display, unbind=True)
        self.__bindDisplayOpts(opts, unbind=True)

        display.removeListener('overlayType', self.__name)

        if len(self.overlays) == 0:
            for key in self.__hasBeenSet.keys():
                self.__hasBeenSet[key] = False

    def __bindDisplayOpts(self, target, unbind=False):
        """Binds or unbinds the properties of the given ``target`` to the
        properties of this ``OverlayGroup``.

        :arg target: A :class:`.Display` or :class:`.DisplayOpts` instance.

        :arg unbind: Set to ``True`` to bind the properties, ``False`` to
                     unbind them.
        """

        # This is the first overlay to be added - the
        # group should inherit its property values
        if len(self.overlays) == 1:
            master, slave = target, self

        # Other overlays are already in the group - the
        # new overlay should inherit the group properties
        else:
            master, slave = self, target

        bindProps = OverlayGroup._groupBindings.get(target,
                                                    allhits=True,
                                                    bykey=True)

        for clsName, propNames in bindProps.items():
            for propName in propNames:

                groupName = '{}_{}'.format(clsName, propName)

                # If the group property has not yet
                # taken on a value, initialise it
                # to the property value being bound.
                #
                # We do this to avoid clobbering
                # property values with un-initialised
                # group property values.
                if not self.__hasBeenSet[clsName, propName]:

                    setattr(self, groupName, getattr(target, propName))
                    self.__hasBeenSet[clsName, propName] = True

                if slave is self:
                    otherName = propName
                    propName = groupName
                else:
                    otherName = groupName

                slave.bindProps(propName,
                                master,
                                otherName,
                                bindatt=False,
                                unbind=unbind)

    def __overlayListChanged(self, *a):
        """Called when overlays are added/removed to/from the
        :class:`.OverlayList` . Makes sure that the :attr:`overlays` list for
        this group does not contain any overlays that have been removed.
        """
        for ovl in self.overlays:
            if ovl not in self.__overlayList:
                self.removeOverlay(ovl)

    def __overlayTypeChanged(self, value, valid, display, name):
        """This method is called when the :attr:`.Display.overlayType`
        property for an overlay in the group changes.

        It makes sure that the display properties of the new
        :class:`.DisplayOpts` instance are bound to the group properties.
        """
        opts = display.opts
        self.__bindDisplayOpts(opts)
コード例 #6
0
ファイル: shortcuts.py プロジェクト: sanjayankur31/fsleyes
actions = td.TypeDict({
    'LoadOverlayAction': 'Ctrl-O',
    'LoadOverlayFromDirAction': 'Ctrl-D',
    'LoadStandardAction': 'Ctrl-S',
    'CopyOverlayAction': 'Ctrl-Shift-C',
    'SaveOverlayAction': 'Ctrl-Shift-S',
    'ReloadOverlayAction': 'Ctrl-Shift-R',
    'RemoveOverlayAction': 'Ctrl-Shift-W',
    'FSLeyesFrame.closeFSLeyes': 'Ctrl-Q',
    'FSLeyesFrame.openHelp': 'Ctrl-?',
    'FSLeyesFrame.layouts.default': 'Ctrl-Shift-D',
    'FSLeyesFrame.addOrthoPanel': 'Ctrl-1',
    'FSLeyesFrame.addLightBoxPanel': 'Ctrl-2',
    'FSLeyesFrame.addTimeSeriesPanel': 'Ctrl-3',
    'FSLeyesFrame.addHistogramPanel': 'Ctrl-4',
    'FSLeyesFrame.addPowerSpectrumPanel': 'Ctrl-5',
    'FSLeyesFrame.addScene3DPanel': 'Ctrl-6',
    'FSLeyesFrame.addShellPanel': 'Ctrl-7',
    'FSLeyesFrame.selectNextOverlay': 'Ctrl-Up',
    'FSLeyesFrame.selectPreviousOverlay': 'Ctrl-Down',
    'FSLeyesFrame.toggleOverlayVisibility': 'Ctrl-F',

    # Shortcuts for next/prev volume

    # ViewPanel actions must use one
    # of CTRL, ALT or Shift due to
    # hacky things in FSLeyesFrame
    # (see __onViewPanelMenuItem)
    'ViewPanel.removeFromFrame': 'Ctrl-W',
    'ViewPanel.removeAllPanels': 'Ctrl-Alt-X',
    'CanvasPanel.toggleOverlayList': 'Ctrl-Alt-1',
    'CanvasPanel.toggleLocationPanel': 'Ctrl-Alt-2',
    'CanvasPanel.toggleOverlayInfo': 'Ctrl-Alt-3',
    'CanvasPanel.toggleDisplayPanel': 'Ctrl-Alt-4',
    'CanvasPanel.toggleCanvasSettingsPanel': 'Ctrl-Alt-5',
    'CanvasPanel.toggleAtlasPanel': 'Ctrl-Alt-6',
    'CanvasPanel.toggleDisplayToolBar': 'Ctrl-Alt-7',
    'OrthoPanel.toggleOrthoToolBar': 'Ctrl-Alt-8',
    'LightBoxPanel.toggleLightBoxToolBar': 'Ctrl-Alt-8',
    'Scene3DPanel.toggleScene3DToolBar': 'Ctrl-Alt-8',
    'CanvasPanel.toggleMovieMode': 'Alt-M',
    'CanvasPanel.toggleDisplaySync': 'Alt-S',
    'OrthoPanel.toggleEditMode': 'Alt-E',
    'OrthoPanel.resetDisplay': 'Alt-R',
    'OrthoPanel.centreCursor': 'Alt-P',
    'OrthoPanel.centreCursorWorld': 'Alt-O',
    'OrthoPanel.toggleLabels': 'Alt-L',
    'OrthoPanel.toggleCursor': 'Alt-C',
    'OrthoPanel.toggleXCanvas': 'Alt-X',
    'OrthoPanel.toggleYCanvas': 'Alt-Y',
    'OrthoPanel.toggleZCanvas': 'Alt-Z',
    'OrthoPanel.pearsonCorrelation': 'Alt-I',
    'PlotPanel.toggleOverlayList': 'Ctrl-Alt-1',
    'PlotPanel.togglePlotList': 'Ctrl-Alt-2',
    'PlotPanel.importDataSeries': 'Ctrl-I',
    'PlotPanel.exportDataSeries': 'Ctrl-E',
    'TimeSeriesPanel.toggleTimeSeriesToolBar': 'Ctrl-Alt-3',
    'TimeSeriesPanel.toggleTimeSeriesControl': 'Ctrl-Alt-4',
    'HistogramPanel.toggleHistogramToolBar': 'Ctrl-Alt-3',
    'HistogramPanel.toggleHistogramControl': 'Ctrl-Alt-4',
    'PowerSpectrumPanel.togglePowerSpectrumToolBar': 'Ctrl-Alt-3',
    'PowerSpectrumPanel.togglePowerSpectrumControl': 'Ctrl-Alt-4',
    'OrthoEditProfile.undo': 'Ctrl-Z',
    'OrthoEditProfile.redo': 'Ctrl-Y',
    'OrthoEditProfile.createMask': 'Ctrl-N',
    'OrthoEditProfile.clearSelection': 'Ctrl-Shift-A',
    'OrthoEditProfile.eraseSelection': 'Ctrl-E',
    'OrthoEditProfile.fillSelection': 'Ctrl-B',
    'OrthoEditProfile.copySelection': 'Ctrl-C',
    'OrthoEditProfile.pasteSelection': 'Ctrl-V',
})
コード例 #7
0
import functools

import wx

import fsl.utils.idle as idle
import fsleyes_props as props
import fsleyes_widgets as fwidgets
import fsleyes_widgets.imagepanel as imagepanel
import fsleyes_widgets.utils.typedict as td
import fsleyes.strings as strings
import fsleyes.colourmaps as fslcm
import fsleyes.controls.colourbar as cbar
import fsleyes.actions.loadcolourmap as loadcmap
import fsleyes.actions.loadvertexdata as loadvdata

_PROPERTIES = td.TypeDict()
_3D_PROPERTIES = td.TypeDict()
_WIDGET_SPECS = td.TypeDict()
_3D_WIDGET_SPECS = td.TypeDict()


def _merge_dicts(d1, d2):
    d3 = d1.copy()
    d3.update(d2)
    return d3


def getPropertyList(target, threedee=False):

    plist = _getThing(target, '_initPropertyList_', _PROPERTIES, threedee)
コード例 #8
0
ファイル: display.py プロジェクト: lachmanfrantisek/fsleyes
    def __init__(self,
                 overlay,
                 overlayList,
                 displayCtx,
                 parent=None,
                 overlayType=None):
        """Create a :class:`Display` for the specified overlay.

        :arg overlay:     The overlay object.

        :arg overlayList: The :class:`.OverlayList` instance which contains
                          all overlays.

        :arg displayCtx:  A :class:`.DisplayContext` instance describing how
                          the overlays are to be displayed.

        :arg parent:      A parent ``Display`` instance - see
                          :mod:`props.syncable`.

        :arg overlayType: Initial overlay type - see the :attr:`overlayType`
                          property.
        """

        self.__overlay     = overlay
        self.__overlayList = overlayList
        self.__displayCtx  = displayCtx
        self.name          = overlay.name

        # Populate the possible choices
        # for the overlayType property
        from . import getOverlayTypes

        ovlTypes    = getOverlayTypes(overlay)
        ovlTypeProp = self.getProp('overlayType')

        log.debug('Enabling overlay types for {}: '.format(overlay, ovlTypes))
        ovlTypeProp.setChoices(ovlTypes, instance=self)

        # Override the default overlay
        # type if it has been specified
        if overlayType is not None:
            self.overlayType = overlayType

        # Call the super constructor after our own
        # initialisation, in case the provided parent
        # has different property values to our own,
        # and our values need to be updated
        props.SyncableHasProperties.__init__(
            self,
            parent=parent,

            # These properties cannot be unbound, as
            # they affect the OpenGL representation.
            # The name can't be unbound either,
            # because it would be silly to allow
            # different names for the same overlay.
            nounbind=['overlayType', 'name'],

            # Initial sync state between this
            # Display and the parent Display
            # (if this Display has a parent)
            state=displayCtx.syncOverlayDisplay)

        # When the overlay type changes, the property
        # values of the DisplayOpts instance for the
        # old overlay type are stored in this dict.
        # If the overlay is later changed back to the
        # old type, its previous values are restored.
        #
        # The structure of the dictionary is:
        #
        #   { (type(DisplayOpts), propName) : propValue }
        #
        # This also applies to the case where the
        # overlay type is changed from one type to
        # a related type (e.g. from VolumeOpts to
        # LabelOpts) - the values of all common
        # properties are copied to the new
        # DisplayOpts instance.
        self.__oldOptValues = td.TypeDict()

        # Set up listeners after caling Syncable.__init__,
        # so the callbacks don't get called during
        # synchronisation
        self.addListener(
            'overlayType',
            'Display_{}'.format(id(self)),
            self.__overlayTypeChanged)

        # The __overlayTypeChanged method creates
        # a new DisplayOpts instance - for this,
        # it needs to be able to access this
        # Display instance's parent (so it can
        # subsequently access a parent for the
        # new DisplayOpts instance). Therefore,
        # we do this after calling Syncable.__init__.
        self.__displayOpts = None
        self.__overlayTypeChanged()

        log.debug('{}.init ({})'.format(type(self).__name__, id(self)))
コード例 #9
0
from .vectoropts import RGBVectorOpts
from .vectoropts import LineVectorOpts
from .meshopts import MeshOpts
from .giftiopts import GiftiOpts
from .freesurferopts import FreesurferOpts
from .labelopts import LabelOpts
from .tensoropts import TensorOpts
from .shopts import SHOpts
from .mipopts import MIPOpts

from .displaycontext import InvalidOverlayError

OVERLAY_TYPES = td.TypeDict({
    'Image': [
        'volume', 'mask', 'rgbvector', 'linevector', 'label', 'sh', 'tensor',
        'mip'
    ],
    'Mesh': ['mesh'],
    'DTIFitTensor': ['tensor', 'rgbvector', 'linevector'],
})
"""This dictionary provides a mapping between all overlay classes,
and the possible values that the :attr:`Display.overlayType` property
may take for each of them.

For each overlay class, the first entry in the corresponding overlay type
list is used as the default overlay type.
"""

ALL_OVERLAY_TYPES = list(set(it.chain(*OVERLAY_TYPES.values())))
"""This attribute contains a list of all possible overlay types - see the
:attr:`.Display.overlayType` property and tge :data:`.display.OVERLAY_TYPES`
dictionary for more details.
コード例 #10
0
        # preserve the display space if
        # it was set to a filegroup image
        dspace = displayCtx.displaySpace
        if dspace in old and dspace.name in new:
            displayCtx.displaySpace = new[dspace.name]

        # Remove all of the
        # old filetree overlays
        idxs = range(len(overlayList))
        idxs = [i for i in idxs if overlayList[i] not in old]
        overlayList[:] = [overlayList[i] for i in idxs]


REPLACE = td.TypeDict({
    'MeshOpts': ['refImage'],
    'VolumeOpts': ['clipImage'],
    'VectorOpts': ['colourImage', 'modulateImage', 'clipImage']
})
"""This dict contains :class:`.DisplayOpts` properties which refer to other
images, and which need to be explicitly handled when the
:class:`OverlayManager` swaps a group of overlays in for another.
"""

SKIP = td.TypeDict({
    'DisplayOpts': ['bounds'],
    'NiftiOpts': ['transform'],
    'MeshOpts': ['vertexSet', 'vertexData', 'vertexDataIndex'],
})
"""This dict contains :class:`.DisplayOpts` properties which are not copied
when the :class:`OverlayManager` swaps a group of overlays in for an existing
group.
コード例 #11
0
_TOOLTIPS = td.TypeDict({
    'Display.name':
    fsltooltips.properties['Display.name'],
    'Display.overlayType':
    fsltooltips.properties['Display.overlayType'],
    'Display.alpha':
    fsltooltips.properties['Display.alpha'],
    'Display.brightness':
    fsltooltips.properties['Display.brightness'],
    'Display.contrast':
    fsltooltips.properties['Display.contrast'],
    'ColourMapOpts.displayRange':
    fsltooltips.properties['ColourMapOpts.displayRange'],
    'VolumeOpts.cmap':
    fsltooltips.properties['ColourMapOpts.cmap'],
    'VolumeOpts.negativeCmap':
    fsltooltips.properties['ColourMapOpts.negativeCmap'],
    'VolumeOpts.useNegativeCmap':
    fsltooltips.properties['ColourMapOpts.useNegativeCmap'],
    'MaskOpts.threshold':
    fsltooltips.properties['MaskOpts.threshold'],
    'MaskOpts.colour':
    fsltooltips.properties['MaskOpts.colour'],
    'LabelOpts.lut':
    fsltooltips.properties['LabelOpts.lut'],
    'LabelOpts.outline':
    fsltooltips.properties['LabelOpts.outline'],
    'LabelOpts.outlineWidth':
    fsltooltips.properties['LabelOpts.'
                           'outlineWidth'],
    'VectorOpts.modulateImage':
    fsltooltips.properties['VectorOpts.'
                           'modulateImage'],
    'VectorOpts.clipImage':
    fsltooltips.properties['VectorOpts.'
                           'clipImage'],
    'VectorOpts.clippingRange':
    fsltooltips.properties['VectorOpts.'
                           'clippingRange'],
    'LineVectorOpts.lineWidth':
    fsltooltips.properties['LineVectorOpts.'
                           'lineWidth'],
    'MeshOpts.colour':
    fsltooltips.properties['MeshOpts.colour'],
    'MeshOpts.outline':
    fsltooltips.properties['MeshOpts.outline'],
    'MeshOpts.outlineWidth':
    fsltooltips.properties['MeshOpts.'
                           'outlineWidth'],
    'MeshOpts.vertexSet':
    fsltooltips.properties['MeshOpts.vertexSet'],
    'MeshOpts.vertexData':
    fsltooltips.properties['MeshOpts.vertexData'],
    'TensorOpts.lighting':
    fsltooltips.properties['TensorOpts.'
                           'lighting'],
    'SHOpts.lighting':
    fsltooltips.properties['SHOpts.lighting'],
    'SHOpts.size':
    fsltooltips.properties['SHOpts.size'],
    'SHOpts.radiusThreshold':
    fsltooltips.properties['SHOpts.'
                           'radiusThreshold'],
})
コード例 #12
0
class OverlayInfoPanel(ctrlpanel.ControlPanel):
    """An ``OverlayInfoPanel`` is a :class:`.ControlPanel` which displays
    information about the currently selected overlay in a
    ``wx.html.HtmlWindow``. The currently selected overlay is defined by the
    :attr:`.DisplayContext.selectedOverlay` property. An ``OverlayInfoPanel``
    looks something like the following:

    .. image:: images/overlayinfopanel.png
       :scale: 50%
       :align: center

    Slightly different information is shown depending on the overlay type,
    and is generated by the following methods:

    ======================== =============================
    :class:`.Image`          :meth:`__getImageInfo`
    :class:`.FEATImage`      :meth:`__getFEATImageInfo`
    :class:`.MelodicImage`   :meth:`__getMelodicImageInfo`
    :class:`.DTIFitTensor`   :meth:`__getDTIFitTensorInfo`
    :class:`.Mesh`           :meth:`__getMeshInfo`
    :class:`.VTKMesh`        :meth:`__getVTKMeshInfo`
    :class:`.GiftiMesh`      :meth:`__getGiftiMeshInfo`
    :class:`.FreesurferMesh` :meth:`__getFreesurferMeshInfo`
    ======================== =============================
    """
    def __init__(self, parent, overlayList, displayCtx, frame):
        """Create an ``OverlayInfoPanel``.

        :arg parent:      The :mod:`wx` parent object.
        :arg overlayList: The :class:`.OverlayList` instance.
        :arg displayCtx:  The :class:`.DisplayContext` instance.
        :arg frame:       The :class:`.FSLeyesFrame` instance.
        """

        ctrlpanel.ControlPanel.__init__(self, parent, overlayList, displayCtx,
                                        frame)

        if USE_HTML2: self.__info = wxhtml.WebView.New(self)
        else: self.__info = HtmlWindow(self)

        self.__sizer = wx.BoxSizer(wx.HORIZONTAL)
        self.__sizer.Add(self.__info, flag=wx.EXPAND, proportion=1)

        self.SetSizer(self.__sizer)

        displayCtx.addListener('selectedOverlay', self.name,
                               self.__selectedOverlayChanged)
        overlayList.addListener('overlays', self.name,
                                self.__selectedOverlayChanged)

        self.__currentOverlay = None
        self.__currentDisplay = None
        self.__currentOpts = None
        self.__selectedOverlayChanged()

        self.SetMinSize((350, 500))
        self.Layout()

    def destroy(self):
        """Must be called when this ``OverlayInfoPanel`` is no longer
        needed. Removes some property listeners, and calls the
        :meth:`.ControlPanel.destroy` method.
        """

        self.displayCtx.removeListener('selectedOverlay', self.name)
        self.overlayList.removeListener('overlays', self.name)

        self.__deregisterOverlay()

        ctrlpanel.ControlPanel.destroy(self)

    def __selectedOverlayChanged(self, *a):
        """Called when the :class:`.OverlayList` or
        :attr:`.DisplayContext.selectedOverlay` changes. Refreshes the
        information shown on this ``OverlayInfoPanel``.
        """

        overlay = self.displayCtx.getSelectedOverlay()

        # Overlay list is empty
        if overlay is None:
            self.__info.SetPage('', '')
            self.__info.Refresh()
            return

        self.__deregisterOverlay()

        if overlay is not None:
            self.__registerOverlay(overlay)

        self.__updateInformation()

    _optProps = td.TypeDict({
        'Image': ['transform'],
        'Mesh': ['refImage', 'coordSpace'],
        'DTIFitTensor': ['transform'],
    })
    """This dictionary contains a list of :class:`.DisplayOpts` properties
    that, when changed, should result in the information being refreshed.
    It is used by the :meth:`__registerOverlay` and :meth:`__deregisterOverlay`
    methods.
    """

    def __registerOverlay(self, overlay):
        """Registers property listeners with the given overlay so the
        information can be refreshed when necessary.
        """

        display = self.displayCtx.getDisplay(overlay)
        opts = display.opts

        self.__currentOverlay = overlay
        self.__currentDisplay = display
        self.__currentOpts = opts

        display.addListener('name', self.name, self.__overlayNameChanged)
        display.addListener('overlayType', self.name,
                            self.__overlayTypeChanged)

        for propName in OverlayInfoPanel._optProps.get(overlay, []):
            opts.addListener(propName, self.name, self.__overlayOptsChanged)

    def __deregisterOverlay(self):
        """De-registers property listeners from the overlay that was
        previously registered via :meth:`__registerOverlay`.
        """

        if self.__currentOverlay is None:
            return

        overlay = self.__currentOverlay
        display = self.__currentDisplay
        opts = self.__currentOpts

        self.__currentOverlay = None
        self.__currentDisplay = None
        self.__currentOpts = None

        display.removeListener('name', self.name)
        display.removeListener('overlayType', self.name)

        for propName in OverlayInfoPanel._optProps[overlay]:
            opts.removeListener(propName, self.name)

    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 __overlayOptsChanged(self, *a):
        """Called when any :class:`.DisplayOpts` properties for the current
        overlay change. Updates the information display. The properties that
        trigger a refresh are  defined in the :attr:`_optProps` dictionary.
        """
        self.__updateInformation()

    def __updateInformation(self):
        """Refreshes the information shown on this ``OverlayInfoPanel``.
        Called by the :meth:`__selectedOverlayChanged` and
        :meth:`__overlayNameChanged` methods.
        """

        overlay = self.__currentOverlay
        display = self.__currentDisplay
        infoFunc = '_{}__get{}Info'.format(
            type(self).__name__,
            type(overlay).__name__)
        infoFunc = getattr(self, infoFunc, None)

        # Overlay is none, or the overlay
        # type is not supported
        if infoFunc is None:
            self.__info.SetPage('', '')
            self.__info.Refresh()
            return

        info = infoFunc(overlay, display)

        self.__info.SetPage(self.__formatOverlayInfo(info), '')
        self.__info.Refresh()

    def __getImageInfo(self, overlay, display, title=None, metadata=True):
        """Creates and returns an :class:`OverlayInfo` object containing
        information about the given :class:`.Image` overlay.

        :arg overlay: A :class:`.Image` instance.
        :arg display: The :class:`.Display` instance assocated with the
                      ``Image``.
        """

        img = overlay.nibImage
        hdr = overlay.header
        isNifti = overlay.niftiVersion >= 1
        hasMeta = metadata and len(overlay.metaKeys()) > 0
        opts = display.opts

        if isNifti: title = strings.labels[self, overlay]
        else: title = strings.labels[self, 'Analyze']

        if overlay.dataSource is not None and \
           fslbids.isBIDSFile(overlay.dataSource):
            metaSect = strings.labels[self, overlay, 'bidsMeta']
        else:
            metaSect = strings.labels[self, overlay, 'jsonMeta']

        info = OverlayInfo('{} - {}'.format(display.name, title))

        generalSect = strings.labels[self, 'general']
        dimSect = strings.labels[self, overlay, 'dimensions']
        xformSect = strings.labels[self, overlay, 'transform']
        orientSect = strings.labels[self, overlay, 'orient']

        info.addSection(generalSect)
        info.addSection(dimSect)
        info.addSection(xformSect)
        info.addSection(orientSect)

        if hasMeta:
            info.addSection(metaSect)

        displaySpace = strings.labels[self, overlay, 'displaySpace',
                                      opts.transform]

        if opts.transform == 'reference':
            dsImg = self.displayCtx.displaySpace
            if isinstance(dsImg, fslimage.Nifti):
                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))

        dataType = strings.nifti.get(('datatype', int(hdr['datatype'])),
                                     'Unknown')

        info.addInfo(strings.labels[self, 'niftiVersion'],
                     strings.nifti['version.{}'.format(overlay.niftiVersion)],
                     section=generalSect)
        info.addInfo(strings.labels[self, 'dataSource'],
                     overlay.dataSource,
                     section=generalSect)
        info.addInfo(strings.nifti['datatype'], dataType, section=generalSect)
        info.addInfo(strings.nifti['descrip'],
                     overlay.strval('descrip'),
                     section=generalSect)

        if isNifti:
            intent = strings.nifti.get(
                ('intent_code', int(hdr['intent_code'])), 'Unknown')

            info.addInfo(strings.nifti['intent_code'],
                         intent,
                         section=generalSect)
            info.addInfo(strings.nifti['intent_name'],
                         overlay.strval('intent_name'),
                         section=generalSect)

        info.addInfo(strings.nifti['aux_file'],
                     overlay.strval('aux_file'),
                     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)),
                     section=dimSect)

        for i in range(len(overlay.shape)):
            info.addInfo(strings.nifti['dim{}'.format(i + 1)],
                         str(overlay.shape[i]),
                         section=dimSect)

        # NIFTI images can specify different units.
        if isNifti:
            voxUnits = overlay.xyzUnits
            timeUnits = overlay.timeUnits

        # Assume mm / seconds for ANALYZE images
        # We are using nifti xyzt_unit code values
        # here
        else:
            voxUnits, timeUnits = 2, 8

        # Convert the unit codes into labels
        voxUnits = strings.nifti.get(('xyz_unit', voxUnits), 'INVALID CODE')
        timeUnits = strings.nifti.get(('t_unit', timeUnits), 'INVALID CODE')

        for i in range(len(overlay.shape)):

            pixdim = hdr['pixdim'][i + 1]

            if i < 3: pixdim = '{:0.4g} {}'.format(pixdim, voxUnits)
            elif i == 3: pixdim = '{:0.4g} {}'.format(pixdim, timeUnits)

            info.addInfo(strings.nifti['pixdim{}'.format(i + 1)],
                         pixdim,
                         section=dimSect)

        bounds = affine.axisBounds(overlay.shape[:3],
                                   opts.getTransform('voxel', 'world'))
        lens = bounds[1] - bounds[0]
        lens = 'X={:0.0f} {} Y={:0.0f} {} Z={:0.0f} {}'.format(
            lens[0], voxUnits, lens[1], voxUnits, lens[2], voxUnits)

        info.addInfo(strings.labels[self, overlay, 'size'],
                     lens,
                     section=dimSect)

        # For NIFTI images, we show both
        # the sform and qform matrices,
        # in addition to the effective
        # transformation
        if isNifti:

            qformCode = int(hdr['qform_code'])
            sformCode = int(hdr['sform_code'])

            info.addInfo(strings.nifti['transform'],
                         self.__formatArray(overlay.voxToWorldMat),
                         section=xformSect)

            info.addInfo(strings.nifti['sform_code'],
                         strings.anatomy['Nifti', 'space', sformCode],
                         section=xformSect)
            info.addInfo(strings.nifti['qform_code'],
                         strings.anatomy['Nifti', 'space', qformCode],
                         section=xformSect)

            if sformCode != constants.NIFTI_XFORM_UNKNOWN:
                sform = img.get_sform()
                info.addInfo(strings.nifti['sform'],
                             self.__formatArray(sform),
                             section=xformSect)

            if qformCode != constants.NIFTI_XFORM_UNKNOWN:
                try:
                    qform = img.get_qform()
                except Exception as e:
                    log.warning('Could not read qform from {}: {}'.format(
                        overlay.name, str(e)))
                    qform = np.eye(4) * np.nan
                info.addInfo(strings.nifti['qform'],
                             self.__formatArray(qform),
                             section=xformSect)

        # For ANALYZE images, we show
        # the scale/offset matrix
        else:
            info.addInfo(strings.nifti['affine'],
                         self.__formatArray(hdr.get_best_affine()),
                         section=xformSect)

        if overlay.getXFormCode() == constants.NIFTI_XFORM_UNKNOWN:
            storageOrder = 'unknown'
        elif overlay.isNeurological():
            storageOrder = 'neuro'
        else:
            storageOrder = 'radio'
        storageOrder = strings.nifti['storageOrder.{}'.format(storageOrder)]

        info.addInfo(strings.nifti['storageOrder'],
                     storageOrder,
                     section=orientSect)

        for i in range(3):
            xform = opts.getTransform('voxel', 'world')
            orient = overlay.getOrientation(i, xform)
            orient = '{} - {}'.format(
                strings.anatomy['Nifti', 'lowlong', orient],
                strings.anatomy['Nifti', 'highlong', orient])
            info.addInfo(strings.nifti['voxOrient.{}'.format(i)],
                         orient,
                         section=orientSect)

        for i in range(3):
            xform = np.eye(4)
            orient = overlay.getOrientation(i, xform)
            orient = '{} - {}'.format(
                strings.anatomy['Nifti', 'lowlong', orient],
                strings.anatomy['Nifti', 'highlong', orient])
            info.addInfo(strings.nifti['worldOrient.{}'.format(i)],
                         orient,
                         section=orientSect)

        if hasMeta:
            for k, v in overlay.metaItems():
                info.addInfo(k, str(v), metaSect)

        return info

    def __getFEATImageInfo(self, overlay, display):
        """Creates and returns an :class:`OverlayInfo` object containing
        information about the given :class:`.FEATImage` overlay.

        :arg overlay: A :class:`.FEATImage` instance.
        :arg display: The :class:`.Display` instance assocated with the
                      ``FEATImage``.
        """

        info = self.__getImageInfo(overlay, display)

        featInfo = [('analysisName', overlay.getAnalysisName()),
                    ('analysisDir', overlay.getFEATDir()),
                    ('numPoints', overlay.numPoints()),
                    ('numEVs', overlay.numEVs()),
                    ('numContrasts', overlay.numContrasts())]

        topLevel = overlay.getTopLevelAnalysisDir()
        report = overlay.getReportFile()

        if topLevel is not None:
            featInfo.insert(2, ('partOfAnalysis', topLevel))

        if report is not None:
            report = '<a href="file://{}">{}</a>'.format(report, report)
            featInfo.insert(2, ('report', report))

        secName = strings.labels[self, overlay, 'featInfo']
        info.addSection(secName)

        for k, v in featInfo:
            info.addInfo(strings.feat[k], v, section=secName)

        return info

    def __getMelodicImageInfo(self, overlay, display):
        """Creates and returns an :class:`OverlayInfo` object containing
        information about the given :class:`.MelodicImage` overlay.

        :arg overlay: A :class:`.MelodicImage` instance.
        :arg display: The :class:`.Display` instance assocated with the
                      ``MelodicImage``.
        """

        info = self.__getImageInfo(overlay, display)

        melInfo = [('dataFile', overlay.getDataFile()),
                   ('analysisDir', overlay.getMelodicDir()),
                   ('tr', overlay.tr),
                   ('numComponents', overlay.numComponents())]

        topLevel = overlay.getTopLevelAnalysisDir()
        report = overlay.getReportFile()

        if topLevel is not None:
            melInfo.insert(2, ('partOfAnalysis', topLevel))

        if report is not None:
            report = '<a href="file://{}">{}</a>'.format(report, report)
            melInfo.insert(2, ('report', report))

        secName = strings.labels[self, overlay, 'melodicInfo']
        info.addSection(secName)

        for k, v in melInfo:
            info.addInfo(strings.melodic[k], v, section=secName)

        return info

    def __getMeshInfo(self, overlay, display):
        """Creates and returns an :class:`OverlayInfo` object containing
        information about the given :class:`.Mesh` overlay.

        :arg overlay: A :class:`.Mesh` instance.
        :arg display: The :class:`.Display` instance assocated with the
                      ``Mesh``.
        """

        opts = display.opts
        refImg = opts.refImage

        modelInfo = [
            ('numVertices', overlay.vertices.shape[0]),
            ('numTriangles', overlay.indices.shape[0]),
        ]

        if refImg is None:
            modelInfo.append(
                ('displaySpace', strings.labels[self, overlay, 'coordSpace',
                                                'display']))

            mesh2worldXform = np.eye(4)

        else:

            refOpts = self.displayCtx.getOpts(refImg)
            dsImg = self.displayCtx.displaySpace
            displaySpace = strings.labels[self, refImg, 'displaySpace',
                                          refOpts.transform]
            coordSpace = strings.labels[self, overlay, 'coordSpace',
                                        opts.coordSpace].format(refImg.name)
            mesh2worldXform = affine.concat(
                refOpts.getTransform('display', 'world'),
                opts.getTransform('mesh', 'display'))

            if refOpts.transform == 'reference':
                dsDisplay = self.displayCtx.getDisplay(dsImg)
                displaySpace = displaySpace.format(dsDisplay.name)

            modelInfo.append(('refImage', refImg.dataSource))
            modelInfo.append(('coordSpace', coordSpace))
            modelInfo.append(('displaySpace', displaySpace))

        bounds = affine.transform(overlay.bounds, mesh2worldXform)
        lens = bounds[1] - bounds[0]
        lens = 'X={:0.0f} mm Y={:0.0f} mm Z={:0.0f} mm'.format(*lens)

        modelInfo.append(('size', lens))

        info = OverlayInfo('{} - {}'.format(display.name,
                                            strings.labels[self, overlay]))

        info.addInfo(strings.labels[self, 'dataSource'], overlay.dataSource)

        for name, value in modelInfo:
            info.addInfo(strings.labels[self, overlay, name], value)

        return info

    def __getVTKMeshInfo(self, overlay, display):
        """Creates and returns an :class:`OverlayInfo` object containing
        information about the given :class:`.VTKMesh` overlay.

        :arg overlay: A :class:`.VTKMesh` instance.
        :arg display: The :class:`.Display` instance assocated with the
                      ``VTKMesh``.
        """

        info = self.__getMeshInfo(overlay, display)
        info.title = strings.labels[self, overlay]

        return info

    def __getGiftiMeshInfo(self, overlay, display):
        """Creates and returns an :class:`OverlayInfo` object containing
        information about the given :class:`.GiftiMesh` overlay.

        :arg overlay: A :class:`.GiftiMesh` instance.
        :arg display: The :class:`.Display` instance assocated with the
                      ``GiftiMesh``.
        """

        info = self.__getMeshInfo(overlay, display)
        info.title = strings.labels[self, overlay]

        return info

    def __getFreesurferMeshInfo(self, overlay, display):
        """Creates and returns an :class:`OverlayInfo` object containing
        information about the given :class:`.FreesurferMesh` overlay.

        :arg overlay: A :class:`.FreesurferMesh` instance.
        :arg display: The :class:`.Display` instance assocated with the
                      ``FreesurferMesh``.
        """

        info = self.__getMeshInfo(overlay, display)
        info.title = strings.labels[self, overlay]

        return info

    def __getDTIFitTensorInfo(self, overlay, display):
        """Creates and returns an :class:`OverlayInfo` object containing
        information about the given :class:`.DTIFitTensor` overlay.

        :arg overlay: A :class:`.DTIFitTensor` instance.
        :arg display: The :class:`.Display` instance assocated with the
                      ``DTIFitTensor``.
        """
        info = self.__getImageInfo(overlay.L1(), display)

        generalSect = strings.labels[self, 'general']
        dataSource = strings.labels[self, 'dataSource']

        info.title = strings.labels[self, overlay]
        info.sections[generalSect][dataSource] = overlay.dataSource

        tensorInfo = [
            ('v1', overlay.V1().dataSource),
            ('v2', overlay.V2().dataSource),
            ('v3', overlay.V3().dataSource),
            ('l1', overlay.L1().dataSource),
            ('l2', overlay.L2().dataSource),
            ('l3', overlay.L3().dataSource),
        ]

        section = strings.labels[self, overlay, 'tensorInfo']

        info.addSection(section)

        for name, val in tensorInfo:
            info.addInfo(strings.tensor[name], val, section)

        return info

    def __getDicomImageInfo(self, overlay, display):
        """Creates and returns an :class:`OverlayInfo` object containing
        information about the given :class:`.DicomImage` overlay.

        :arg overlay: A :class:`.DicomImage` instance.
        :arg display: The :class:`.Display` instance assocated with the
                      ``DicomImage``.
        """

        info = self.__getImageInfo(overlay, display, metadata=False)
        dicomInfo = strings.labels[self, overlay, 'dicomMeta']

        info.addInfo(strings.labels[self, overlay, 'dicomDir'],
                     overlay.dicomDir)

        info.addSection(dicomInfo)

        for k, v in overlay.metaItems():
            info.addInfo(k, str(v), dicomInfo)

        return info

    def __getMGHImageInfo(self, overlay, display):
        """Creates and returns an :class:`OverlayInfo` object containing
        information about the given :class:`.MGHImage` overlay.

        :arg overlay: A :class:`.MGHImage` instance.
        :arg display: The :class:`.Display` instance assocated with the
                      ``DicomImage``.
        """

        info = self.__getImageInfo(overlay, display)
        filename = overlay.mghImageFile

        if filename is not None:
            info.addInfo(strings.labels[self, overlay, 'filename'], filename)

        return info

    def __formatArray(self, array):
        """Creates and returns a string containing a HTML table which
        formats the data in the given ``numpy.array``.
        """

        lines = []

        lines.append('<table border="0" style="font-size: small;">')

        for rowi in range(array.shape[0]):

            lines.append('<tr>')

            for coli in range(array.shape[1]):
                lines.append('<td>{:0.4g}</td>'.format(array[rowi, coli]))
            lines.append('</tr>')

        lines.append('</table>')

        return ''.join(lines)

    def __formatOverlayInfo(self, info):
        """Creates and returns a string containing some HTML which formats
        the information in the given ``OverlayInfo`` instance.
        """
        lines = []

        lines.append('<html>')
        lines.append('<body style="font-family: '
                     'sans-serif; font-size: small;">')
        lines.append('<h2>{}</h2>'.format(info.title))

        sections = []

        if len(info.info) > 0:
            sections.append((None, list(info.info.items())))

        for secName, secInf in info.sections.items():
            sections.append((secName, list(secInf.items())))

        for i, (secName, secInf) in enumerate(sections):

            lines.append('<div style="float:left; margin: 5px; '
                         'background-color: #f0f0f0;">')

            if secName is not None:
                lines.append('<h3>{}</h3>'.format(secName))

            lines.append('<table border="0" style="font-size: small;">')

            for i, (infName, infData) in enumerate(secInf):

                if i % 2: bgColour = '#f0f0f0'
                else: bgColour = '#cdcdff'

                lines.append('<tr bgcolor="{}">'
                             '<td><b>{}</b></td>'
                             '<td>{}</td></tr>'.format(bgColour, infName,
                                                       infData))

            lines.append('</table>')
            lines.append('</div>')

        lines.append('</body></html>')

        return '\n'.join(lines)
コード例 #13
0
def test_class_hierarchy():

    class A(object):
        pass

    class B(A):
        pass

    td = typedict.TypeDict()
    td['A.a'] = 'A.a'
    td['A.b'] = 'A.b'
    td['A.c'] = 'A.c'
    td['A.d'] = 'A.d'
    td['B.a'] = 'B.a'
    td['B.b'] = 'B.b'
    td['B.1'] = 'B.1'
    td['B.2'] = 'B.2'

    a = A()
    b = B()

    assert td[A, 'a'] == 'A.a'
    assert td[A, 'b'] == 'A.b'
    assert td[A, 'c'] == 'A.c'
    assert td[A, 'd'] == 'A.d'

    assert td['A.a']  == 'A.a'
    assert td['A.b']  == 'A.b'
    assert td['A.c']  == 'A.c'
    assert td['A.d']  == 'A.d'

    assert td.get((A, 'a')) == 'A.a'
    assert td.get((A, 'b')) == 'A.b'
    assert td.get((A, 'c')) == 'A.c'
    assert td.get((A, 'd')) == 'A.d'

    assert td.get((A, 'a'), allhits=True) == ['A.a']
    assert td.get((A, 'b'), allhits=True) == ['A.b']
    assert td.get((A, 'c'), allhits=True) == ['A.c']
    assert td.get((A, 'd'), allhits=True) == ['A.d']

    assert td.get((a, 'a'), allhits=True) == ['A.a']
    assert td.get((a, 'b'), allhits=True) == ['A.b']
    assert td.get((a, 'c'), allhits=True) == ['A.c']
    assert td.get((a, 'd'), allhits=True) == ['A.d']

    assert td[B, 'a'] == 'B.a'
    assert td[B, 'b'] == 'B.b'
    assert td[B, '1'] == 'B.1'
    assert td[B, '2'] == 'B.2'

    assert td['B.a']  == 'B.a'
    assert td['B.b']  == 'B.b'
    assert td['B.1']  == 'B.1'
    assert td['B.2']  == 'B.2'

    assert td.get('B.a') == 'B.a'
    assert td.get('B.b') == 'B.b'
    assert td.get('B.1') == 'B.1'
    assert td.get('B.2') == 'B.2'

    assert td.get((B, 'a')) == 'B.a'
    assert td.get((B, 'b')) == 'B.b'
    assert td.get((B, '1')) == 'B.1'
    assert td.get((B, '2')) == 'B.2'
    assert td.get((b, 'a')) == 'B.a'
    assert td.get((b, 'b')) == 'B.b'
    assert td.get((b, '1')) == 'B.1'
    assert td.get((b, '2')) == 'B.2'

    with pytest.raises(KeyError):
        td['B.c']
    with pytest.raises(KeyError):
        td['B.d']

    assert td[B, 'c'] == 'A.c'
    assert td[B, 'd'] == 'A.d'
    assert td[b, 'c'] == 'A.c'
    assert td[b, 'd'] == 'A.d'

    assert td.get('B.a',    allhits=True) == ['B.a']
    assert td.get('B.b',    allhits=True) == ['B.b']
    assert td.get('B.1',    allhits=True) == ['B.1']
    assert td.get('B.2',    allhits=True) == ['B.2']
    assert td.get((B, 'a'), allhits=True) == ['B.a', 'A.a']
    assert td.get((B, 'b'), allhits=True) == ['B.b', 'A.b']
    assert td.get((B, '1'), allhits=True) == ['B.1']
    assert td.get((B, '2'), allhits=True) == ['B.2']
    assert td.get((B, 'c'), allhits=True) == ['A.c']
    assert td.get((B, 'd'), allhits=True) == ['A.d']

    assert td.get((b, 'a'), allhits=True) == ['B.a', 'A.a']
    assert td.get((b, 'b'), allhits=True) == ['B.b', 'A.b']
    assert td.get((b, '1'), allhits=True) == ['B.1']
    assert td.get((b, '2'), allhits=True) == ['B.2']
    assert td.get((b, 'c'), allhits=True) == ['A.c']

    assert td.get((B, 'a'), allhits=False, bykey=True) == 'B.a'
    assert td.get((B, 'b'), allhits=False, bykey=True) == 'B.b'
    assert td.get((B, '1'), allhits=False, bykey=True) == 'B.1'
    assert td.get((B, '2'), allhits=False, bykey=True) == 'B.2'

    assert td.get((B, 'a'), allhits=True,  bykey=True) == {('A', 'a') : 'A.a',
                                                           ('B', 'a') : 'B.a'}
    assert td.get((B, 'b'), allhits=True,  bykey=True) == {('A', 'b') : 'A.b',
                                                           ('B', 'b') : 'B.b'}
    assert td.get((B, '1'), allhits=True,  bykey=True) == {('B', '1') : 'B.1'}
    assert td.get((B, '2'), allhits=True,  bykey=True) == {('B', '2') : 'B.2'}
    assert td.get((B, 'c'), allhits=True,  bykey=True) == {('A', 'c') : 'A.c'}
    assert td.get((B, 'd'), allhits=True,  bykey=True) == {('A', 'd') : 'A.d'}

    assert td.get((b, 'a'), allhits=True,  bykey=True) == {('A', 'a') : 'A.a',
                                                           ('B', 'a') : 'B.a'}
    assert td.get((b, 'b'), allhits=True,  bykey=True) == {('A', 'b') : 'A.b',
                                                           ('B', 'b') : 'B.b'}
    assert td.get((b, '1'), allhits=True,  bykey=True) == {('B', '1') : 'B.1'}
    assert td.get((b, '2'), allhits=True,  bykey=True) == {('B', '2') : 'B.2'}
    assert td.get((b, 'c'), allhits=True,  bykey=True) == {('A', 'c') : 'A.c'}
    assert td.get((b, 'd'), allhits=True,  bykey=True) == {('A', 'd') : 'A.d'}

    assert td.get((A, 'a'), allhits=True,  bykey=True) == {('A', 'a') : 'A.a'}
    assert td.get((A, 'b'), allhits=True,  bykey=True) == {('A', 'b') : 'A.b'}
    assert td.get((A, 'c'), allhits=True,  bykey=True) == {('A', 'c') : 'A.c'}
    assert td.get((A, 'd'), allhits=True,  bykey=True) == {('A', 'd') : 'A.d'}
コード例 #14
0
ファイル: __init__.py プロジェクト: neurodebian/fsleyes
from .vectoropts     import VectorOpts
from .vectoropts     import RGBVectorOpts
from .vectoropts     import LineVectorOpts
from .meshopts       import MeshOpts
from .giftiopts      import GiftiOpts
from .labelopts      import LabelOpts
from .tensoropts     import TensorOpts
from .shopts         import SHOpts

from .displaycontext import InvalidOverlayError


OVERLAY_TYPES = td.TypeDict({

    'Image'        : ['volume',     'mask',  'rgbvector',
                      'linevector', 'label', 'sh', 'tensor'],
    'TriangleMesh' : ['mesh'],
    'GiftiSurface' : ['giftimesh'],
    'DTIFitTensor' : ['tensor', 'rgbvector', 'linevector'],
})
"""This dictionary provides a mapping between all overlay classes,
and the possible values that the :attr:`Display.overlayType` property
may take for each of them.

For each overlay class, the first entry in the corresponding overlay type
list is used as the default overlay type.
"""


ALL_OVERLAY_TYPES = list(set(it.chain(*OVERLAY_TYPES.values())))
"""This attribute contains a list of all possible overlay types - see the
:attr:`.Display.overlayType` property and tge :data:`.display.OVERLAY_TYPES`