1 # Copyright (C) 2010 W. Trevor King <wking@drexel.edu>
3 # This file is part of Hooke.
5 # Hooke is free software: you can redistribute it and/or modify it
6 # under the terms of the GNU Lesser General Public License as
7 # published by the Free Software Foundation, either version 3 of the
8 # License, or (at your option) any later version.
10 # Hooke is distributed in the hope that it will be useful, but WITHOUT
11 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
13 # Public License for more details.
15 # You should have received a copy of the GNU Lesser General Public
16 # License along with Hooke. If not, see
17 # <http://www.gnu.org/licenses/>.
19 """The ``playlist`` module provides :class:`PlaylistPlugin` and
20 several associated :class:`hooke.command.Command`\s for handling
21 :mod:`hooke.playlist` classes.
27 from ..command import Command, Argument, Failure
28 from ..playlist import FilePlaylist
29 from ..plugin import Builtin
32 class PlaylistPlugin (Builtin):
34 super(PlaylistPlugin, self).__init__(name='playlist')
36 NextCommand(self), PreviousCommand(self), JumpCommand(self),
37 GetCommand(self), IndexCommand(self), CurveListCommand(self),
38 SaveCommand(self), LoadCommand(self),
39 AddCommand(self), AddGlobCommand(self),
40 RemoveCommand(self), FilterCommand(self), NoteFilterCommand(self)]
43 # Define common or complicated arguments
45 def current_playlist_callback(hooke, command, argument, value):
48 playlist = hooke.playlists.current()
50 raise Failure('No playlists loaded')
53 PlaylistArgument = Argument(
54 name='playlist', type='playlist', callback=current_playlist_callback,
56 :class:`hooke.playlist.Playlist` to act on. Defaults to the current
60 def playlist_name_callback(hooke, command, argument, value):
64 names = [p.name for p in hooke.playlists]
66 name = 'playlist-%d' % i
71 PlaylistNameArgument = Argument(
72 name='name', type='string', optional=True, callback=playlist_name_callback,
74 Name of the new playlist (defaults to an auto-generated name).
77 def all_drivers_callback(hooke, command, argument, value):
83 class NextCommand (Command):
84 """Move playlist to the next curve.
86 def __init__(self, plugin):
87 super(NextCommand, self).__init__(
89 arguments=[PlaylistArgument],
90 help=self.__doc__, plugin=plugin)
92 def _run(self, hooke, inqueue, outqueue, params):
93 params['playlist'].next()
95 class PreviousCommand (Command):
96 """Move playlist to the previous curve.
98 def __init__(self, plugin):
99 super(PreviousCommand, self).__init__(
100 name='previous curve',
101 arguments=[PlaylistArgument],
102 help=self.__doc__, plugin=plugin)
104 def _run(self, hooke, inqueue, outqueue, params):
105 params['playlist'].previous()
107 class JumpCommand (Command):
108 """Move playlist to a given curve.
110 def __init__(self, plugin):
111 super(JumpCommand, self).__init__(
112 name='jump to curve',
115 Argument(name='index', type='int', optional=False, help="""
116 Index of target curve.
119 help=self.__doc__, plugin=plugin)
121 def _run(self, hooke, inqueue, outqueue, params):
122 params['playlist'].jump(params['index'])
124 class IndexCommand (Command):
125 """Print the index of the current curve.
127 The first curve has index 0.
129 def __init__(self, plugin):
130 super(IndexCommand, self).__init__(
135 help=self.__doc__, plugin=plugin)
137 def _run(self, hooke, inqueue, outqueue, params):
138 outqueue.put(params['playlist'].index())
140 class GetCommand (Command):
141 """Return a :class:`hooke.playlist.Playlist`.
143 def __init__(self, plugin):
144 super(GetCommand, self).__init__(
146 arguments=[PlaylistArgument],
147 help=self.__doc__, plugin=plugin)
149 def _run(self, hooke, inqueue, outqueue, params):
150 outqueue.put(params['playlist'])
152 class CurveListCommand (Command):
153 """Get the curves in a playlist.
155 def __init__(self, plugin):
156 super(CurveListCommand, self).__init__(
157 name='playlist curves',
158 arguments=[PlaylistArgument],
159 help=self.__doc__, plugin=plugin)
161 def _run(self, hooke, inqueue, outqueue, params):
162 outqueue.put(list(params['playlist']))
164 class SaveCommand (Command):
167 def __init__(self, plugin):
168 super(SaveCommand, self).__init__(
169 name='save playlist',
172 Argument(name='output', type='file',
174 File name for the output playlist. Defaults to overwriting the input
175 playlist. If the playlist does not have an input file (e.g. it was
176 created from scratch with 'new playlist'), this option is required.
179 help=self.__doc__, plugin=plugin)
181 def _run(self, hooke, inqueue, outqueue, params):
182 params['playlist'].save(params['output'])
184 class LoadCommand (Command):
187 def __init__(self, plugin):
188 super(LoadCommand, self).__init__(
189 name='load playlist',
191 Argument(name='input', type='file', optional=False,
193 File name for the input playlist.
195 Argument(name='drivers', type='driver', optional=True,
196 count=-1, callback=all_drivers_callback,
198 Drivers for loading curves.
201 help=self.__doc__, plugin=plugin)
203 def _run(self, hooke, inqueue, outqueue, params):
204 p = FilePlaylist(drivers=params['drivers'], path=params['input'])
206 hooke.playlists.append(p)
209 class AddCommand (Command):
210 """Add a curve to a playlist.
212 def __init__(self, plugin):
213 super(AddCommand, self).__init__(
214 name='add curve to playlist',
217 Argument(name='input', type='file', optional=False,
219 File name for the input :class:`hooke.curve.Curve`.
221 Argument(name='info', type='dict', optional=True,
223 Additional information for the input :class:`hooke.curve.Curve`.
226 help=self.__doc__, plugin=plugin)
228 def _run(self, hooke, inqueue, outqueue, params):
229 params['playlist'].append_curve_by_path(params['input'],
232 class AddGlobCommand (Command):
233 """Add curves to a playlist with file globbing.
235 Adding lots of files one at a time can be tedious. With this
236 command you can use globs (`data/curves/*.dat`) to add curves
237 for all matching files at once.
239 def __init__(self, plugin):
240 super(AddGlobCommand, self).__init__(
241 name='glob curves to playlist',
244 Argument(name='input', type='string', optional=False,
246 File name glob for the input :class:`hooke.curve.Curve`.
248 Argument(name='info', type='dict', optional=True,
250 Additional information for the input :class:`hooke.curve.Curve`.
253 help=self.__doc__, plugin=plugin)
255 def _run(self, hooke, inqueue, outqueue, params):
256 for path in sorted(glob.glob(params['input'])):
257 params['playlist'].append_curve_by_path(path, params['info'])
259 class RemoveCommand (Command):
260 """Remove a curve from a playlist.
262 def __init__(self, plugin):
263 super(RemoveCommand, self).__init__(
264 name='remove curve from playlist',
267 Argument(name='index', type='int', optional=False, help="""
268 Index of target curve.
271 help=self.__doc__, plugin=plugin)
273 def _run(self, hooke, inqueue, outqueue, params):
274 params['playlist'].pop(params['index'])
275 params['playlist'].jump(params.index())
277 class FilterCommand (Command):
278 """Create a subset playlist via a selection function.
280 Removing lots of curves one at a time can be tedious. With this
281 command you can use a function `filter` to select the curves you
286 There are issues with pickling functions bound to class
287 attributes, because the pickle module doesn't know where those
288 functions were originally defined (where it should point the
289 loader). Because of this, subclasses with hard-coded filter
290 functions are encouraged to define their filter function as a
291 method of their subclass. See, for example,
292 :meth:`NoteFilterCommand.filter`.
294 def __init__(self, plugin, name='filter playlist'):
295 super(FilterCommand, self).__init__(
299 PlaylistNameArgument,
301 help=self.__doc__, plugin=plugin)
302 if not hasattr(self, 'filter'):
303 self.arguments.append(
304 Argument(name='filter', type='function', optional=False,
306 Function returning `True` for "good" curves.
307 `filter(curve, hooke, inqueue, outqueue, params) -> True/False`.
310 def _run(self, hooke, inqueue, outqueue, params):
311 if not hasattr(self, 'filter'):
312 filter_fn = params['filter']
314 filter_fn = self.filter
315 p = params['playlist'].filter(filter_fn,
316 hooke=hooke, inqueue=inqueue, outqueue=outqueue, params=params)
317 p.name = params['name']
318 if hasattr(p, 'path') and p.path != None:
319 p.set_path(os.path.join(os.path.dirname(p.path), p.name))
320 hooke.playlists.append(p)
323 class NoteFilterCommand (FilterCommand):
324 """Create a subset playlist of curves with `.info['note'] != None`.
326 def __init__(self, plugin):
327 super(NoteFilterCommand, self).__init__(
328 plugin, name='note filter playlist')
330 def filter(self, curve, hooke, inqueue, outqueue, params):
331 return 'note' in curve.info and curve.info['note'] != None