source: sasview/invariantview/perspectives/invariant/invariant_state.py @ b35d3d1

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since b35d3d1 was b35d3d1, checked in by Jae Cho <jhjcho@…>, 14 years ago

save state file: working, but more work to do

  • Property mode set to 100644
File size: 31.3 KB
Line 
1import time, os, sys
2import logging
3import copy
4import DataLoader
5from xml.dom.minidom import parse
6from lxml import etree
7from DataLoader.readers.cansas_reader import Reader as CansasReader
8from DataLoader.readers.cansas_reader import get_content
9from sans.guiframe.utils import format_number
10from sans.guiframe.dataFitting import Theory1D, Data1D
11INVNODE_NAME = 'invariant'
12CANSAS_NS = "cansas1d/1.0"
13# default state
14list = {'file': 'None',
15        'compute_num':0,
16        'state_num':0,
17        'is_time_machine':False,
18        'background_tcl':0.0,
19        'scale_tcl':1.0,
20        'contrast_tcl':1.0,
21        'porod_constant_tcl':'',
22        'npts_low_tcl':10,
23        'npts_high_tcl':10,
24        'power_high_tcl':4.0,
25        'power_low_tcl': 4.0,
26        'enable_high_cbox':False,
27        'enable_low_cbox':False,
28        'guinier': True,
29        'power_law_high': False,
30        'power_law_low': False,
31        'fit_enable_high': False,
32        'fit_enable_low': False,
33        'fix_enable_high':True,
34        'fix_enable_low':True,
35        'volume_tcl':'',
36        'volume_err_tcl':'',
37        'surface_tcl':'',
38        'surface_err_tcl':''}
39# list of states: This list will be filled as panel init and the number of states increases
40state_list = {}
41bookmark_list = {}
42# list of input parameters (will be filled up on panel init) used by __str__
43input_list = {'background_tcl':0,
44        'scale_tcl':0,
45        'contrast_tcl':0,
46        'porod_constant_tcl':'',
47        'npts_low_tcl':0,
48        'npts_high_tcl':0,
49        'power_high_tcl':0,
50        'power_low_tcl': 0} 
51# list of output parameters (order sensitive) used by __str__   
52output_list = [["qstar_low",                  "Q* from low Q extrapolation [1/(cm*A)]"],
53               ["qstar_low_err",             "dQ* from low Q extrapolation"],
54               ["qstar_low_percent",  "Q* percent from low Q extrapolation"],
55               ["qstar",                                     "Q* from data [1/(cm*A)]"],
56               ["qstar_err",                                "dQ* from data"],
57               ["qstar_percent",                     "Q* percent from data"],
58               ["qstar_high",                "Q* from high Q extrapolation [1/(cm*A)]"],
59               ["qstar_high_err",           "dQ* from high Q extrapolation"],
60               ["qstar_high_percent", "Q* percent from low Q extrapolation"],
61               ["qstar_total",                                   "total Q* [1/(cm*A)]"],
62               ["qstar_total_err",                              "total dQ*"],
63               ["volume",                                 "volume fraction"],
64               ["volume_err",                       "volume fraction error"],
65               ["surface",                               "specific surface"],
66               ["surface_err",                     "specific surface error"]]
67
68   
69
70class InvariantState(object):
71    """
72    Class to hold the state information of the InversionControl panel.
73    """
74    def __init__(self):
75        """
76        Default values
77        """
78        # Input
79        self.file  = None
80        self.data = Data1D(x=[], y=[], dx=None, dy=None)
81        self.theory_lowQ =  Theory1D(x=[], y=[], dy=None)
82        self.theory_highQ = Theory1D(x=[], y=[], dy=None)
83        #self.is_time_machine = False
84        self.saved_state = list
85        self.state_list = state_list
86        self.bookmark_list = bookmark_list
87        self.input_list = input_list
88        self.output_list = output_list
89       
90        self.compute_num = 0
91        self.state_num = 0
92        self.timestamp = ('00:00:00', '00/00/0000')
93        self.container = None
94        #plot image
95        self.wximbmp = None
96        # report_html strings
97        import sans.perspectives.invariant as invariant
98        path = invariant.get_data_path(media='media')
99        path_report_html= os.path.join(path,"report_template.html")
100        html_template=open(path_report_html,"r")
101        self.template_str = html_template.read()
102        self.report_str = self.template_str
103        #self.report_str_save = None
104        html_template.close()
105       
106
107       
108    def __str__(self):
109        """
110        Pretty print
111           
112        : return: string representing the state
113        """
114        # Input string
115        compute_num = self.saved_state['compute_num']
116        compute_state = self.state_list[str(compute_num)]
117        my_time, date = self.timestamp
118        file_name = self.file
119
120        state_num = int(self.saved_state['state_num'])
121        state = "\n[Invariant computation for %s: "% file_name
122        state += "performed at %s on %s] \n"%(my_time, date)
123        state += "State No.: %d \n"% state_num
124        state += "\n=== Inputs ===\n"
125       
126        # text ctl general inputs ( excluding extrapolation text ctl)
127        for key,value in self.input_list.iteritems(): 
128            if value == '':
129                continue
130            key_split = key.split('_') 
131            max_ind = len(key_split)-1
132            if key_split[max_ind] == 'tcl': 
133                name =""
134                if key_split[1]== 'low' or key_split[1]== 'high':
135                    continue
136                for ind in range(0,max_ind):
137                    name +=" %s"%key_split[ind]
138                state += "%s:   %s\n"% (name.lstrip(" "),value)
139               
140        # other input parameters       
141        extra_lo = compute_state['enable_low_cbox']
142        if compute_state['enable_low_cbox']:
143            if compute_state['guinier']:
144                extra_lo = 'Guinier'
145            else:
146                extra_lo = 'Power law'
147        extra_hi = compute_state['enable_high_cbox']
148        if compute_state['enable_high_cbox']:
149            extra_hi = 'Power law'
150        state += "\nExtrapolation:  High=%s; Low=%s\n" %(extra_hi,extra_lo)   
151        low_off = False
152        high_off = False
153        for key,value in self.input_list.iteritems(): 
154            key_split = key.split('_') 
155            max_ind = len(key_split)-1   
156            if key_split[max_ind] == 'tcl': 
157                name ="" 
158                # check each buttons whether or not ON or OFF
159                if key_split[1]== 'low' or key_split[1]== 'high':
160                    if not compute_state['enable_low_cbox'] and key_split[max_ind-1] == 'low':
161                        low_off = True
162                        continue             
163                    elif not compute_state['enable_high_cbox'] and key_split[max_ind-1]== 'high':
164                        high_off = True
165                        continue
166                    elif extra_lo == 'Guinier' and key_split[0]== 'power' and key_split[max_ind-1]== 'low':
167                        continue
168                    for ind in range(0,max_ind):
169                        name +=" %s"%key_split[ind]
170                    name = name.lstrip(" ")
171                    if name == "power low" :
172                        if compute_state['fix_enable_low']:
173                            name += ' (Fixed)'
174                        else:
175                            name += ' (Fitted)'
176                    if name == "power high" :
177                        if compute_state['fix_enable_high']:
178                            name += ' (Fixed)'
179                        else:
180                            name += ' (Fitted)'
181                    state += "%s:   %s\n"% (name,value)
182        # Outputs
183        state += "\n=== Outputs ==="
184        for item in output_list:
185            item_split = item[0].split('_') 
186            # Exclude the extrapolation that turned off
187            if len(item_split)>1:
188                if low_off and item_split[1] == 'low': continue
189                if high_off and item_split[1] == 'high': continue
190            max_ind = len(item_split)-1
191            value = None
192            try:
193                # Q* outputs
194                exec "value = self.container.%s\n"% item[0]
195            except:
196                # other outputs than Q*
197                name = item[0]+"_tcl"
198                exec "value = self.saved_state['%s']"% name
199               
200            # Exclude the outputs w/''   
201            if value == '': continue   
202           
203            # Error outputs
204            if item_split[max_ind] == 'err':
205                state += "+- %s "%format_number(value)
206               
207            # Percentage outputs
208            elif item_split[max_ind] == 'percent':
209                try:
210                    value = float(value)*100
211                except:
212                    pass
213                state += "(%s %s)"%(format_number(value),'%')
214            # Outputs
215            else:
216                state += "\n%s:   %s "%(item[1],format_number(value,high=True))
217        # Include warning msg
218        state += "\n\nNote:\n" + self.container.warning_msg
219       
220        return state
221
222   
223    def clone_state(self):
224        """
225        deepcopy the state
226        """
227        return copy.deepcopy(self.saved_state)
228   
229
230    def toXML(self, file="inv_state.inv", doc=None, entry_node=None):
231        """
232        Writes the state of the InversionControl panel to file, as XML.
233       
234        Compatible with standalone writing, or appending to an
235        already existing XML document. In that case, the XML document
236        is required. An optional entry node in the XML document may also be given.
237       
238        : param file: file to write to
239        : param doc: XML document object [optional]
240        : param entry_node: XML node within the XML document at which we will append the data [optional]   
241        """
242        from xml.dom.minidom import getDOMImplementation
243        import time
244        timestamp = time.time()
245        # Check whether we have to write a standalone XML file
246        if doc is None:
247            impl = getDOMImplementation()
248       
249            doc_type = impl.createDocumentType(INVNODE_NAME, "1.0", "1.0")     
250       
251            newdoc = impl.createDocument(None, INVNODE_NAME, doc_type)
252            top_element = newdoc.documentElement
253        else:
254            # We are appending to an existing document
255            newdoc = doc
256            top_element = newdoc.createElement(INVNODE_NAME)
257            if entry_node is None:
258                newdoc.documentElement.appendChild(top_element)
259            else:
260                entry_node.appendChild(top_element)
261           
262        attr = newdoc.createAttribute("version")
263        attr.nodeValue = '1.0'
264        top_element.setAttributeNode(attr)
265       
266        # File name
267        element = newdoc.createElement("filename")
268        if self.file != None and self.file !='':
269            element.appendChild(newdoc.createTextNode(str(self.file)))
270        else:
271            element.appendChild(newdoc.createTextNode(str(file)))
272        top_element.appendChild(element)
273       
274        element = newdoc.createElement("timestamp")
275        element.appendChild(newdoc.createTextNode(time.ctime(timestamp)))
276        attr = newdoc.createAttribute("epoch")
277        attr.nodeValue = str(timestamp)
278        element.setAttributeNode(attr)
279        top_element.appendChild(element)
280       
281        # Current state
282        state = newdoc.createElement("state")
283        top_element.appendChild(state)
284       
285        for name,value in self.saved_state.iteritems():
286            element = newdoc.createElement(str(name))
287            element.appendChild(newdoc.createTextNode(str(value)))
288            state.appendChild(element)
289             
290        # State history list
291        history = newdoc.createElement("history")
292        top_element.appendChild(history)
293       
294        for name, value in self.state_list.iteritems():
295            history_element = newdoc.createElement('state_'+str(name))
296            for state_name,state_value in value.iteritems():
297                state_element = newdoc.createElement(str(state_name))
298                state_element.appendChild(newdoc.createTextNode(str(state_value)))
299                history_element.appendChild(state_element)
300            #history_element.appendChild(state_list_element)
301            history.appendChild(history_element)
302
303        # Bookmarks  bookmark_list[self.bookmark_num] = [my_time,date,state,comp_state]
304        bookmark = newdoc.createElement("bookmark")
305        top_element.appendChild(bookmark)
306        item_list = ['time','date','state','comp_state']
307        for name, value_list in self.bookmark_list.iteritems():
308            element = newdoc.createElement('mark_'+ str(name))
309            time,date,state,comp_state = value_list
310            time_element = newdoc.createElement('time')
311            time_element.appendChild(newdoc.createTextNode(str(value_list[0])))
312            date_element = newdoc.createElement('date')
313            date_element.appendChild(newdoc.createTextNode(str(value_list[1])))
314            state_list_element = newdoc.createElement('state')
315            comp_state_list_element = newdoc.createElement('comp_state')
316            for state_name,state_value in value_list[2].iteritems():
317                state_element = newdoc.createElement(str(state_name))
318                state_element.appendChild(newdoc.createTextNode(str(state_value)))
319                state_list_element.appendChild(state_element)
320            for comp_name,comp_value in value_list[3].iteritems():
321                comp_element = newdoc.createElement(str(comp_name))
322                comp_element.appendChild(newdoc.createTextNode(str(comp_value)))
323                comp_state_list_element.appendChild(comp_element)
324            element.appendChild(time_element)
325            element.appendChild(date_element)
326            element.appendChild(state_list_element)
327            element.appendChild(comp_state_list_element)
328            bookmark.appendChild(element)
329
330        # Save the file
331        if doc is None:
332            fd = open('test000', 'w')
333            fd.write(newdoc.toprettyxml())
334            fd.close()
335            return None
336        else:
337            return newdoc.toprettyxml()
338       
339    def fromXML(self, file=None, node=None):
340        """
341        Load invariant states from a file
342           
343        : param file: .inv file
344        : param node: node of a XML document to read from       
345        """
346        if file is not None:
347            raise RuntimeError, "InvariantSate no longer supports non-CanSAS format for invariant files"
348       
349        if node.get('version')\
350            and node.get('version') == '1.0':
351
352            # Get file name
353            entry = get_content('ns:filename', node)
354            if entry is not None:
355                file_name = entry.text.strip()
356
357            # Get time stamp
358            entry = get_content('ns:timestamp', node)
359            if entry is not None and entry.get('epoch'):
360                try:
361                    timestamp = (entry.get('epoch'))
362                except:
363                    logging.error("InvariantSate.fromXML: Could not read timestamp\n %s" % sys.exc_value)
364           
365            # Parse bookmarks
366            entry_bookmark = get_content('ns:bookmark', node)
367
368            for ind in range(1,len(entry_bookmark)+1):
369                temp_state = {}
370                temp_bookmark = {}
371                entry = get_content('ns:mark_%s' % ind, entry_bookmark) 
372                               
373                if entry is not None:
374                    time = get_content('ns:time', entry )
375                    val_time = str(time.text.strip())
376                    date = get_content('ns:date', entry )
377                    val_date = str(date.text.strip())
378                    state_entry = get_content('ns:state', entry )
379                    for item in list:
380
381                        input_field = get_content('ns:%s' % item, state_entry )
382                        val = str(input_field.text.strip())
383
384                        if input_field is not None:
385                            try:
386                                exec "temp_state['%s'] = %s"% (item,val)     
387                            except:
388                                exec "temp_state['%s'] = '%s'"% (item,val)
389                           
390                    comp_entry = get_content('ns:comp_state', entry )
391                   
392                    for item in list:
393                        input_field = get_content('ns:%s' % item, comp_entry )
394                        val = str(input_field.text.strip())
395                        if input_field is not None:
396                            try:
397                                exec "temp_bookmark['%s'] = %s"% (item,val)     
398                            except:
399                                exec "temp_bookmark['%s'] = '%s'"% (item,val)
400                    try:
401                        exec "self.bookmark_list[%s] = [val_time,val_date,temp_state,temp_bookmark]"% ind
402
403                    except:
404                        raise "missing components of bookmarks..."
405   
406            # Parse histories
407            entry_history = get_content('ns:history', node)
408
409            for ind in range(0,len(entry_history)):
410                temp_state = {}
411                entry = get_content('ns:state_%s' % ind, entry_history) 
412
413                if entry is not None:
414                    for item in list:
415                        input_field = get_content('ns:%s' % item, entry )
416                        val = str(input_field.text.strip())
417
418                        if input_field is not None:
419                            try:
420                                exec "temp_state['%s'] = %s"% (item,val)         
421                            except:
422                                exec "temp_state['%s'] = '%s'"% (item,val)
423                            finally:
424                                exec "self.state_list['%s'] = temp_state"% ind
425           
426            # Parse current state (ie, saved_state)
427            entry = get_content('ns:state', node)           
428            if entry is not None:
429                for item in list:
430                    input_field = get_content('ns:%s' % item, entry)
431                    val = str(input_field.text.strip())
432                    if input_field is not None:
433                        self.set_saved_state(name=item, value=val)
434                       
435            self.file = file_name
436           
437    def set_report_string(self):
438        """
439        Get the values (strings) from __str__ for report
440        """
441        strings = self.__str__()
442   
443        # default string values
444        for num in range (1,19):
445            exec "s_%s = 'NA'"% str(num)
446        lines = strings.split('\n')
447        # get all string values from __str__()
448        for line in range(0,len(lines)):
449            if line == 1:
450                s_1 = lines[1]
451            elif line == 2:
452                s_2 = lines[2]
453            else:
454                item = lines[line].split(':')
455                item[0] = item[0].strip()
456                if item[0]== "scale":
457                    s_3 = item[1]
458                elif item[0] == "porod constant":
459                    s_4 = item[1]
460                elif item[0] == "background":
461                    s_5 = item[1]
462                elif item[0] == "contrast":
463                    s_6 = item[1]
464                elif item[0] == "Extrapolation":
465                    extra = item[1].split(";")
466                    bool_0 = extra[0].split("=")
467                    bool_1 = extra[1].split("=")
468                    s_7 = " "+bool_0[0]+"Q region = "+bool_0[1]
469                    s_8 = " "+bool_1[0]+"Q region = "+bool_1[1]
470                elif item[0]=="npts low":
471                    s_9 = item[1]
472                elif item[0]=="npts high":
473                    s_10 = item[1]
474                elif item[0] == "volume fraction":
475                    val=item[1].split("+-")[0].strip()
476                    error = item[1].split("+-")[1].strip()
477                    s_17 = val+" &plusmn; "+error
478                elif item[0] == "specific surface":
479                    val=item[1].split("+-")[0].strip()
480                    error = item[1].split("+-")[1].strip()
481                    s_18 = val+" &plusmn; "+error
482                elif item[0].split("(")[0].strip()=="power high":
483                    s_11 = item[0]+" ="+item[1]
484                elif item[0].split("(")[0].strip()=="power low":
485                    s_12 = item[0]+" ="+item[1]
486                elif item[0].split("[")[0].strip()=="Q* from low Q extrapolation":
487                    #looks messy but this way the symbols +_ and % work on html
488                    val=item[1].split("+-")[0].strip()
489                    error = item[1].split("+-")[1].strip()
490                    err = error.split("%")[0].strip()
491                    percent = error.split("%")[1].strip()
492                    s_13 = val+" &plusmn; "+err+"&#37"+percent
493                elif item[0].split("[")[0].strip()=="Q* from data":
494                    val=item[1].split("+-")[0].strip()
495                    error = item[1].split("+-")[1].strip()
496                    err = error.split("%")[0].strip()
497                    percent = error.split("%")[1].strip()
498                    s_14 = val+" &plusmn; "+err+"&#37"+percent
499                elif item[0].split("[")[0].strip()=="Q* from high Q extrapolation":
500                    val=item[1].split("+-")[0].strip()
501                    error = item[1].split("+-")[1].strip()
502                    err = error.split("%")[0].strip()
503                    percent = error.split("%")[1].strip()
504                    s_15 = val+" &plusmn; "+err+"&#37"+percent
505                elif item[0].split("[")[0].strip()=="total Q*":
506                    val=item[1].split("+-")[0].strip()
507                    error = item[1].split("+-")[1].strip()
508                    s_16 = val+" &plusmn; "+error
509                else:
510                    continue
511
512        # make plot image
513        self.set_plot_state(extra_high=bool_0[1],extra_low=bool_1[1])
514        # get ready for report with setting all the html strings
515        self.report_str =  str(self.template_str)% (s_1,s_2,s_3,s_4,s_5,s_6,s_7,s_8,s_9,s_10,s_11,s_12,s_13,s_14,s_15,s_16,s_17,s_18,self.file,"%s")
516
517
518    def set_saved_state(self, name, value):
519        """
520        Set the state list
521                   
522        : param name: name of the state component
523        : param value: value of the state component
524        """
525        rb_list = [['power_law_low','guinier'],['fit_enable_low','fix_enable_low'],['fit_enable_high','fix_enable_high']]
526
527        try:
528            if value == None or value.lstrip().rstrip() =='':
529                exec "self.%s = '%s'" % (name, value)
530                exec "self.saved_state['%s'] = '%s'" %  (name, value)
531            else:
532                exec 'self.%s = %s' % (name, value)
533                exec "self.saved_state['%s'] = %s" %  (name, value)
534
535           
536            # set the count part of radio button clicked False for the saved_state
537            for title,content in rb_list:
538                if name ==  title:
539                    name = content
540                    value = False     
541                elif name == content:
542                    name = title
543                    value = False 
544            exec "self.saved_state['%s'] = '%s'" %  (name, value)     
545            self.state_num = self.saved_state['state_num']
546        except:           
547            pass
548
549    def set_plot_state(self,extra_high=False,extra_low=False):
550        """
551        Build image state that wx.html understand
552        by plotting, putting it into wx.FileSystem image object
553       
554        : extrap_high,extra_low: low/high extrapolations are possible extra-plots
555        """
556        # some imports
557        import wx
558        import matplotlib.pyplot as plt
559        from matplotlib.backends.backend_agg import FigureCanvasAgg
560
561        #we use simple plot, not plotpanel
562        #make matlab figure
563        fig = plt.figure()
564        fig.set_facecolor('w')
565        graph = fig.add_subplot(111)
566
567        #data plot
568        graph.errorbar(self.data.x, self.data.y, yerr=self.data.dy, fmt='o')
569        #low Q extrapolation fit plot
570        if not extra_low == 'False':
571            graph.plot(self.theory_lowQ.x,self.theory_lowQ.y)
572        #high Q extrapolation fit plot
573        if not extra_high == 'False':
574            graph.plot(self.theory_highQ.x,self.theory_highQ.y)
575        graph.set_xscale("log", nonposx='clip')
576        graph.set_yscale("log", nonposy='clip')
577        graph.set_xlabel('$\\rm{Q}(\\AA^{-1})$', fontsize = 12)
578        graph.set_ylabel('$\\rm{Intensity}(cm^{-1})$', fontsize = 12)
579        canvas = FigureCanvasAgg(fig)
580        #actually make image
581        canvas.draw()
582       
583        #make python.Image object
584        #size
585        w,h =canvas.get_width_height()
586        #convert to wx.Image
587        wximg = wx.EmptyImage(w,h)
588        #wxim.SetData(img.convert('RGB').tostring() )
589        wximg.SetData(canvas.tostring_rgb()) 
590        #get the dynamic image for the htmlwindow
591        wximgbmp = wx.BitmapFromImage(wximg)
592        #store the image in wx.FileSystem Object
593        wx.FileSystem.AddHandler(wx.MemoryFSHandler())
594        # use wx.MemoryFSHandler
595        self.imgRAM = wx.MemoryFSHandler()
596        #AddFile, image can be retrieved with 'memory:filename'
597        self.imgRAM.AddFile('img_inv.png',wximgbmp, wx.BITMAP_TYPE_PNG)
598       
599        self.wximgbmp = 'memory:img_inv.png'
600        self.image = fig
601
602class Reader(CansasReader):
603    """
604    Class to load a .inv invariant file
605    """
606    ## File type
607    type_name = "Invariant"
608   
609    ## Wildcards
610    type = ["Invariant file (*.inv)|*.inv",
611            "SANSView file (*.svs)|*.svs"]
612    ## List of allowed extensions
613    ext=['.inv', '.INV', '.svs', 'SVS'] 
614   
615    def __init__(self, call_back, cansas=True):
616        """
617        Initialize the call-back method to be called
618        after we load a file
619       
620        : param call_back: call-back method
621        : param cansas:  True = files will be written/read in CanSAS format
622                        False = write CanSAS format 
623        """
624        ## Call back method to be executed after a file is read
625        self.call_back = call_back
626        ## CanSAS format flag
627        self.cansas = cansas
628
629    def read(self, path):
630        """
631        Load a new invariant state from file
632       
633        : param path: file path
634        : return: None
635        """
636        if self.cansas==True:
637            return self._read_cansas(path)
638        else:
639            return self._read_standalone(path)
640       
641    def _read_standalone(self, path):
642        """
643        Load a new invariant state from file.
644        The invariant node is assumed to be the top element.
645       
646        : param path: file path
647        : return: None
648        """
649        # Read the new state from file
650        state = InvariantState()
651
652        state.fromXML(file=path)
653       
654        # Call back to post the new state
655        self.call_back(state)
656        return None
657   
658    def _parse_state(self, entry):
659        """
660        Read an invariant result from an XML node
661       
662        : param entry: XML node to read from
663        : return: InvariantState object
664        """
665        state = None
666        # Locate the invariant node
667        try:
668            nodes = entry.xpath('ns:%s' % INVNODE_NAME, namespaces={'ns': CANSAS_NS})
669            # Create an empty state
670            if nodes !=[]:
671                state = InvariantState()
672                state.fromXML(node=nodes[0])
673        except:
674            logging.info("XML document does not contain invariant information.\n %s" % sys.exc_value) 
675        return state
676   
677    def _read_cansas(self, path):
678        """
679        Load data and invariant information from a CanSAS XML file.
680       
681        : param path: file path
682        : return: Data1D object if a single SASentry was found,
683                    or a list of Data1D objects if multiple entries were found,
684                    or None of nothing was found
685        : raise RuntimeError: when the file can't be opened
686        : raise ValueError: when the length of the data vectors are inconsistent
687        """
688        output = []
689        if os.path.isfile(path):
690            basename  = os.path.basename(path)
691            root, extension = os.path.splitext(basename)
692           
693            if  extension.lower() in self.ext or \
694                extension.lower() == '.xml':
695                tree = etree.parse(path, parser=etree.ETCompatXMLParser())
696       
697                # Check the format version number
698                # Specifying the namespace will take care of the file format version
699                root = tree.getroot()
700               
701                entry_list = root.xpath('/ns:SASroot/ns:SASentry', namespaces={'ns': CANSAS_NS})
702               
703                for entry in entry_list:
704                   
705                    sas_entry = self._parse_entry(entry)
706                    invstate = self._parse_state(entry)
707                   
708                    #invstate could be None when .svs file is loaded
709                    #in this case, skip appending to output
710                    if invstate != None:
711                        sas_entry.meta_data['invstate'] = invstate
712                        sas_entry.filename = invstate.file
713                        output.append(sas_entry)
714        else:
715            raise RuntimeError, "%s is not a file" % path
716
717        # Return output consistent with the loader's api
718        if len(output)==0:
719            return None
720        elif len(output)==1:
721            # Call back to post the new state
722
723            self.call_back(state=output[0].meta_data['invstate'], datainfo = output[0])
724            return output[0]
725        else:
726            return output               
727   
728   
729    def write(self, filename, datainfo=None, invstate=None):
730        """
731        Write the content of a Data1D as a CanSAS XML file
732       
733        : param filename: name of the file to write
734        : param datainfo: Data1D object
735        : param invstate: InvariantState object
736        """
737        # Sanity check
738        if self.cansas == True:
739            doc = self.write_toXML(datainfo,invstate)
740            # Write the XML document
741            fd = open(filename, 'w')
742            fd.write(doc.toprettyxml())
743            fd.close()
744        else:
745            invstate.toXML(file=filename)
746       
747    def write_toXML(self, datainfo=None, state=None):
748        """
749        Write toXML, a helper for write()
750       
751        : return: xml doc
752        """
753        if datainfo is None:
754            datainfo = DataLoader.data_info.Data1D(x=[], y=[])   
755        elif not issubclass(datainfo.__class__, DataLoader.data_info.Data1D):
756            raise RuntimeError, "The cansas writer expects a Data1D instance: %s" % str(datainfo.__class__.__name__)
757        #make sure title and data run is filled up.
758        if datainfo.title == None or datainfo.title=='': datainfo.title = datainfo.name
759        if datainfo.run_name == None or datainfo.run_name=={}: 
760            datainfo.run = [str(datainfo.name)]
761            datainfo.run_name[0] = datainfo.name
762
763        # Create basic XML document
764        doc, sasentry = self._to_xml_doc(datainfo)
765   
766        # Add the invariant information to the XML document
767        if state is not None:
768            state.toXML(doc=doc, entry_node=sasentry)
769           
770        return doc
Note: See TracBrowser for help on using the repository browser.