X-Git-Url: http://git.tremily.us/?p=hooke.git;a=blobdiff_plain;f=hooke%2Fplaylist.py;h=352617ce90120c7c2afd6698300d61293535096d;hp=bba2990604927b1fbcdc624dfe7992a94e678295;hb=643629fa189f2bc8d05a6d9903ccd22637bc563d;hpb=13d7ede05e4a0c1f80d028e35a94de6b8d9300ad diff --git a/hooke/playlist.py b/hooke/playlist.py index bba2990..352617c 100644 --- a/hooke/playlist.py +++ b/hooke/playlist.py @@ -2,15 +2,15 @@ # # This file is part of Hooke. # -# Hooke is free software: you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation, either -# version 3 of the License, or (at your option) any later version. +# Hooke is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. # -# Hooke is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. +# Hooke is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General +# Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with Hooke. If not, see @@ -23,7 +23,9 @@ import copy import hashlib +import os import os.path +import types import xml.dom.minidom from . import curve as curve @@ -44,13 +46,27 @@ class NoteIndexList (list): self._index = 0 def __str__(self): - return '<%s %s>' % (self.__class__.__name__, self.name) + return str(self.__unicode__()) + + def __unicode__(self): + return u'<%s %s>' % (self.__class__.__name__, self.name) + + def __repr__(self): + return self.__str__() def _setup_item(self, item): """Perform any required initialization before returning an item. """ pass + def index(self, value=None, *args, **kwargs): + """Extend `list.index`, returning the current index if `value` + is `None`. + """ + if value == None: + return self._index + return super(NoteIndexList, self).index(value, *args, **kwargs) + def current(self): if len(self) == 0: return None @@ -70,10 +86,34 @@ class NoteIndexList (list): def previous(self): self.jump(self._index - 1) + def items(self, reverse=False): + """Iterate through `self` calling `_setup_item` on each item + before yielding. + + Notes + ----- + Updates :attr:`_index` during the iteration so + :func:`~hooke.plugin.curve.current_curve_callback` works as + expected in :class:`~hooke.command.Command`\s called from + :class:`~hooke.plugin.playlist.ApplyCommandStack`. After the + iteration completes, :attr:`_index` is restored to its + original value. + """ + index = self._index + items = self + if reverse == True: + items = reversed(enumerate(self)) + else: + items = enumerate(self) + for i,item in items: + self._index = i + self._setup_item(item) + yield item + self._index = index + def filter(self, keeper_fn=lambda item:True, *args, **kwargs): c = copy.deepcopy(self) - for item in reversed(c): - c._setup_item(item) + for item in c.items(reverse=True): if keeper_fn(item, *args, **kwargs) != True: c.remove(item) try: # attempt to maintain the same current item @@ -82,6 +122,7 @@ class NoteIndexList (list): c._index = 0 return c + class Playlist (NoteIndexList): """A :class:`NoteIndexList` of :class:`hooke.curve.Curve`\s. @@ -93,11 +134,10 @@ class Playlist (NoteIndexList): self._loaded = [] # List of loaded curves, see :meth:`._setup_item`. self._max_loaded = 100 # curves to hold in memory simultaneously. - def append_curve_by_path(self, path, info=None, identify=True): - if self.path != None: - path = os.path.join(os.path.dirname(self.path), path) + def append_curve_by_path(self, path, info=None, identify=True, hooke=None): path = os.path.normpath(path) c = curve.Curve(path, info=info) + c.set_hooke(hooke) if identify == True: c.identify(self.drivers) self.append(c) @@ -116,11 +156,15 @@ class Playlist (NoteIndexList): oldest = self._loaded.pop(0) oldest.unload() + class FilePlaylist (Playlist): + """A file-backed :class:`Playlist`. + """ version = '0.1' def __init__(self, drivers, name=None, path=None): super(FilePlaylist, self).__init__(drivers, name) + self.path = None self.set_path(path) self._digest = None self._ignored_keys = [ @@ -135,6 +179,11 @@ class FilePlaylist (Playlist): if self.name == None: self.name = os.path.basename(path) + def append_curve_by_path(self, path, *args, **kwargs): + if self.path != None: + path = os.path.join(os.path.dirname(self.path), path) + super(FilePlaylist, self).append_curve_by_path(path, *args, **kwargs) + def is_saved(self): return self.digest() == self._digest @@ -194,7 +243,7 @@ class FilePlaylist (Playlist): Relative paths are interpreted relative to the location of the playlist file. - + Examples -------- @@ -231,6 +280,9 @@ class FilePlaylist (Playlist): root.setAttribute('version', self.version) # store playlist version root.setAttribute('index', str(self._index)) for key,value in self.info.items(): # save info variables + if (key in self._ignored_keys + or not isinstance(value, types.StringTypes)): + continue root.setAttribute(self._clean_key(key), str(value)) for curve in self: # save curves and their attributes curve_element = doc.createElement('curve') @@ -244,7 +296,8 @@ class FilePlaylist (Playlist): os.path.expanduser(self.path)))) curve_element.setAttribute('path', path) for key,value in curve.info.items(): - if key in self._ignored_keys: + if (key in self._ignored_keys + or not isinstance(value, types.StringTypes)): continue curve_element.setAttribute(self._clean_key(key), str(value)) string = doc.toprettyxml(encoding='utf-8') @@ -303,18 +356,23 @@ class FilePlaylist (Playlist): doc = xml.dom.minidom.parseString(string) self._from_xml_doc(doc, identify=identify) - def load(self, path=None, identify=True): + def load(self, path=None, identify=True, hooke=None): """Load a playlist from a file. """ self.set_path(path) doc = xml.dom.minidom.parse(self.path) self._from_xml_doc(doc, identify=identify) self._digest = self.digest() + for curve in self: + curve.set_hooke(hooke) - def save(self, path=None): + def save(self, path=None, makedirs=True): """Saves the playlist in a XML file. """ self.set_path(path) - f = file(self.path, 'w') - f.write(self.flatten()) - f.close() + dirname = os.path.dirname(self.path) + if makedirs == True and not os.path.isdir(dirname): + os.makedirs(dirname) + with open(self.path, 'w') as f: + f.write(self.flatten()) + self._digest = self.digest()