5 General library of internal objects and utilities for Hooke.
7 Copyright (C) 2006 Massimo Sandal (University of Bologna, Italy).
8 With algorithms contributed by Francesco Musiani (University of Bologna, Italy)
10 This program is released under the GNU General Public License version 2.
13 import libhookecurve as lhc
17 import xml.dom.minidom
23 HOOKE_VERSION=['0.8.3_devel', 'Seinei', '2008-04-16']
26 class PlaylistXML(object):
28 This module allows for import/export of an XML playlist into/out of a list of HookeCurve objects
33 self.playlist=None #the DOM object representing the playlist data structure
34 self.playpath=None #the path of the playlist XML file
36 self.hidden_attributes=['curve'] #This list contains hidden attributes that we don't want to go into the playlist.
38 def export(self, list_of_hooke_curves, generics):
40 Creates an initial playlist from a list of files.
41 A playlist is an XML document with the following syntaxis:
43 <element path="/my/file/path/"/ attribute="attribute">
48 #create the output playlist, a simple XML document
49 impl=xml.dom.minidom.getDOMImplementation()
50 #create the document DOM object and the root element
51 newdoc=impl.createDocument(None, "playlist",None)
52 top_element=newdoc.documentElement
54 #save generics variables
55 playlist_generics=newdoc.createElement("generics")
56 top_element.appendChild(playlist_generics)
57 for key in generics.keys():
58 newdoc.createAttribute(key)
59 playlist_generics.setAttribute(key,str(generics[key]))
61 #save curves and their attributes
62 for item in list_of_hooke_curves:
63 #playlist_element=newdoc.createElement("curve")
64 playlist_element=newdoc.createElement("element")
65 top_element.appendChild(playlist_element)
66 for key in item.__dict__:
67 if not (key in self.hidden_attributes):
68 newdoc.createAttribute(key)
69 playlist_element.setAttribute(key,str(item.__dict__[key]))
73 def load(self,filename):
78 self.playpath=filename
80 #the following 3 lines are needed to strip newlines. otherwise, since newlines
81 #are XML elements too (why?), the parser would read them (and re-save them, multiplying
84 the_file=myplay.read()
85 the_file_lines=the_file.split('\n')
86 the_file=''.join(the_file_lines)
88 self.playlist=xml.dom.minidom.parseString(the_file)
90 #inner parsing functions
91 def handlePlaylist(playlist):
92 list_of_files=playlist.getElementsByTagName("element")
93 generics=playlist.getElementsByTagName("generics")
94 return handleFiles(list_of_files), handleGenerics(generics)
96 def handleGenerics(generics):
101 for attribute in generics[0].attributes.keys():
102 generics_dict[attribute]=generics[0].getAttribute(attribute)
105 def handleFiles(list_of_files):
107 for myfile in list_of_files:
108 #rebuild a data structure from the xml attributes
109 the_curve=lhc.HookeCurve(myfile.getAttribute('path'))
110 for attribute in myfile.attributes.keys(): #extract attributes for the single curve
111 the_curve.__dict__[attribute]=myfile.getAttribute(attribute)
112 new_playlist.append(the_curve)
114 return new_playlist #this is the true thing returned at the end of this function...(FIXME: clarity)
116 return handlePlaylist(self.playlist)
119 def save(self,output_filename):
121 saves the playlist in a XML file.
124 outfile=file(output_filename,'w')
126 print 'libhooke.py : Cannot save playlist. Wrong path or filename'
129 self.playlist.writexml(outfile,indent='\n')
133 class HookeConfig(object):
135 Handling of Hooke configuration file
137 Mostly based on the simple-yet-useful examples of the Python Library Reference
138 about xml.dom.minidom
140 FIXME: starting to look a mess, should require refactoring
143 def __init__(self, config_dir=None):
145 self.config['plugins']=[]
146 self.config['drivers']=[]
147 self.config['plotmanips']=[]
148 self.config_dir = config_dir
149 if self.config_dir == None:
150 self.config_dir = os.path.abspath(
151 os.path.join(os.path.dirname(os.path.dirname(__file__)),
154 def load_config(self, filename):
155 print 'loading config file', os.path.join(self.config_dir, filename)
156 myconfig=file(os.path.join(self.config_dir, filename))
158 #the following 3 lines are needed to strip newlines. otherwise, since newlines
159 #are XML elements too, the parser would read them (and re-save them, multiplying
161 #yes, I'm an XML n00b
162 the_file=myconfig.read()
163 the_file_lines=the_file.split('\n')
164 the_file=''.join(the_file_lines)
166 self.config_tree=xml.dom.minidom.parseString(the_file)
168 def getText(nodelist):
169 #take the text from a nodelist
170 #from Python Library Reference 13.7.2
172 for node in nodelist:
173 if node.nodeType == node.TEXT_NODE:
177 def handleConfig(config):
178 display_elements=config.getElementsByTagName("display")
179 plugins_elements=config.getElementsByTagName("plugins")
180 drivers_elements=config.getElementsByTagName("drivers")
181 workdir_elements=config.getElementsByTagName("workdir")
182 defaultlist_elements=config.getElementsByTagName("defaultlist")
183 plotmanip_elements=config.getElementsByTagName("plotmanips")
184 handleDisplay(display_elements)
185 handlePlugins(plugins_elements)
186 handleDrivers(drivers_elements)
187 handleWorkdir(workdir_elements)
188 handleDefaultlist(defaultlist_elements)
189 handlePlotmanip(plotmanip_elements)
191 def handleDisplay(display_elements):
192 for element in display_elements:
193 for attribute in element.attributes.keys():
194 self.config[attribute]=element.getAttribute(attribute)
196 def handlePlugins(plugins):
197 for plugin in plugins[0].childNodes:
199 self.config['plugins'].append(str(plugin.tagName))
200 except: #if we allow fancy formatting of xml, there is a text node, so tagName fails for it...
202 #FIXME: code duplication
203 def handleDrivers(drivers):
204 for driver in drivers[0].childNodes:
206 self.config['drivers'].append(str(driver.tagName))
207 except: #if we allow fancy formatting of xml, there is a text node, so tagName fails for it...
210 def handlePlotmanip(plotmanips):
211 for plotmanip in plotmanips[0].childNodes:
213 self.config['plotmanips'].append(str(plotmanip.tagName))
214 except: #if we allow fancy formatting of xml, there is a text node, so tagName fails for it...
217 def handleWorkdir(workdir):
219 default working directory
221 wdir=getText(workdir[0].childNodes)
222 self.config['workdir']=wdir.strip()
224 def handleDefaultlist(defaultlist):
228 dflist=getText(defaultlist[0].childNodes)
229 self.config['defaultlist']=dflist.strip()
231 handleConfig(self.config_tree)
232 #making items in the dictionary more machine-readable
233 for item in self.config.keys():
235 self.config[item]=float(self.config[item])
236 except TypeError: #we are dealing with a list, probably. keep it this way.
238 self.config[item]=eval(self.config[item])
239 except: #not a list, not a tuple, probably a string?
241 except ValueError: #if we can't get it to a number, it must be None or a string
242 if string.lower(self.config[item])=='none':
243 self.config[item]=None
250 def save_config(self, config_filename):
251 print 'Not Implemented.'
255 class ClickedPoint(object):
257 this class defines what a clicked point on the curve plot is
261 self.is_marker=None #boolean ; decides if it is a marker
262 self.is_line_edge=None #boolean ; decides if it is the edge of a line (unused)
263 self.absolute_coords=(None,None) #(float,float) ; the absolute coordinates of the clicked point on the graph
264 self.graph_coords=(None,None) #(float,float) ; the coordinates of the plot that are nearest in X to the clicked point
265 self.index=None #integer ; the index of the clicked point with respect to the vector selected
266 self.dest=None #0 or 1 ; 0=top plot 1=bottom plot
269 def find_graph_coords_old(self, xvector, yvector):
271 Given a clicked point on the plot, finds the nearest point in the dataset (in X) that
272 corresponds to the clicked point.
273 OLD & DEPRECATED - to be removed
276 #FIXME: a general algorithm using min() is needed!
277 print '---DEPRECATED FIND_GRAPH_COORDS_OLD---'
279 best_dist=10**9 #should be more than enough given the scale
281 for index in scipy.arange(1,len(xvector),1):
282 dist=((self.absolute_coords[0]-xvector[index])**2)+(100*((self.absolute_coords[1]-yvector[index])))**2
283 #TODO, generalize? y coordinate is multiplied by 100 due to scale differences in the plot
288 self.index=best_index
289 self.graph_coords=(xvector[best_index],yvector[best_index])
292 def find_graph_coords(self,xvector,yvector):
294 Given a clicked point on the plot, finds the nearest point in the dataset that
295 corresponds to the clicked point.
298 for index in scipy.arange(1,len(xvector),1):
299 dists.append(((self.absolute_coords[0]-xvector[index])**2)+((self.absolute_coords[1]-yvector[index])**2))
301 self.index=dists.index(min(dists))
302 self.graph_coords=(xvector[self.index],yvector[self.index])
303 #-----------------------------------------
304 #CSV-HELPING FUNCTIONS
306 def transposed2(lists, defval=0):
308 transposes a list of lists, i.e. from [[a,b,c],[x,y,z]] to [[a,x],[b,y],[c,z]] without losing
310 (by Zoran Isailovski on the Python Cookbook online)
312 if not lists: return []
313 return map(lambda *row: [elem or defval for elem in row], *lists)
315 def csv_write_dictionary(f, data, sorting='COLUMNS'):
317 Writes a CSV file from a dictionary, with keys as first column or row
318 Keys are in "random" order.
320 Keys should be strings
321 Values should be lists or other iterables
325 t_values=transposed2(values)
328 if sorting=='COLUMNS':
329 writer.writerow(keys)
330 for item in t_values:
331 writer.writerow(item)
334 print 'Not implemented!' #FIXME: implement it.
337 #-----------------------------------------
341 debug stuff from latest rewrite of hooke_playlist.py
342 should be removed sooner or later (or substituted with new debug code!)
345 print confo.load_config('hooke.conf')
347 if __name__ == '__main__':