#
# reloadoverlay.py - The ReloadOverlayAction class.
#
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#
"""This module provides the :class:`ReloadOverlayAction`, a global action
which reloads the currently selected overlay from disk.
"""
import logging
import os.path as op
import fsl.data.image as fslimage
import fsleyes_widgets.utils.status as status
import fsleyes_props as props
from . import base
from . import removeoverlay
log = logging.getLogger(__name__)
[docs]class ReloadOverlayAction(base.Action):
"""The ``ReloadOverlayAction`` reloads the currently selected overlay
from disk. Currently only :class:`.Image` overlays are supported.
"""
[docs] def __init__(self, overlayList, displayCtx, frame):
"""Create a ``ReloadOverlayAction``.
:arg overlayList: The :class:`.OverlayList`.
:arg displayCtx: The :class:`.DisplayContext`.
:arg frame: The :class:`.FSLeyesFrame`.
"""
base.Action.__init__(
self, overlayList, displayCtx, self.__reloadOverlay)
self.__frame = frame
self.__name = '{}_{}'.format(type(self).__name__, id(self))
overlayList.addListener('overlays',
self.__name,
self.__selectedOverlayChanged)
displayCtx .addListener('selectedOverlay',
self.__name,
self.__selectedOverlayChanged)
self.__selectedOverlayChanged()
[docs] def destroy(self):
"""Must be called when this ``ReloadOverlayAction`` is no longer
required. Removes some property listeners, and calls
:meth:`.Action.destroy`.
"""
self.overlayList.removeListener('overlays', self.__name)
self.displayCtx .removeListener('selectedOverlay', self.__name)
base.Action.destroy(self)
def __selectedOverlayChanged(self, *a):
"""Called when the currently selected overlay changes. Enables/disables
this ``Action`` depending on the type of the newly selected overlay.
"""
ovl = self.displayCtx.getSelectedOverlay()
self.enabled = (ovl is not None) and \
(ovl.dataSource is not None) and \
type(ovl) == fslimage.Image and \
op.exists(ovl.dataSource)
def __reloadOverlay(self):
"""Reloads the currently selected overlay from disk.
"""
ovl = self.displayCtx.getSelectedOverlay()
if ovl is None or type(ovl) != fslimage.Image:
raise RuntimeError('Only Image overlays can be reloaded')
index = self.overlayList.index(ovl)
order = self.displayCtx.overlayOrder[:]
dataSource = ovl.dataSource
status.update('Reloading {}...'.format(dataSource))
# Get refs to all DisplayContexts -
# the master one, and the one for
# every view panel.
displayCtxs = [self.displayCtx]
viewPanels = self.__frame.viewPanels
displayCtxs += [vp.displayCtx for vp in viewPanels]
# Now get refs to all Display and
# DisplayOpts instances for this
# overlay.
displays = []
opts = []
for dctx in displayCtxs:
displays.append(self.displayCtx.getDisplay(ovl))
opts .append(self.displayCtx.getOpts( ovl))
# Turn those references into
# {prop : value} dictionaries
for i in range(len(displays)):
d = displays[i]
o = opts[ i]
displayProps = d.getAllProperties()[0]
optProps = o.getAllProperties()[0]
displays[i] = {p : getattr(d, p) for p in displayProps}
opts[ i] = {p : getattr(o, p) for p in optProps}
# Now that we've got all the settings
# for this overlay, we'll remove it
# from the list. If removeOverlay
# returns False, it probably means
# the user cancelled the action.
if not removeoverlay.removeOverlay(self.overlayList,
self.displayCtx,
ovl,
'reloadoverlay.unsaved'):
return
# Now we re-load the overlay, and add it
# back in to the list at the same location
ovl = fslimage.Image(dataSource)
self.overlayList.insert(index, ovl)
# Make sure the overlay is selected,
# and the display order is preserved
self.displayCtx.selectOverlay(ovl)
self.displayCtx.overlayOrder = order
# The last step is to re-apply all of the
# Display/DisplayOpts settings to the
# newly created Display/DisplayOpts
# instances.
for i, dctx in enumerate(displayCtxs):
displayProps = displays[i]
optProps = opts[ i]
d = dctx.getDisplay(ovl)
for prop, val in displayProps.items():
if not d.propertyIsEnabled(prop):
continue
try:
setattr(d, prop, val)
except props.DisabledError:
continue
# Get a ref to the DisplayOpts instance
# after we have configured the Display,
# as its overlay type may have changed
# (and hence the DisplayOpts instance
# may have been re-created).
o = dctx.getOpts(ovl)
for prop, val in optProps.items():
if not o.propertyIsEnabled(prop):
continue
setattr(o, prop, val)
status.update('{} reloaded.'.format(dataSource))