Remove superfluous ..plugin from imports in hooke.plugin.*
[hooke.git] / hooke / plugin / playlist.py
1 # Copyright (C) 2010 W. Trevor King <wking@drexel.edu>
2 #
3 # This file is part of Hooke.
4 #
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.
9 #
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.
14 #
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/>.
18
19 """The ``playlist`` module provides :class:`PlaylistPlugin` and
20 several associated :class:`hooke.command.Command`\s for handling
21 :mod:`hooke.playlist` classes.
22 """
23
24 import glob
25 import os.path
26
27 from ..command import Command, Argument, Failure
28 from ..playlist import FilePlaylist
29 from . import Builtin
30
31
32 class PlaylistPlugin (Builtin):
33     def __init__(self):
34         super(PlaylistPlugin, self).__init__(name='playlist')
35         self._commands = [
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)]
41
42
43 # Define common or complicated arguments
44
45 def current_playlist_callback(hooke, command, argument, value):
46     if value != None:
47         return value
48     playlist = hooke.playlists.current()
49     if playlist == None:
50         raise Failure('No playlists loaded')
51     return playlist
52
53 PlaylistArgument = Argument(
54     name='playlist', type='playlist', callback=current_playlist_callback,
55     help="""
56 :class:`hooke.playlist.Playlist` to act on.  Defaults to the current
57 playlist.
58 """.strip())
59
60 def playlist_name_callback(hooke, command, argument, value):
61     if value != None:
62         return value
63     i = 0
64     names = [p.name for p in hooke.playlists]
65     while True:
66         name = 'playlist-%d' % i
67         if name not in names:
68             return name
69         i += 1
70
71 PlaylistNameArgument = Argument(
72     name='name', type='string', optional=True, callback=playlist_name_callback,
73     help="""
74 Name of the new playlist (defaults to an auto-generated name).
75 """.strip())
76
77 def all_drivers_callback(hooke, command, argument, value):
78     return hooke.drivers
79
80
81 # Define commands
82
83 class NextCommand (Command):
84     """Move playlist to the next curve.
85     """
86     def __init__(self, plugin):
87         super(NextCommand, self).__init__(
88             name='next curve',
89             arguments=[PlaylistArgument],
90             help=self.__doc__, plugin=plugin)
91
92     def _run(self, hooke, inqueue, outqueue, params):
93         params['playlist'].next()
94
95 class PreviousCommand (Command):
96     """Move playlist to the previous curve.
97     """
98     def __init__(self, plugin):
99         super(PreviousCommand, self).__init__(
100             name='previous curve',
101             arguments=[PlaylistArgument],
102             help=self.__doc__, plugin=plugin)
103
104     def _run(self, hooke, inqueue, outqueue, params):
105         params['playlist'].previous()
106
107 class JumpCommand (Command):
108     """Move playlist to a given curve.
109     """
110     def __init__(self, plugin):
111         super(JumpCommand, self).__init__(
112             name='jump to curve',
113             arguments=[
114                 PlaylistArgument,
115                 Argument(name='index', type='int', optional=False, help="""
116 Index of target curve.
117 """.strip()),
118                 ],
119             help=self.__doc__, plugin=plugin)
120
121     def _run(self, hooke, inqueue, outqueue, params):
122         params['playlist'].jump(params['index'])
123
124 class IndexCommand (Command):
125     """Print the index of the current curve.
126
127     The first curve has index 0.
128     """
129     def __init__(self, plugin):
130         super(IndexCommand, self).__init__(
131             name='curve index',
132             arguments=[
133                 PlaylistArgument,
134                 ],
135             help=self.__doc__, plugin=plugin)
136
137     def _run(self, hooke, inqueue, outqueue, params):
138         outqueue.put(params['playlist'].index())
139
140 class GetCommand (Command):
141     """Return a :class:`hooke.playlist.Playlist`.
142     """
143     def __init__(self, plugin):
144         super(GetCommand, self).__init__(
145             name='get playlist',
146             arguments=[PlaylistArgument],
147             help=self.__doc__, plugin=plugin)
148
149     def _run(self, hooke, inqueue, outqueue, params):
150         outqueue.put(params['playlist'])
151
152 class CurveListCommand (Command):
153     """Get the curves in a playlist.
154     """
155     def __init__(self, plugin):
156         super(CurveListCommand, self).__init__(
157             name='playlist curves',
158             arguments=[PlaylistArgument],
159             help=self.__doc__, plugin=plugin)
160
161     def _run(self, hooke, inqueue, outqueue, params):
162         outqueue.put(list(params['playlist']))
163
164 class SaveCommand (Command):
165     """Save a playlist.
166     """
167     def __init__(self, plugin):
168         super(SaveCommand, self).__init__(
169             name='save playlist',
170             arguments=[
171                 PlaylistArgument,
172                 Argument(name='output', type='file',
173                          help="""
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.
177 """.strip()),
178                 ],
179             help=self.__doc__, plugin=plugin)
180
181     def _run(self, hooke, inqueue, outqueue, params):
182         params['playlist'].save(params['output'])
183
184 class LoadCommand (Command):
185     """Load a playlist.
186     """
187     def __init__(self, plugin):
188         super(LoadCommand, self).__init__(
189             name='load playlist',
190             arguments=[
191                 Argument(name='input', type='file', optional=False,
192                          help="""
193 File name for the input playlist.
194 """.strip()),
195                 Argument(name='drivers', type='driver', optional=True,
196                          count=-1, callback=all_drivers_callback,
197                          help="""
198 Drivers for loading curves.
199 """.strip()),
200                 ],
201             help=self.__doc__, plugin=plugin)
202
203     def _run(self, hooke, inqueue, outqueue, params):
204         p = FilePlaylist(drivers=params['drivers'], path=params['input'])
205         p.load()
206         hooke.playlists.append(p)
207         outqueue.put(p)
208
209 class AddCommand (Command):
210     """Add a curve to a playlist.
211     """
212     def __init__(self, plugin):
213         super(AddCommand, self).__init__(
214             name='add curve to playlist',
215             arguments=[
216                 PlaylistArgument,
217                 Argument(name='input', type='file', optional=False,
218                          help="""
219 File name for the input :class:`hooke.curve.Curve`.
220 """.strip()),
221                 Argument(name='info', type='dict', optional=True,
222                          help="""
223 Additional information for the input :class:`hooke.curve.Curve`.
224 """.strip()),
225                 ],
226             help=self.__doc__, plugin=plugin)
227
228     def _run(self, hooke, inqueue, outqueue, params):
229         params['playlist'].append_curve_by_path(params['input'],
230                                                 params['info'])
231
232 class AddGlobCommand (Command):
233     """Add curves to a playlist with file globbing.
234
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.
238     """
239     def __init__(self, plugin):
240         super(AddGlobCommand, self).__init__(
241             name='glob curves to playlist',
242             arguments=[
243                 PlaylistArgument,
244                 Argument(name='input', type='string', optional=False,
245                          help="""
246 File name glob for the input :class:`hooke.curve.Curve`.
247 """.strip()),
248                 Argument(name='info', type='dict', optional=True,
249                          help="""
250 Additional information for the input :class:`hooke.curve.Curve`.
251 """.strip()),
252                 ],
253             help=self.__doc__, plugin=plugin)
254
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'])
258
259 class RemoveCommand (Command):
260     """Remove a curve from a playlist.
261     """
262     def __init__(self, plugin):
263         super(RemoveCommand, self).__init__(
264             name='remove curve from playlist',
265             arguments=[
266                 PlaylistArgument,
267                 Argument(name='index', type='int', optional=False, help="""
268 Index of target curve.
269 """.strip()),
270                 ],
271             help=self.__doc__, plugin=plugin)
272
273     def _run(self, hooke, inqueue, outqueue, params):
274         params['playlist'].pop(params['index'])
275         params['playlist'].jump(params.index())
276
277 class FilterCommand (Command):
278     """Create a subset playlist via a selection function.
279
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
282     wish to keep.
283
284     Notes
285     -----
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`.
293     """
294     def __init__(self, plugin, name='filter playlist'):
295         super(FilterCommand, self).__init__(
296             name=name,
297             arguments=[
298                 PlaylistArgument,
299                 PlaylistNameArgument,
300                 ],
301             help=self.__doc__, plugin=plugin)
302         if not hasattr(self, 'filter'):
303             self.arguments.append(
304                 Argument(name='filter', type='function', optional=False,
305                          help="""
306 Function returning `True` for "good" curves.
307 `filter(curve, hooke, inqueue, outqueue, params) -> True/False`.
308 """.strip()))
309
310     def _run(self, hooke, inqueue, outqueue, params):
311         if not hasattr(self, 'filter'):
312             filter_fn = params['filter']
313         else:
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)
321         outqueue.put(p)
322
323 class NoteFilterCommand (FilterCommand):
324     """Create a subset playlist of curves with `.info['note'] != None`.
325     """
326     def __init__(self, plugin):
327         super(NoteFilterCommand, self).__init__(
328             plugin, name='note filter playlist')
329
330     def filter(self, curve, hooke, inqueue, outqueue, params):
331         return 'note' in curve.info and curve.info['note'] != None