source: sasview/src/sans/perspectives/invariant/invariant_state.py @ 5777106

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 5777106 was 5777106, checked in by Mathieu Doucet <doucetm@…>, 11 years ago

Moving things around. Will definitely not build.

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