+def _name_argument(name, default, help):
+ """TODO
+ """
+ return Argument(name=name, type='string', default=default, help=help)
+
+def block_argument(*args, **kwargs):
+ """TODO
+ """
+ return _name_argument(*args, **kwargs)
+
+def column_argument(*args, **kwargs):
+ """TODO
+ """
+ return _name_argument(*args, **kwargs)
+
+
+# Define useful command subclasses
+
+class CurveCommand (Command):
+ """A :class:`~hooke.command.Command` operating on a
+ :class:`~hooke.curve.Curve`.
+ """
+ def __init__(self, **kwargs):
+ if 'arguments' in kwargs:
+ kwargs['arguments'].insert(0, CurveArgument)
+ else:
+ kwargs['arguments'] = [CurveArgument]
+ super(CurveCommand, self).__init__(**kwargs)
+
+ def _curve(self, hooke, params):
+ """Get the selected curve.
+
+ Notes
+ -----
+ `hooke` is intended to attach the selected curve to the local
+ playlist; the returned curve should not be effected by the
+ state of `hooke`. This is important for reliable
+ :class:`~hooke.command_stack.CommandStack`\s.
+ """
+ # HACK? rely on params['curve'] being bound to the local hooke
+ # playlist (i.e. not a copy, as you would get by passing a
+ # curve through the queue). Ugh. Stupid queues. As an
+ # alternative, we could pass lookup information through the
+ # queue...
+ return params['curve']
+
+ def _add_to_command_stack(self, params):
+ """Store the command name and current `params` values in the
+ curve's `.command_stack`.
+
+ If this would duplicate the command currently on top of the
+ stack, no action is taken. Call early on, or watch out for
+ repeated param processing.
+
+ Recommended practice is to *not* lock in argument values that
+ are loaded from the plugin's :attr:`.config`.
+
+ Notes
+ -----
+ Perhaps we should subclass :meth:`_run` and use :func:`super`,
+ or embed this in :meth:`run` to avoid subclasses calling this
+ method explicitly, with all the tedium and brittality that
+ implies. On the other hand, the current implemtnation allows
+ CurveCommands that don't effect the curve itself
+ (e.g. :class:`GetCommand`) to avoid adding themselves to the
+ stack entirely.
+ """
+ if params['stack'] == True:
+ curve = self._curve(hooke=None, params=params)
+ if (len(curve.command_stack) > 0
+ and curve.command_stack[-1].command == self.name
+ and curve.command_stack[-1].arguments == params):
+ pass # no need to place duplicate calls on the stack.
+ else:
+ curve.command_stack.append(CommandMessage(
+ self.name, params))
+
+
+class BlockCommand (CurveCommand):
+ """A :class:`CurveCommand` operating on a :class:`~hooke.curve.Data` block.
+ """
+ def __init__(self, blocks=None, **kwargs):
+ if blocks == None:
+ blocks = [('block', None, 'Name of the data block to act on.')]
+ block_args = []
+ for name,default,help in blocks:
+ block_args.append(block_argument(name, default, help))
+ self._block_arguments = block_args
+ if 'arguments' not in kwargs:
+ kwargs['arguments'] = []
+ kwargs['arguments'] = block_args + kwargs['arguments']
+ super(BlockCommand, self).__init__(**kwargs)
+
+ def _block_names(self, hooke, params):
+ curve = self._curve(hooke, params)
+ return [b.info['name'] for b in curve.data]
+
+ def _block_index(self, hooke, params, name=None):
+ if name == None:
+ name = self._block_arguments[0].name
+ block_name = params[name]
+ if block_name == None:
+ curve = self._curve(hooke=hooke, params=params)
+ if len(curve.data) == 0:
+ raise Failure('no blocks in %s' % curve)
+ block_name = curve.data[0].info['name']
+ names = self._block_names(hooke=hooke, params=params)
+ try:
+ return names.index(block_name)
+ except ValueError, e:
+ curve = self._curve(hooke, params)
+ raise Failure('no block named %s in %s (%s): %s'
+ % (block_name, curve, names, e))
+
+ def _block(self, hooke, params, name=None):
+ # HACK? rely on params['block'] being bound to the local hooke
+ # playlist (i.e. not a copy, as you would get by passing a
+ # block through the queue). Ugh. Stupid queues. As an
+ # alternative, we could pass lookup information through the
+ # queue...
+ curve = self._curve(hooke, params)
+ index = self._block_index(hooke, params, name)
+ return curve.data[index]
+
+
+class ColumnAccessCommand (BlockCommand):
+ """A :class:`BlockCommand` accessing a :class:`~hooke.curve.Data`
+ block column.
+ """
+ def __init__(self, columns=None, **kwargs):
+ if columns == None:
+ columns = [('column', None, 'Name of the data column to act on.')]
+ column_args = []
+ for name,default,help in columns:
+ column_args.append(column_argument(name, default, help))
+ self._column_arguments = column_args
+ if 'arguments' not in kwargs:
+ kwargs['arguments'] = []
+ kwargs['arguments'] = column_args + kwargs['arguments']
+ super(ColumnAccessCommand, self).__init__(**kwargs)
+
+ def _get_column(self, hooke, params, block_name=None, column_name=None):
+ if column_name == None:
+ column_name = self._column_arguments[0].name
+ column_name = params[column_name]
+ block = self._block(hooke, params, block_name)
+ columns = block.info['columns']
+ try:
+ column_index = columns.index(column_name)
+ except ValueError, e:
+ raise Failure('%s not in %s (%s): %s'
+ % (column_name, block.info['name'], columns, e))
+ return block[:,column_index]
+
+
+class ColumnAddingCommand (ColumnAccessCommand):
+ """A :class:`ColumnAccessCommand` that also adds columns.
+ """
+ def __init__(self, new_columns=None, **kwargs):
+ if new_columns == None:
+ new_columns = []
+ column_args = []
+ for name,default,help in new_columns:
+ column_args.append(column_argument(name, default, help))
+ self._new_column_arguments = column_args
+ if 'arguments' not in kwargs:
+ kwargs['arguments'] = []
+ kwargs['arguments'] = column_args + kwargs['arguments']
+ super(ColumnAddingCommand, self).__init__(**kwargs)
+
+ def _get_column(self, hooke, params, block_name=None, column_name=None):
+ if column_name == None and len(self._column_arguments) == 0:
+ column_name = self._new_column_arguments[0].name
+ return super(ColumnAddingCommand, self)._get_column(
+ hooke=hooke, params=params, block_name=block_name,
+ column_name=column_name)
+
+ def _set_column(self, hooke, params, block_name=None, column_name=None,
+ values=None):
+ if column_name == None:
+ column_name = self._column_arguments[0].name
+ column_name = params[column_name]
+ block = self._block(hooke=hooke, params=params, name=block_name)
+ if column_name not in block.info['columns']:
+ new = Data((block.shape[0], block.shape[1]+1), dtype=block.dtype)
+ new.info = copy.deepcopy(block.info)
+ new[:,:-1] = block
+ new.info['columns'].append(column_name)
+ block = new
+ block_index = self._block_index(hooke, params, name=block_name)
+ self._curve(hooke, params).data[block_index] = block
+ column_index = block.info['columns'].index(column_name)
+ block[:,column_index] = values
+
+
+# The plugin itself
+
+class CurvePlugin (Builtin):
+ def __init__(self):
+ super(CurvePlugin, self).__init__(name='curve')
+ self._commands = [
+ GetCommand(self), InfoCommand(self), DeltaCommand(self),
+ ExportCommand(self), DifferenceCommand(self),
+ DerivativeCommand(self), PowerSpectrumCommand(self),
+ ClearStackCommand(self)]
+