#
# applycommandline.py - The ApplyCommandLineAction
#
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#
"""This module provides the :class:`ApplyCommandLineAction` class, an
:class:`.Action` which allows the user to apply FSLeyes command line
arguments to a :class:`.CanvasPanel`. The stand-alone
:func:`applyCommandLineArgs` function is where the work is actually
implemented.
"""
import os.path as op
import os
import sys
import argparse
import contextlib
import six
import wx
import fsleyes_widgets.dialog as fsldlg
import fsleyes_widgets.utils.status as status
import fsleyes.strings as strings
import fsleyes.parseargs as parseargs
from . import base
[docs]class ApplyCommandLineAction(base.Action):
"""The :class:`ApplyCommandLineAction` class is an :class:`.Action` which
allows the user to apply FSLeyes command line arguments to a
:class:`.CanvasPanel`.
"""
[docs] def __init__(self, overlayList, displayCtx, panel):
"""Create an ``ApplyCommandLineAction``.
:arg overlayList: The :class:`.OverlayList`.
:arg displayCtx: The :class:`.DisplayContext`.
:arg panel: The :class:`.CanvasPanel`.
"""
base.Action.__init__(
self, overlayList, displayCtx, self.__applyCommandLineArgs)
self.__panel = panel
def __applyCommandLineArgs(self):
"""Called when this action is executed. Prompts the user to enter
some command line arguments, and then passes them to the
:func:`applyCommandLineArgs` function.
"""
# prompt user for some arguments
dlg = fsldlg.TextEditDialog(
self.__panel,
strings.titles[ self, 'title'],
strings.messages[self, 'apply'],
style=fsldlg.TED_MULTILINE | fsldlg.TED_OK_CANCEL)
dlg.CentreOnParent()
if dlg.ShowModal() != wx.ID_OK:
return
# Pass GetText through str, because it
# returns a unicode string, and python's
# argparse does not seem to like unicode.
argv = str(dlg.GetText()).split()
errTitle = strings.titles[ self, 'error']
errMsg = strings.messages[self, 'error']
# apply said arguments
with status.reportIfError(errTitle, errMsg):
applyCommandLineArgs(self.overlayList,
self.displayCtx,
argv,
self.__panel)
[docs]class ApplyCLIExit(Exception):
"""``Exception`` class raised by the :func:`applyCommandLineArgs`
function.
"""
[docs] def __init__(self, code, stdout, stderr):
self.code = code
self.stdout = stdout
self.stderr = stderr
[docs] def __str__(self):
return '\n'.join((self.stderr, self.stdout))
[docs]@contextlib.contextmanager
def chdir(dest=None):
"""Context manager which temporarily changes to the specified ``dest``
directory, then restores the current directory afterwards.
If ``dest is None``, the function yields without doing anything.
"""
if dest is None:
yield
return
src = os.getcwd()
os.chdir(dest)
try:
yield
finally:
os.chdir(src)
[docs]def applyCommandLineArgs(overlayList,
displayCtx,
argv,
panel=None,
applyOverlayArgs=True,
baseDir=None,
**kwargs):
"""Applies the command line arguments stored in ``argv`` to the
:class:`.CanvasPanel` ``panel``. If ``panel is None``, it is assumed
that ``argv`` only contains overlay arguments.
:arg overlayList: The :class:`.OverlayList`.
:arg displayCtx: The :class:`.DisplayContext`. If a ``panel`` is
provided, this should be the ``DisplayContext``
associated with that panel.
:arg argv: List of command line arguments to apply.
:arg panel: Optional :class:`.CanvasPanel` to apply the
arguments to.
:arg applyOverlayArgs: If ``False``, overlay arguments are not applied.
:arg baseDir: Directory from which to interpret the arguments,
in case this is different from the current working
directory, and overlays have been specified with
relative paths.
All other keyword arguments are passed to the
:func:`.parseargs.applyOverlayArgs` function.
"""
# We patch sys.stdout/stderr
# while parseargs.parseArgs is
# called so we can capture its
# output.
stdout = six.StringIO()
stderr = six.StringIO()
if argv[0] == 'fsleyes':
argv = argv[1:]
parser = argparse.ArgumentParser(add_help=False)
try:
real_stdout = sys.stdout
real_stderr = sys.stderr
sys.stdout = stdout
sys.stderr = stderr
with chdir(baseDir):
namespace = parseargs.parseArgs(parser, argv, 'fsleyes')
except SystemExit as e:
raise ApplyCLIExit(e.code, stdout.getvalue(), stderr.getvalue())
finally:
sys.stdout = real_stdout
sys.stderr = real_stderr
if baseDir is not None:
for o in namespace.overlays:
if not op.isabs(o.overlay):
o.overlay = op.join(baseDir, o.overlay)
if applyOverlayArgs:
parseargs.applyOverlayArgs(
namespace, overlayList, displayCtx, **kwargs)
if panel is not None:
sceneOpts = panel.sceneOpts
parseargs.applySceneArgs(namespace, overlayList, displayCtx, sceneOpts)