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.
15 import xml.dom.minidom
21 from . import libhookecurve as lhc
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(
110 os.path.join(os.path.dirname(self.playpath),
111 myfile.getAttribute('path')))
112 for attribute in myfile.attributes.keys():
113 #extract attributes for the single curve
114 if attribute == 'path':
115 continue # we already added this attribute
116 the_curve.__dict__[attribute]=myfile.getAttribute(attribute)
117 new_playlist.append(the_curve)
119 return new_playlist #this is the true thing returned at the end of this function...(FIXME: clarity)
121 return handlePlaylist(self.playlist)
124 def save(self,output_filename):
126 saves the playlist in a XML file.
129 outfile=file(output_filename,'w')
131 print 'libhooke.py : Cannot save playlist. Wrong path or filename'
134 self.playlist.writexml(outfile,indent='\n')
137 def config_file_path(filename, config_dir=None):
138 if config_dir == None:
139 config_dir = os.path.abspath(
140 os.path.join(os.path.dirname(os.path.dirname(__file__)), 'conf'))
141 return os.path.join(config_dir, filename)
143 class HookeConfig(object):
145 Handling of Hooke configuration file
147 Mostly based on the simple-yet-useful examples of the Python Library Reference
148 about xml.dom.minidom
150 FIXME: starting to look a mess, should require refactoring
153 def __init__(self, config_dir=None):
155 self.config['install']={}
156 self.config['plugins']=[]
157 self.config['drivers']=[]
158 self.config['plotmanips']=[]
159 self.config_dir = config_dir
161 def load_config(self, filename):
162 myconfig=file(config_file_path(filename, config_dir=self.config_dir))
164 #the following 3 lines are needed to strip newlines. otherwise, since newlines
165 #are XML elements too, the parser would read them (and re-save them, multiplying
167 #yes, I'm an XML n00b
168 the_file=myconfig.read()
169 the_file_lines=the_file.split('\n')
170 the_file=''.join(the_file_lines)
172 self.config_tree=xml.dom.minidom.parseString(the_file)
174 def getText(nodelist):
175 #take the text from a nodelist
176 #from Python Library Reference 13.7.2
178 for node in nodelist:
179 if node.nodeType == node.TEXT_NODE:
183 def handleConfig(config):
184 install_elements=config.getElementsByTagName("install")
185 display_elements=config.getElementsByTagName("display")
186 plugins_elements=config.getElementsByTagName("plugins")
187 drivers_elements=config.getElementsByTagName("drivers")
188 defaultlist_elements=config.getElementsByTagName("defaultlist")
189 plotmanip_elements=config.getElementsByTagName("plotmanips")
190 handleInstall(install_elements)
191 handleDisplay(display_elements)
192 handlePlugins(plugins_elements)
193 handleDrivers(drivers_elements)
194 handleDefaultlist(defaultlist_elements)
195 handlePlotmanip(plotmanip_elements)
197 def handleInstall(install_elements):
198 for install in install_elements:
199 for node in install.childNodes:
200 if node.nodeType == node.TEXT_NODE:
202 path = os.path.abspath(getText(node.childNodes).strip())
203 self.config['install'][str(node.tagName)] = path
205 def handleDisplay(display_elements):
206 for element in display_elements:
207 for attribute in element.attributes.keys():
208 self.config[attribute]=element.getAttribute(attribute)
210 def handlePlugins(plugins):
211 for plugin in plugins[0].childNodes:
213 self.config['plugins'].append(str(plugin.tagName))
214 except: #if we allow fancy formatting of xml, there is a text node, so tagName fails for it...
216 #FIXME: code duplication
217 def handleDrivers(drivers):
218 for driver in drivers[0].childNodes:
220 self.config['drivers'].append(str(driver.tagName))
221 except: #if we allow fancy formatting of xml, there is a text node, so tagName fails for it...
224 def handlePlotmanip(plotmanips):
225 for plotmanip in plotmanips[0].childNodes:
227 self.config['plotmanips'].append(str(plotmanip.tagName))
228 except: #if we allow fancy formatting of xml, there is a text node, so tagName fails for it...
231 def handleDefaultlist(defaultlist):
235 dflist=getText(defaultlist[0].childNodes)
236 self.config['defaultlist']=dflist.strip()
238 handleConfig(self.config_tree)
239 #making items in the dictionary more machine-readable
240 for item in self.config.keys():
242 self.config[item]=float(self.config[item])
243 except TypeError: #we are dealing with a list, probably. keep it this way.
245 self.config[item]=eval(self.config[item])
246 except: #not a list, not a tuple, probably a string?
248 except ValueError: #if we can't get it to a number, it must be None or a string
249 if string.lower(self.config[item])=='none':
250 self.config[item]=None
257 def save_config(self, config_filename):
258 print 'Not Implemented.'
262 class ClickedPoint(object):
264 this class defines what a clicked point on the curve plot is
268 self.is_marker=None #boolean ; decides if it is a marker
269 self.is_line_edge=None #boolean ; decides if it is the edge of a line (unused)
270 self.absolute_coords=(None,None) #(float,float) ; the absolute coordinates of the clicked point on the graph
271 self.graph_coords=(None,None) #(float,float) ; the coordinates of the plot that are nearest in X to the clicked point
272 self.index=None #integer ; the index of the clicked point with respect to the vector selected
273 self.dest=None #0 or 1 ; 0=top plot 1=bottom plot
276 def find_graph_coords_old(self, xvector, yvector):
278 Given a clicked point on the plot, finds the nearest point in the dataset (in X) that
279 corresponds to the clicked point.
280 OLD & DEPRECATED - to be removed
283 #FIXME: a general algorithm using min() is needed!
284 print '---DEPRECATED FIND_GRAPH_COORDS_OLD---'
286 best_dist=10**9 #should be more than enough given the scale
288 for index in scipy.arange(1,len(xvector),1):
289 dist=((self.absolute_coords[0]-xvector[index])**2)+(100*((self.absolute_coords[1]-yvector[index])))**2
290 #TODO, generalize? y coordinate is multiplied by 100 due to scale differences in the plot
295 self.index=best_index
296 self.graph_coords=(xvector[best_index],yvector[best_index])
299 def find_graph_coords(self,xvector,yvector):
301 Given a clicked point on the plot, finds the nearest point in the dataset that
302 corresponds to the clicked point.
305 for index in scipy.arange(1,len(xvector),1):
306 dists.append(((self.absolute_coords[0]-xvector[index])**2)+((self.absolute_coords[1]-yvector[index])**2))
308 self.index=dists.index(min(dists))
309 self.graph_coords=(xvector[self.index],yvector[self.index])
310 #-----------------------------------------
311 #CSV-HELPING FUNCTIONS
313 def transposed2(lists, defval=0):
315 transposes a list of lists, i.e. from [[a,b,c],[x,y,z]] to [[a,x],[b,y],[c,z]] without losing
317 (by Zoran Isailovski on the Python Cookbook online)
319 if not lists: return []
320 return map(lambda *row: [elem or defval for elem in row], *lists)
322 def csv_write_dictionary(f, data, sorting='COLUMNS'):
324 Writes a CSV file from a dictionary, with keys as first column or row
325 Keys are in "random" order.
327 Keys should be strings
328 Values should be lists or other iterables
332 t_values=transposed2(values)
335 if sorting=='COLUMNS':
336 writer.writerow(keys)
337 for item in t_values:
338 writer.writerow(item)
341 print 'Not implemented!' #FIXME: implement it.
344 #-----------------------------------------
348 debug stuff from latest rewrite of hooke_playlist.py
349 should be removed sooner or later (or substituted with new debug code!)
352 print confo.load_config('hooke.conf')
354 if __name__ == '__main__':