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

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 51f14603 was 51f14603, checked in by Peter Parker, 10 years ago

Refs #202 - Fix Sphinx build errors (not including park-1.2.1/). Most warnings remain.

  • Property mode set to 100644
File size: 32.0 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 we will append the data [optional]
255        """
256        from xml.dom.minidom import getDOMImplementation
257        import time
258        timestamp = time.time()
259        # Check whether we have to write a standalone XML file
260        if doc is None:
261            impl = getDOMImplementation()
262       
263            doc_type = impl.createDocumentType(INVNODE_NAME, "1.0", "1.0")     
264       
265            newdoc = impl.createDocument(None, INVNODE_NAME, doc_type)
266            top_element = newdoc.documentElement
267        else:
268            # We are appending to an existing document
269            newdoc = doc
270            top_element = newdoc.createElement(INVNODE_NAME)
271            if entry_node is None:
272                newdoc.documentElement.appendChild(top_element)
273            else:
274                entry_node.appendChild(top_element)
275           
276        attr = newdoc.createAttribute("version")
277        attr.nodeValue = '1.0'
278        top_element.setAttributeNode(attr)
279       
280        # File name
281        element = newdoc.createElement("filename")
282        if self.file != None and self.file != '':
283            element.appendChild(newdoc.createTextNode(str(self.file)))
284        else:
285            element.appendChild(newdoc.createTextNode(str(file)))
286        top_element.appendChild(element)
287       
288        element = newdoc.createElement("timestamp")
289        element.appendChild(newdoc.createTextNode(time.ctime(timestamp)))
290        attr = newdoc.createAttribute("epoch")
291        attr.nodeValue = str(timestamp)
292        element.setAttributeNode(attr)
293        top_element.appendChild(element)
294       
295        # Current state
296        state = newdoc.createElement("state")
297        top_element.appendChild(state)
298       
299        for name,value in self.saved_state.iteritems():
300            element = newdoc.createElement(str(name))
301            element.appendChild(newdoc.createTextNode(str(value)))
302            state.appendChild(element)
303             
304        # State history list
305        history = newdoc.createElement("history")
306        top_element.appendChild(history)
307       
308        for name, value in self.state_list.iteritems():
309            history_element = newdoc.createElement('state_' + str(name))
310            for state_name,state_value in value.iteritems():
311                state_element = newdoc.createElement(str(state_name))
312                child = newdoc.createTextNode(str(state_value))
313                state_element.appendChild(child)
314                history_element.appendChild(state_element)
315            #history_element.appendChild(state_list_element)
316            history.appendChild(history_element)
317
318        # Bookmarks  bookmark_list[self.bookmark_num] = [\
319        #my_time,date,state,comp_state]
320        bookmark = newdoc.createElement("bookmark")
321        top_element.appendChild(bookmark)
322        item_list = ['time','date','state','comp_state']
323        for name, value_list in self.bookmark_list.iteritems():
324            element = newdoc.createElement('mark_'+ str(name))
325            _,date,state,comp_state = value_list
326            time_element = newdoc.createElement('time')
327            time_element.appendChild(newdoc.createTextNode(str(value_list[0])))
328            date_element = newdoc.createElement('date')
329            date_element.appendChild(newdoc.createTextNode(str(value_list[1])))
330            state_list_element = newdoc.createElement('state')
331            comp_state_list_element = newdoc.createElement('comp_state')
332            for state_name, state_value in value_list[2].iteritems():
333                state_element = newdoc.createElement(str(state_name))
334                child = newdoc.createTextNode(str(state_value))
335                state_element.appendChild(child)
336                state_list_element.appendChild(state_element)
337            for comp_name, comp_value in value_list[3].iteritems():
338                comp_element = newdoc.createElement(str(comp_name))
339                comp_element.appendChild(newdoc.createTextNode(str(comp_value)))
340                comp_state_list_element.appendChild(comp_element)
341            element.appendChild(time_element)
342            element.appendChild(date_element)
343            element.appendChild(state_list_element)
344            element.appendChild(comp_state_list_element)
345            bookmark.appendChild(element)
346
347        # Save the file
348        if doc is None:
349            fd = open('test000', 'w')
350            fd.write(newdoc.toprettyxml())
351            fd.close()
352            return None
353        else:
354            return newdoc.toprettyxml()
355       
356    def fromXML(self, file=None, node=None):
357        """
358        Load invariant states from a file
359           
360        : param file: .inv file
361        : param node: node of a XML document to read from       
362        """
363        if file is not None:
364            msg = "InvariantSate no longer supports non-CanSAS"
365            msg += " format for invariant files"
366            raise RuntimeError, msg
367       
368        if node.get('version')\
369            and node.get('version') == '1.0':
370
371            # Get file name
372            entry = get_content('ns:filename', node)
373            if entry is not None:
374                file_name = entry.text.strip()
375
376            # Get time stamp
377            entry = get_content('ns:timestamp', node)
378            if entry is not None and entry.get('epoch'):
379                try:
380                    timestamp = (entry.get('epoch'))
381                except:
382                    msg = "InvariantSate.fromXML: Could not read"
383                    msg += " timestamp\n %s" % sys.exc_value
384                    logging.error(msg)
385           
386            # Parse bookmarks
387            entry_bookmark = get_content('ns:bookmark', node)
388
389            for ind in range(1, len(entry_bookmark) + 1):
390                temp_state = {}
391                temp_bookmark = {}
392                entry = get_content('ns:mark_%s' % ind, entry_bookmark) 
393                               
394                if entry is not None:
395                    my_time = get_content('ns:time', entry)
396                    val_time = str(my_time.text.strip())
397                    date = get_content('ns:date', entry)
398                    val_date = str(date.text.strip())
399                    state_entry = get_content('ns:state', entry)
400                    for item in list:
401
402                        input_field = get_content('ns:%s' % item, state_entry)
403                        val = str(input_field.text.strip())
404                        if input_field is not None:
405                            try:
406                                exec "temp_state['%s'] = %s" % (item, val)     
407                            except:
408                                exec "temp_state['%s'] = '%s'" % (item, val)
409                    comp_entry = get_content('ns:comp_state', entry)
410                   
411                    for item in list:
412                        input_field = get_content('ns:%s' % item, comp_entry)
413                        val = str(input_field.text.strip())
414                        if input_field is not None:
415                            try:
416                                exec "temp_bookmark['%s'] = %s" % (item, val)     
417                            except:
418                                exec "temp_bookmark['%s'] = '%s'" % (item, val)
419                    try:
420                        cmd = "self.bookmark_list[%s] = [val_time,"
421                        cmd += "val_date,temp_state,temp_bookmark]"
422                        exec cmd % ind
423                    except:
424                        raise "missing components of bookmarks..."
425            # Parse histories
426            entry_history = get_content('ns:history', node)
427
428            for ind in range(0, len(entry_history)):
429                temp_state = {}
430                entry = get_content('ns:state_%s' % ind, entry_history) 
431
432                if entry is not None:
433                    for item in list:
434                        input_field = get_content('ns:%s' % item, entry )
435                        val = str(input_field.text.strip())
436
437                        if input_field is not None:
438                            try:
439                                exec "temp_state['%s'] = %s" % (item, val)         
440                            except:
441                                exec "temp_state['%s'] = '%s'" % (item, val)
442                            finally:
443                                exec "self.state_list['%s'] = temp_state" % ind
444            # Parse current state (ie, saved_state)
445            entry = get_content('ns:state', node)           
446            if entry is not None:
447                for item in list:
448                    input_field = get_content('ns:%s' % item, entry)
449                    val = str(input_field.text.strip())
450                    if input_field is not None:
451                        self.set_saved_state(name=item, value=val)
452            self.file = file_name
453           
454    def set_report_string(self):
455        """
456        Get the values (strings) from __str__ for report
457        """
458        strings = self.__str__()
459   
460        # default string values
461        for num in range (1, 19):
462            exec "s_%s = 'NA'" % str(num)
463        lines = strings.split('\n')
464        # get all string values from __str__()
465        for line in range(0, len(lines)):
466            if line == 1:
467                s_1 = lines[1]
468            elif line == 2:
469                s_2 = lines[2]
470            else:
471                item = lines[line].split(':')
472                item[0] = item[0].strip()
473                if item[0] == "scale":
474                    s_3 = item[1]
475                elif item[0] == "porod constant":
476                    s_4 = item[1]
477                elif item[0] == "background":
478                    s_5 = item[1]
479                elif item[0] == "contrast":
480                    s_6 = item[1]
481                elif item[0] == "Extrapolation":
482                    extra = item[1].split(";")
483                    bool_0 = extra[0].split("=")
484                    bool_1 = extra[1].split("=")
485                    s_8 = " " + bool_0[0] + "Q region = " + bool_0[1]
486                    s_7 = " " + bool_1[0] + "Q region = " + bool_1[1]
487                elif item[0] == "npts low":
488                    s_9 = item[1]
489                elif item[0] == "npts high":
490                    s_10 = item[1]
491                elif item[0] == "volume fraction":
492                    val=item[1].split("+-")[0].strip()
493                    error = item[1].split("+-")[1].strip()
494                    s_17 = val + " ± " + error
495                elif item[0] == "specific surface":
496                    val = item[1].split("+-")[0].strip()
497                    error = item[1].split("+-")[1].strip()
498                    s_18 = val + " ± " + error
499                elif item[0].split("(")[0].strip() == "power low":
500                    s_11 = item[0]+" ="+item[1]
501                elif item[0].split("(")[0].strip() == "power high":
502                    s_12 = item[0]+" ="+item[1]
503                elif item[0].split("[")[0].strip() == "Q* from low Q extrapolation":
504                    #looks messy but this way the symbols +_ and % work on html
505                    val = item[1].split("+-")[0].strip()
506                    error = item[1].split("+-")[1].strip()
507                    err = error.split("%")[0].strip()
508                    percent = error.split("%")[1].strip()
509                    s_13 = val + " ± " + err + "&#37" + percent
510                elif item[0].split("[")[0].strip() == "Q* from data":
511                    val = item[1].split("+-")[0].strip()
512                    error = item[1].split("+-")[1].strip()
513                    err = error.split("%")[0].strip()
514                    percent = error.split("%")[1].strip()
515                    s_14 = val + " ± " + err + "&#37" + percent
516                elif item[0].split("[")[0].strip() == "Q* from high Q extrapolation":
517                    val = item[1].split("+-")[0].strip()
518                    error = item[1].split("+-")[1].strip()
519                    err = error.split("%")[0].strip()
520                    percent = error.split("%")[1].strip()
521                    s_15 = val + " ± " + err + "&#37" + percent
522                elif item[0].split("[")[0].strip() == "total Q*":
523                    val = item[1].split("+-")[0].strip()
524                    error = item[1].split("+-")[1].strip()
525                    s_16 = val + " ± " + error
526                else:
527                    continue
528
529        s_1 = self._check_html_format(s_1)
530        file_name = self._check_html_format(self.file)
531
532        # make plot image
533        self.set_plot_state(extra_high=bool_0[1],extra_low=bool_1[1])
534        # get ready for report with setting all the html strings
535        self.report_str = str(self.template_str) % (s_1, s_2,
536                                        s_3, s_4, s_5, s_6, s_7, s_8,
537                                    s_9, s_10, s_11, s_12, s_13, s_14, s_15,
538                                        s_16, s_17, s_18, file_name, "%s")
539
540    def _check_html_format(self, name):
541        """
542        Check string '%' for html format
543        """
544        if name.count('%'):
545            name = name.replace('%', '&#37')
546       
547        return name
548       
549    def set_saved_state(self, name, value):
550        """
551        Set the state list
552                   
553        : param name: name of the state component
554        : param value: value of the state component
555        """
556        rb_list = [['power_law_low','guinier'],
557                   ['fit_enable_low','fix_enable_low'],
558                   ['fit_enable_high','fix_enable_high']]
559
560        try:
561            if value == None or value.lstrip().rstrip() == '':
562                exec "self.%s = '%s'" % (name, value)
563                exec "self.saved_state['%s'] = '%s'" % (name, value)
564            else:
565                exec 'self.%s = %s' % (name, value)
566                exec "self.saved_state['%s'] = %s" % (name, value)
567            # set the count part of radio button clicked
568            # False for the saved_state
569            for title, content in rb_list:
570                if name ==  title:
571                    name = content
572                    value = False     
573                elif name == content:
574                    name = title
575                    value = False 
576            exec "self.saved_state['%s'] = '%s'" % (name, value)     
577            self.state_num = self.saved_state['state_num']
578        except:           
579            pass
580
581    def set_plot_state(self, extra_high=False, extra_low=False):
582        """
583        Build image state that wx.html understand
584        by plotting, putting it into wx.FileSystem image object
585       
586        : extrap_high,extra_low: low/high extrapolations
587        are possible extra-plots
588        """
589        # some imports
590        import wx
591        import matplotlib.pyplot as plt
592        from matplotlib.backends.backend_agg import FigureCanvasAgg
593
594        #we use simple plot, not plotpanel
595        #make matlab figure
596        fig = plt.figure()
597        fig.set_facecolor('w')
598        graph = fig.add_subplot(111)
599
600        #data plot
601        graph.errorbar(self.data.x, self.data.y, yerr=self.data.dy, fmt='o')
602        #low Q extrapolation fit plot
603        if not extra_low == 'False':
604            graph.plot(self.theory_lowQ.x,self.theory_lowQ.y)
605        #high Q extrapolation fit plot
606        if not extra_high == 'False':
607            graph.plot(self.theory_highQ.x,self.theory_highQ.y)
608        graph.set_xscale("log", nonposx='clip')
609        graph.set_yscale("log", nonposy='clip')
610        graph.set_xlabel('$\\rm{Q}(\\AA^{-1})$', fontsize = 12)
611        graph.set_ylabel('$\\rm{Intensity}(cm^{-1})$', fontsize = 12)
612        canvas = FigureCanvasAgg(fig)
613        #actually make image
614        canvas.draw()
615       
616        #make python.Image object
617        #size
618        w, h = canvas.get_width_height()
619        #convert to wx.Image
620        wximg = wx.EmptyImage(w,h)
621        #wxim.SetData(img.convert('RGB').tostring() )
622        wximg.SetData(canvas.tostring_rgb()) 
623        #get the dynamic image for the htmlwindow
624        wximgbmp = wx.BitmapFromImage(wximg)
625        #store the image in wx.FileSystem Object
626        wx.FileSystem.AddHandler(wx.MemoryFSHandler())
627        # use wx.MemoryFSHandler
628        self.imgRAM = wx.MemoryFSHandler()
629        #AddFile, image can be retrieved with 'memory:filename'
630        self.imgRAM.AddFile('img_inv.png', wximgbmp, wx.BITMAP_TYPE_PNG)
631       
632        self.wximgbmp = 'memory:img_inv.png'
633        self.image = fig
634
635class Reader(CansasReader):
636    """
637    Class to load a .inv invariant file
638    """
639    ## File type
640    type_name = "Invariant"
641   
642    ## Wildcards
643    type = ["Invariant file (*.inv)|*.inv",
644            "SANSView file (*.svs)|*.svs"]
645    ## List of allowed extensions
646    ext = ['.inv', '.INV', '.svs', 'SVS'] 
647   
648    def __init__(self, call_back, cansas=True):
649        """
650        Initialize the call-back method to be called
651        after we load a file
652       
653        : param call_back: call-back method
654        : param cansas:  True = files will be written/read in CanSAS format
655                        False = write CanSAS format 
656        """
657        ## Call back method to be executed after a file is read
658        self.call_back = call_back
659        ## CanSAS format flag
660        self.cansas = cansas
661        self.state = None
662
663    def read(self, path):
664        """
665        Load a new invariant state from file
666       
667        : param path: file path
668        : return: None
669        """
670        if self.cansas == True:
671            return self._read_cansas(path)
672        else:
673            return self._read_standalone(path)
674       
675    def _read_standalone(self, path):
676        """
677        Load a new invariant state from file.
678        The invariant node is assumed to be the top element.
679       
680        : param path: file path
681        : return: None
682        """
683        # Read the new state from file
684        state = InvariantState()
685
686        state.fromXML(file=path)
687       
688        # Call back to post the new state
689        self.call_back(state)
690        return None
691   
692    def _parse_state(self, entry):
693        """
694        Read an invariant result from an XML node
695       
696        : param entry: XML node to read from
697        : return: InvariantState object
698        """
699        state = None
700        # Locate the invariant node
701        try:
702            nodes = entry.xpath('ns:%s' % INVNODE_NAME,
703                                namespaces={'ns': CANSAS_NS})
704            # Create an empty state
705            if nodes != []:
706                state = InvariantState()
707                state.fromXML(node=nodes[0])
708        except:
709            msg = "XML document does not contain invariant"
710            msg += " information.\n %s" % sys.exc_value
711            logging.info(msg) 
712        return state
713   
714    def _read_cansas(self, path):
715        """
716        Load data and invariant information from a CanSAS XML file.
717       
718        : param path: file path
719        : return: Data1D object if a single SASentry was found,
720                    or a list of Data1D objects if multiple entries were found,
721                    or None of nothing was found
722        : raise RuntimeError: when the file can't be opened
723        : raise ValueError: when the length of the data vectors are inconsistent
724        """
725        output = []
726        if os.path.isfile(path):
727            basename  = os.path.basename(path)
728            root, extension = os.path.splitext(basename)
729           
730            if  extension.lower() in self.ext or \
731                extension.lower() == '.xml':
732                tree = etree.parse(path, parser=etree.ETCompatXMLParser())
733       
734                # Check the format version number
735                # Specifying the namespace will take care of
736                # the file format version
737                root = tree.getroot()
738               
739                entry_list = root.xpath('/ns:SASroot/ns:SASentry',
740                                        namespaces={'ns': CANSAS_NS})
741               
742                for entry in entry_list:
743                   
744                    sas_entry = self._parse_entry(entry)
745                    invstate = self._parse_state(entry)
746                   
747                    #invstate could be None when .svs file is loaded
748                    #in this case, skip appending to output
749                    if invstate != None:
750                        sas_entry.meta_data['invstate'] = invstate
751                        sas_entry.filename = invstate.file
752                        output.append(sas_entry)
753        else:
754            raise RuntimeError, "%s is not a file" % path
755
756        # Return output consistent with the loader's api
757        if len(output) == 0:
758            return None
759        elif len(output) == 1:
760            # Call back to post the new state
761            self.state = output[0].meta_data['invstate']
762            self.call_back(state=output[0].meta_data['invstate'],
763                          datainfo = output[0])
764            return output[0]
765        else:
766            return output               
767   
768    def get_state(self):
769        return self.state
770   
771    def write(self, filename, datainfo=None, invstate=None):
772        """
773        Write the content of a Data1D as a CanSAS XML file
774       
775        : param filename: name of the file to write
776        : param datainfo: Data1D object
777        : param invstate: InvariantState object
778        """
779        # Sanity check
780        if self.cansas == True:
781            doc = self.write_toXML(datainfo,invstate)
782            # Write the XML document
783            fd = open(filename, 'w')
784            fd.write(doc.toprettyxml())
785            fd.close()
786        else:
787            invstate.toXML(file=filename)
788       
789    def write_toXML(self, datainfo=None, state=None):
790        """
791        Write toXML, a helper for write()
792       
793        : return: xml doc
794        """
795        if datainfo is None:
796            datainfo = sans.dataloader.data_info.Data1D(x=[], y=[])   
797        elif not issubclass(datainfo.__class__, sans.dataloader.data_info.Data1D):
798            msg = "The cansas writer expects a Data1D"
799            msg += " instance: %s" % str(datainfo.__class__.__name__)
800            raise RuntimeError, msg
801        #make sure title and data run is filled up.
802        if datainfo.title == None or datainfo.title == '':
803            datainfo.title = datainfo.name
804        if datainfo.run_name == None or datainfo.run_name == {}: 
805            datainfo.run = [str(datainfo.name)]
806            datainfo.run_name[0] = datainfo.name
807        # Create basic XML document
808        doc, sasentry = self._to_xml_doc(datainfo)
809        # Add the invariant information to the XML document
810        if state is not None:
811            state.toXML(datainfo.name,doc=doc, entry_node=sasentry)
812        return doc
813
Note: See TracBrowser for help on using the repository browser.