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

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 cadd049e was 75fbd17, checked in by Gervaise Alina <gervyh@…>, 14 years ago

working on save state

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