Skip to content
Snippets Groups Projects
Commit 76fd1cd7 authored by Paul McCarthy's avatar Paul McCarthy
Browse files

Documentation for actions package.

parent d8b198f4
No related branches found
No related tags found
No related merge requests found
fsl.fsleyes.actions package
===========================
Submodules
----------
.. toctree::
fsl.fsleyes.actions.copyoverlay
fsl.fsleyes.actions.loadcolourmap
fsl.fsleyes.actions.openfile
fsl.fsleyes.actions.openstandard
fsl.fsleyes.actions.saveoverlay
Module contents
---------------
.. automodule:: fsl.fsleyes.actions
:members:
:undoc-members:
......
......@@ -5,17 +5,27 @@
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#
"""This package provides a collection of actions, and two package-level
classes - the :class:`Action` class, and the :class:`ActionProvider` class.
classes - the :class:`Action` class and the :class:`ActionProvider` class.
The :class:`Action` class represents some sort of action which may be
performed, enabled and disabled, and may be bound to a GUI menu item or
button.
Some 'global' actions are provided in this package, for example the
:class:`.OpenFileAction`, and the :class:`.OpenStandardAction`.
The :class:`ActionProvider` class represents some entity which can perform one
or more actions. As the :class:`.FSLEyesPanel` class derives from
Some 'global' actions are also provided in this package:
.. autosummary::
~fsl.fsleyes.actions.copyoverlay
~fsl.fsleyes.actions.loadcolourmap
~fsl.fsleyes.actions.openfile
~fsl.fsleyes.actions.openstandard
~fsl.fsleyes.actions.saveoverlay
The :class:`ActionProvider` class represents some entity which can perform
one or more actions. As the :class:`.FSLEyesPanel` class derives from
:class:`ActionProvider` pretty much everything in FSLEyes is an
:class:`ActionProvider`.
"""
......@@ -53,6 +63,14 @@ class ActionButton(props.Button):
:class:`Action` instance.
"""
def __init__(self, actionName, classType=None, **kwargs):
"""Create an ``ActionButton``.
:arg actionName: Name of the action
:arg classType: The type which defines the action.
:arg kwargs: Passed to the :class:`props.Button` constructor.
"""
self.name = actionName
......@@ -102,12 +120,16 @@ class Action(props.HasProperties):
def __init__(self, overlayList, displayCtx, action=None):
"""
"""Create an ``Action``.
:arg overlayList: An :class:`.OverlayList` instance
containing the list of overlays being displayed.
:arg displayCtx: A :class:`.DisplayContext` instance defining how
the overlays are to be displayed.
:arg action: The action function. If not provided, assumes that
the :meth:`doAction` method has been overridden.
"""
self._overlayList = overlayList
self._displayCtx = displayCtx
......@@ -125,7 +147,14 @@ class Action(props.HasProperties):
def bindToWidget(self, parent, evType, widget):
"""Binds this action to the given :class:`wx.Button`. """
"""Binds this action to the given :mod:`wx` widget.
:arg parent: The :mod:`wx` object on which the event should be bound.
:arg evType: The :mod:`wx` event type.
:arg widget: The :mod:`wx` widget.
"""
def wrappedAction(ev):
self.doAction()
......@@ -249,8 +278,7 @@ class ActionProvider(props.SyncableHasProperties):
def getAction(self, name):
"""Return the :class:`Action` object of the given name.
"""
"""Return the :class:`Action` object of the given name. """
return self.__actions[name]
......
#!/usr/bin/env python
#
# copyoverlay.py -
# copyoverlay.py - Action which copies the currently selected overlay.
#
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#
"""This module provides the :class:`CopyOverlayAction`, a global action
which creates a copy of the currently selected overlay.
"""
import logging
log = logging.getLogger(__name__)
import numpy as np
......@@ -14,9 +16,19 @@ import fsl.fsleyes.actions as actions
import fsl.data.image as fslimage
log = logging.getLogger(__name__)
class CopyOverlayAction(actions.Action):
"""The ``CopyOverlayAction`` does as its name suggests - it creates a
copy of the currently selected overlay.
"""
def __init__(self, *args, **kwargs):
"""Create a ``CopyOverlayAction``. All arguments are passed through
to the :meth:`.Action.__init__` constructor.
"""
actions.Action.__init__(self, *args, **kwargs)
self._displayCtx .addListener('selectedOverlay',
......@@ -30,10 +42,31 @@ class CopyOverlayAction(actions.Action):
def __selectedOverlayChanged(self, *a):
self.enabled = self._displayCtx.getSelectedOverlay() is not None
"""Called when the selected overlay, or overlay list, changes.
Enables/disables this action depending on the nature of the selected
overlay.
"""
overlay = self._displayCtx.getSelectedOverlay()
self.enabled = (overlay is not None) and \
isinstance(overlay, fslimage.Image)
def destroy(self):
"""Removes listeners from the :class:`.DisplayContext` and
:class:`.OverlayList`, and calls :meth:`.Action.destroy`.
"""
self._displayCtx .removeListener('selectedOverlay', self._name)
self._overlayList.removeListener('overlays', self._name)
actions.Action.destroy(self)
def doAction(self):
"""Creates a copy of the currently selected overlay, and inserts it
into the :class:`.OverlayList`.
"""
ovlIdx = self._displayCtx.selectedOverlay
overlay = self._overlayList[ovlIdx]
......
#!/usr/bin/env python
#
# loadcolourmap.py -
# loadcolourmap.py - Load a colour map file.
#
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#
"""This module provides the :class:`LoadColourMapAction`, which allows the
user to load a new colour map.
"""
import logging
import os.path as op
......@@ -13,7 +16,6 @@ import fsl.fsleyes.actions as actions
import fsl.fsleyes.colourmaps as fslcmap
log = logging.getLogger(__name__)
......@@ -21,8 +23,26 @@ _stringID = 'actions.loadcolourmap.'
class LoadColourMapAction(actions.Action):
"""The ``LoadColourMapAction`` allows the yser to select a colour
map file and give it a name.
The loaded colour map is then registered with the :mod:`.colourmaps`
module. The user is also given the option to permanently save the
loaded colour map in *FSLeyes*.
"""
def doAction(self):
"""This method does the following:
1. Prompts the user to select a colour map file
2. Prompts the user to name the new colour map.
3. Registers the colour map with the :mod:`.colourmaps` module.
4. Asks the user if they want the colour map installed, and installs
it if they do.
"""
import wx
......
#!/usr/bin/env python
#
# openfileaction.py -
# openfileaction.py - Action which allows the user to load overlay files.
#
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#
"""This module provides the :class:`OpenFileAction`, which allows the user to
load overlay files into the :class:`.OverlayList`.
"""
import logging
log = logging.getLogger(__name__)
import fsl.fsleyes.actions as actions
log = logging.getLogger(__name__)
class OpenFileAction(actions.Action):
"""The ``OpenFileAction`` allows the user to add files to the
:class:`.OverlayList`. This functionality is provided by the
:meth:`.OverlayList.addOverlays` method.
"""
def doAction(self):
"""Calls :meth:`.OverlayList.addOverlays` method. If overlays were added,
updates the :attr:`.DisplayContext.selectedOverlay` accordingly.
"""
if self._overlayList.addOverlays():
self._displayCtx.selectedOverlay = \
......
#!/usr/bin/env python
#
# openfileaction.py -
# openstandardaction.py - Action which allows the user to open standard
# images.
#
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#
"""This module provides the :class:`OpenStandardAction`, which allows the user
to load in standard space images from the ``$FSLDIR/data/standard/`` directory.
"""
import os
import os.path as op
import logging
log = logging.getLogger(__name__)
import fsl.fsleyes.actions as actions
log = logging.getLogger(__name__)
class OpenStandardAction(actions.Action):
"""The ``OpenStandardAction`` prompts the user to open one or more
overlays, using ``$FSLDIR/data/standard/`` as the default directory.
"""
def __init__(self, overlayList, displayCtx):
actions.Action.__init__(self, overlayList, displayCtx)
# disable the 'add standard' menu
# item if $FSLDIR is not set
# disable this action
# if $FSLDIR is not set
fsldir = os.environ.get('FSLDIR', None)
if fsldir is not None:
self._stddir = op.join(fsldir, 'data', 'standard')
self.__stddir = op.join(fsldir, 'data', 'standard')
else:
self._stddir = None
self.enabled = False
self.__stddir = None
self.enabled = False
def doAction(self):
if self._overlayList.addOverlays(self._stddir, addToEnd=False):
"""Calls the :meth:`.OverlayList.addOverlays` method. If the user
added some overlays, updates the
:attr:`.DisplayContext.selectedOverlay` accordingly.
"""
if self._overlayList.addOverlays(self.__stddir, addToEnd=False):
self._displayCtx.selectedOverlay = self._displayCtx.overlayOrder[0]
#!/usr/bin/env python
#
# saveoverlay.py -
# saveoverlay.py - Save the currently selected overlay.
#
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#
"""This module provides the :class:`SaveOverlayAction`, which allows the user
to save the currently selected overlay.
"""
import logging
......@@ -15,8 +19,15 @@ log = logging.getLogger(__name__)
class SaveOverlayAction(actions.Action):
"""The ``SaveOverlayAction`` allows the user to save the currently
selected overlay, if it has been edited, or only exists in memory.
"""
def __init__(self, *args, **kwargs):
"""Create a ``SaveOverlayAction``. All arguments are passed through
to the :meth:`.Action.__init__` constructor.
"""
actions.Action.__init__(self, *args, **kwargs)
self._displayCtx .addListener('selectedOverlay',
......@@ -28,8 +39,23 @@ class SaveOverlayAction(actions.Action):
self.__selectedOverlayChanged()
def destroy(self):
"""Removes listeners from the :class:`.DisplayContext` and
:class:`.OverlayList`, and calls :meth:`.Action.destroy`.
"""
self._displayCtx .removeListener('selectedOverlay', self._name)
self._overlayList.removeListener('overlays', self._name)
actions.Action.destroy(self)
def __selectedOverlayChanged(self, *a):
"""Called when the selected overlay, or overlay list changes.
If the overlay is a :class:`.Image`, and it has unsaved changes,
this action is enabled; otherwise it is disabled.
"""
overlay = self._displayCtx.getSelectedOverlay()
......@@ -44,7 +70,11 @@ class SaveOverlayAction(actions.Action):
continue
ovl.removeListener('saved', self._name)
# Register a listener on the saved property
# of the currently selected image, so we can
# enable the save action when the image
# becomes 'unsaved', and vice versa.
if ovl is overlay:
ovl.addListener('saved',
self._name,
......@@ -52,6 +82,13 @@ class SaveOverlayAction(actions.Action):
def __overlaySaveStateChanged(self, *a):
"""Called when the :attr:`.Image.saved` property of the currently
selected overlay changes. Enables/disables this ``SaveOverlayAction``
accordingly.
This is only applicable if the current overlay is a :class:`.Image` -
see the :meth:`__selectedOverlayChanged` method.
"""
overlay = self._displayCtx.getSelectedOverlay()
......@@ -65,6 +102,9 @@ class SaveOverlayAction(actions.Action):
def doAction(self):
"""Saves the currently selected overlay (only if it is a
:class:`.Image`), by a call to :meth:`.Image.save`.
"""
overlay = self._displayCtx.getSelectedOverlay()
......
......@@ -4,41 +4,101 @@
#
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#
"""This module encapsulates the logic for parsing command line arguments
which specify a scene to be displayed in FSLEyes. This logic is shared
between fsleyes.py and render.py.
"""This module encapsulates the logic for parsing command line arguments which
specify a scene to be displayed in *FSLeyes*. This logic is shared between
the :mod:`~fsl.tools.fsleyes` and :mod:`~fsl.tools.render` tools. This module
make use of the command line generation features of the :mod:`props` package.
The functions in this module make use of the command line generation
features of the :mod:`props` package.
There are a lot of command line arguments made available to the user,
broadly split into the following groups:
- *Main* arguments control the overall scene display, such as the
display type (orthographic or lightbox), the displayed location,
display type (e.g. orthographic or lightbox), the displayed location,
and whether to show a colour bar.
- *Display* arguments control the display for a single overlay file (e.g.
an image), such as interpolation, colour map, etc.
The main entry points of this module are:
- :func:`parseArgs`:
Parses command line arguments, and returns an :class:`argparse.Namespace`
object.
- :func:`handleSceneArgs`:
Configures :class:`.FSLEyesFrame` and :class:`.DisplayContext` instances
according to the arguments contained in a given
:class:`~argparse.Namespace` object.
- :func:`handleOverlayArgs`:
Loads and configures the display of any overlay files specified by a given
:class:`~argparse.Namespace` object.
- *Display* arguments control the display for a single overlay file (e.g.
a NIFTI1 image), such as interpolation, colour map, etc.
This module provides the following functions:
.. autosummary::
:nosignatures:
parseArgs
applySceneArgs
generateSceneArgs
generateOverlayArgs
applyOverlayArgs
-------------------------------
Adding new command line options
-------------------------------
Most classes in *FSleyes* derive from the :class:`.HasProperties` class of the
:mod:`props` package. Therefore, with only a couple of excpetions, the
processing of nearly all *FSLeyes* command line arguments is completely
automatic.
Therefore, adding a new command line option is fairly easy. For example,
let's say you have added a new property on the :class:`.ModelOpts` class,
called ``rotation``::
class ModelOpts(fsldisplay.DisplayOpts):
# .
# .
# .
rotation = props.Int(minval=0, maxval=360, clamped=True)
# .
# .
# .
To make this new propery settable via the command line, you need to:
1. Add an entry to the :data:`OPTIONS` dictionary::
OPTIONS = td.TypeDict({
# .
# .
# .
'ModelOpts' : ['colour',
'outline',
'outlineWidth',
'refImage',
'rotation'],
# .
# .
# .
})
2. Specify the command line flags to use, in the :data:`ARGUMENTS`
dictionary::
ARGUMENTS = td.TypeDict({
# .
# .
# .
'ModelOpts.rotation' : ('mr', 'modelRotation'),
# .
# .
# .
})
3. Add a description in the :data:`HELP` dictionary::
HELP = td.TypeDict({
# .
# .
# .
'ModelOpts.rotation' : 'Rotate the model by this much',
# .
# .
# .
})
"""
import sys
......@@ -66,8 +126,10 @@ log = logging.getLogger(__name__)
def concat(lists):
"""Concatenates a list of lists. Used a few times, and writing
concat(lists) is nicer-looking than writing lambda blah blah each time.
"""Concatenates a list of lists.
This function is used a few times, and writing concat(lists) is
nicer-looking than writing lambda blah blah each time.
"""
return reduce(lambda a, b: a + b, lists)
......@@ -147,6 +209,14 @@ OPTIONS = td.TypeDict({
'outline',
'outlineWidth'],
})
"""This dictionary defines all of the options which are exposed on the command
line.
With the exception of ``Main``, every key is the name of a
:class:`.HasProperties` class, and the list of values are the names of
properties on that class.
"""
# Headings for each of the option groups
GROUPNAMES = td.TypeDict({
......@@ -164,6 +234,10 @@ GROUPNAMES = td.TypeDict({
'ModelOpts' : 'Model options',
'LabelOpts' : 'Label options',
})
"""Command line arguments are grouped according to the class to which
they are applied (see the :data:`ARGUMENTS` dictionary). This dictionary
defines descriptions for ecah command line group.
"""
# Short/long arguments for all of those options
#
......@@ -252,6 +326,18 @@ ARGUMENTS = td.TypeDict({
'LabelOpts.outline' : ('lo', 'labelOutline'),
'LabelOpts.outlineWidth' : ('lw', 'labelOutlineWidth'),
})
"""This dictionary defines the short and long command line flags to be used
for every option.
.. note:: 1. There cannot be any collisions between the main options, the
:class:`.SceneOpts` options, the :class:`.OrthOpts` options,
and the :class:`.LightBoxOpts` options.
2. There cannot be any collisions between the :class:`.Display`
options and any of the :class:`.DisplayOpts` options.
3. There *can* be collisions between these two groups though.
"""
# Help text for all of the options
HELP = td.TypeDict({
......@@ -342,6 +428,7 @@ HELP = td.TypeDict({
'LabelOpts.outline' : 'Show label outlines',
'LabelOpts.outlineWidth' : 'Label outline width',
})
"""This dictionary defines the help text for all command line options."""
# Extra settings for some properties, passed through
......@@ -362,7 +449,10 @@ EXTRA = td.TypeDict({
'choices' : [],
'useAlts' : True
}
})
})
"""This dictionary defines any extra settings to be passed through
to the :func:`.props.addParserArguments` function.
"""
# Transform functions for properties where the
# value passed in on the command line needs to
......@@ -399,6 +489,10 @@ TRANSFORMS = td.TypeDict({
'LabelOpts.lut' : _lutTrans,
})
"""This dictionary defines any transformations for command line options
where the value passed on the command line cannot be directly converted
into the corresponding property value.
"""
def _configMainParser(mainParser):
......@@ -406,10 +500,10 @@ def _configMainParser(mainParser):
to the scene. This function configures the following argument
groups:
- *Main*: Top level optoins
- *ColourBar*: Colour bar related options
- *OrthoPanel*: Options related to setting up a orthographic display
- *LightBoxPanel*: Options related to setting up a lightbox display
- *Main*: Top level optoins
- *SceneOpts*: Common scene options
- *OrthoOpts*: Options related to setting up a orthographic display
- *LightBoxOpts*: Options related to setting up a lightbox display
"""
mainParser.add_argument('-h', '--help',
......@@ -453,6 +547,9 @@ def _configMainParser(mainParser):
def _configParser(target, parser, propNames=None):
"""Configures the given parser so it will parse arguments for the
given target.
"""
if propNames is None:
propNames = OPTIONS[target]
......@@ -485,14 +582,14 @@ def _configParser(target, parser, propNames=None):
def _configSceneParser(sceneParser):
"""Adds options to the given argument parser which allow
the user to specify colour bar properties.
the user to specify :class:`.SceneOpts` properties.
"""
_configParser(fsldisplay.SceneOpts, sceneParser)
def _configOrthoParser(orthoParser):
"""Adds options to the given parser allowing the user to
configure an orthographic display.
configure :class:`.OrthoOpts` properties.
"""
OrthoOpts = fsldisplay.OrthoOpts
......@@ -520,7 +617,7 @@ def _configOrthoParser(orthoParser):
def _configLightBoxParser(lbParser):
"""Adds options to the given parser allowing the user to
configure a lightbox display.
configure :class:`.LightBoxOpts` properties.
"""
_configParser(fsldisplay.LightBoxOpts, lbParser)
......@@ -616,7 +713,8 @@ def parseArgs(mainParser, argv, name, desc, toolOptsDesc='[options]'):
- argv: The arguments as passed in on the command line.
- name: The name of the tool - this function might be called by
either the ``fsleyes`` tool or the ``render`` tool.
either the :mod:`~fsl.tools.fsleyes` tool or the
:mod:`~fsl.tools.render` tool.
- desc: A description of the tool.
......@@ -696,7 +794,10 @@ def parseArgs(mainParser, argv, name, desc, toolOptsDesc='[options]'):
# the program to exit
def ovlArgError(message):
raise RuntimeError(message)
# The argparse internals are completely inconfigurable,
# and completely undocumented. Here, I'm monkey-patching
# the overlay parser error handler
ovlParser.error = ovlArgError
_configOverlayParser(ovlParser)
......@@ -709,7 +810,7 @@ def parseArgs(mainParser, argv, name, desc, toolOptsDesc='[options]'):
# Make a list of all the options which
# accept filenames, and which we need
# to account for when we're searching
# for overaly files, flattening the
# for overlay files, flattening the
# short/long arguments into a 1D list.
fileOpts = []
......@@ -855,8 +956,7 @@ def applySceneArgs(args, overlayList, displayCtx, sceneOpts):
:arg displayCtx: A :class:`.DisplayContext` instance.
:arg sceneOpts:
:arg sceneOpts: A :class:`.SceneOpts` instance.
"""
# First apply all command line options
......@@ -890,6 +990,14 @@ def applySceneArgs(args, overlayList, displayCtx, sceneOpts):
def generateSceneArgs(overlayList, displayCtx, sceneOpts, exclude=None):
"""Generates command line arguments which describe the current state of
the provided ``displayCtx`` and ``sceneOpts`` instances.
:arg overlayList: A :class:`.OverlayList` instance.
:arg displayCtx: A :class:`.DisplayContext` instance.
:arg sceneOpts: A :class:`.SceneOpts` instance.
:arg exclude: A list of property names to exclude.
"""
if exclude is None:
......@@ -923,6 +1031,11 @@ def generateSceneArgs(overlayList, displayCtx, sceneOpts, exclude=None):
def generateOverlayArgs(overlay, displayCtx):
"""Generates command line arguments which describe the display
of the current overlay.
:arg overlay: An overlay object.
:arg displayCtx: A :class:`.DisplayContext` instance.
"""
display = displayCtx.getDisplay(overlay)
opts = display .getDisplayOpts()
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment