Moved hooke.libhookecurve -> hooke.curve and rewrote HookeCurve -> Curve class.
[hooke.git] / hooke / playlist.py
1 import copy
2 import os
3 import os.path
4 import xml.dom.minidom
5
6 from . import hooke as hooke
7 from . import curve as lhc
8 from . import libhooke as lh
9
10 class Playlist(object):
11     def __init__(self, drivers):
12         self._saved = False
13         self.count = 0
14         self.curves = []
15         self.drivers = drivers
16         self.path = ''
17         self.genericsDict = {}
18         self.hiddenAttributes = ['curve', 'driver', 'name', 'plots']
19         self.index = -1
20         self.name = 'Untitled'
21         self.plotPanel = None
22         self.plotTab = None
23         self.xml = None
24
25     def add_curve(self, path, attributes={}):
26         curve = lhc.HookeCurve(path)
27         for key,value in attribures.items():
28             setattr(curve, key, value)
29         curve.identify(self.drivers)
30         curve.plots = curve.driver.default_plots()
31         self.curves.append(curve)
32         self._saved = False
33         self.count = len(self.curves)
34         return curve
35
36     def close_curve(self, index):
37         if index >= 0 and index < self.count:
38             self.curves.remove(index)
39
40     def filter_curves(self, keeper_fn=labmda curve:True):
41         playlist = copy.deepcopy(self)
42         for curve in reversed(playlist.curves):
43             if not keeper_fn(curve):
44                 playlist.curves.remove(curve)
45         try: # attempt to maintain the same active curve
46             playlist.index = playlist.curves.index(self.get_active_curve())
47         except ValueError:
48             playlist.index = 0
49         playlist._saved = False
50         playlist.count = len(playlist.curves)
51         return playlist
52
53     def get_active_curve(self):
54         return self.curves[self.index]
55
56     #TODO: do we need this?
57     def get_active_plot(self):
58         return self.curves[self.index].plots[0]
59
60     def get_status_string(self):
61         if self.has_curves()
62             return '%s (%s/%s)' % (self.name, self.index + 1, self.count)
63         return 'The file %s does not contain any valid force curve data.' \
64             % self.name
65
66     def has_curves(self):
67         if self.count > 0:
68             return True
69         return False
70
71     def is_saved(self):
72         return self._saved
73
74     def load(self, path):
75         '''
76         loads a playlist file
77         '''
78         self.path = path
79         self.name = os.path.basename(path)
80         playlist = lh.delete_empty_lines_from_xmlfile(path)
81         self.xml = xml.dom.minidom.parse(path)
82         # Strip blank spaces:
83         self._removeWhitespaceNodes()
84
85         generics_list = self.xml.getElementsByTagName('generics')
86         curve_list = self.xml.getElementsByTagName('curve')
87         self._loadGenerics(generics_list)
88         self._loadCurves(curve_list)
89         self._saved = True
90
91     def _removeWhitespaceNodes(self, root_node=None):
92         if root_node == None:
93             root_node = self.xml
94         for node in root_node.childNodes:
95             if node.nodeType == node.TEXT_NODE and node.data.strip() == '':
96                 root_node.removeChild(node) # drop this whitespace node
97             else:
98                 _removeWhitespaceNodes(root_node=node) # recurse down a level
99
100     def _loadGenerics(self, generics_list, clear=True):
101         if clear:
102             self.genericsDict = {}
103         #populate generics
104         generics_list = self.xml.getElementsByTagName('generics')
105         for generics in generics_list:
106             for attribute in generics.attributes.keys():
107                 self.genericsDict[attribute] = generics_list[0].getAttribute(attribute)
108         if self.genericsDict.has_key('pointer'):
109             index = int(self.genericsDict['pointer'])
110             if index >= 0 and index < len(self.curves):
111                 self.index = index
112             else:
113                 index = 0
114
115     def _loadCurves(self, curve_list, clear=True):
116         if clear:
117             self.curves = []
118         #populate playlist with curves
119         for curve in curve_list:
120             #rebuild a data structure from the xml attributes
121             curve_path = lh.get_file_path(element.getAttribute('path'))
122             #extract attributes for the single curve
123             attributes = dict([(k,curve.getAttribute(k))
124                                for k in curve.attributes.keys()])
125             attributes.pop('path')
126             curve = self.add_curve(os.path.join(path, curve_path), attributes)
127             if curve is not None:
128                 for plot in curve.plots:
129                     curve.add_data('raw', plot.vectors[0][0], plot.vectors[0][1], color=plot.colors[0], style='plot')
130                     curve.add_data('raw', plot.vectors[1][0], plot.vectors[1][1], color=plot.colors[1], style='plot')
131
132     def next(self):
133         self.index += 1
134         if self.index > self.count - 1:
135             self.index = 0
136
137     def previous(self):
138         self.index -= 1
139         if self.index < 0:
140             self.index = self.count - 1
141
142     def reset(self):
143         if self.has_curves():
144             self.index = 0
145         else:
146             self.index = None
147
148     def save(self, path):
149         '''
150         saves the playlist in a XML file.
151         '''
152         try:
153             output_file = file(path, 'w')
154         except IOError, e:
155             #TODO: send message
156             print 'Cannot save playlist: %s' % e
157             return
158         self.xml.writexml(output_file, indent='\n')
159         output_file.close()
160         self._saved = True
161
162     def set_XML(self):
163         '''
164         Creates an initial playlist from a list of files.
165         A playlist is an XML document with the following syntax:
166           <?xml version="1.0" encoding="utf-8"?>
167           <playlist>
168             <generics pointer="0"/>
169             <curve path="/my/file/path/"/ attribute="value" ...>
170             <curve path="...">
171           </playlist>
172         Relative paths are interpreted relative to the location of the
173         playlist file.
174         '''
175         #create the output playlist, a simple XML document
176         implementation = xml.dom.minidom.getDOMImplementation()
177         #create the document DOM object and the root element
178         self.xml = implementation.createDocument(None, 'playlist', None)
179         root = self.xml.documentElement
180
181         #save generics variables
182         playlist_generics = self.xml.createElement('generics')
183         root.appendChild(playlist_generics)
184         self.genericsDict['pointer'] = self.index
185         for key in self.genericsDict.keys():
186             self.xml.createAttribute(key)
187             playlist_generics.setAttribute(key, str(self.genericsDict[key]))
188             
189         #save curves and their attributes
190         for item in self.curves:
191             playlist_curve = self.xml.createElement('curve')
192             root.appendChild(playlist_curve)
193             for key in item.__dict__:
194                 if not (key in self.hiddenAttributes):
195                     self.xml.createAttribute(key)
196                     playlist_curve.setAttribute(key, str(item.__dict__[key]))
197         self._saved = False