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
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'
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
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
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)
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', })
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)
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)))
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.
# 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.
_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'], })
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)
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'}
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`