source: sasview/sansview/perspectives/fitting/pagestate.py @ 1b43306

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 1b43306 was 5062bbf, checked in by Gervaise Alina <gervyh@…>, 14 years ago

working on documentation

  • Property mode set to 100644
File size: 53.8 KB
RevLine 
[c77d859]1
2
[5062bbf]3################################################################################
4#This software was developed by the University of Tennessee as part of the
5#Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
6#project funded by the US National Science Foundation.
7#
8#See the license text in license.txt
9#
10#copyright 2009, University of Tennessee
11################################################################################
12
[11a7e11]13import time
14import os
15import sys
[6f023e8]16import copy
[11a7e11]17import logging
[c77d859]18
[35b556d]19import xml.dom.minidom
[11a7e11]20from xml.dom.minidom import parse
21from lxml import etree
22
23import DataLoader
[61cada5]24from DataLoader.readers.cansas_reader import Reader as CansasReader
[e9b12eaf]25from DataLoader.readers.cansas_reader import get_content, write_node
[35b556d]26from DataLoader.data_info import Data2D
[61cada5]27
[11a7e11]28#Information to read/write state as xml
[61cada5]29FITTING_NODE_NAME = 'fitting_plug_in'
30CANSAS_NS = "cansas1d/1.0"
31
[26f3dd5]32list_of_data_attributes = [["is_data", "is_data", "bool"],
[61cada5]33                      ["group_id", "data_group_id", "string"],
34                      ["data_name", "data_name", "string"],
35                      ["data_id", "data_id", "string"],
36                      ["name", "name", "string"],
[26f3dd5]37                      ["data_name", "data_name", "string"]]
38list_of_state_attributes = [["qmin", "qmin", "float"],
[61cada5]39                      ["qmax", "qmax", "float"],
40                      ["npts", "npts", "float"],
41                      ["shape_rbutton", "shape_rbutton", "bool"],
42                      ["shape_indep_rbutton", "shape_indep_rbutton", "bool"],
43                      ["plugin_rbutton", "plugin_rbutton","bool"],
44                      ["struct_rbutton", "struct_rbutton", "bool"],
45                      ["formfactorcombobox", "formfactorcombobox", "bool"],
46                      ["structurecombobox", "structurecombobox", "bool"],
[26f3dd5]47                      ["disp_box", "disp_box"],
48                      ["enable_smearer","enable_smearer","bool"],
49                      ["disable_smearer","disable_smearer","bool"],
50                      ["pinhole_smearer","pinhole_smearer","bool"],
51                      ["slit_smearer","slit_smearer","bool"],
52                      ["enable_disp","enable_disp","bool"],
53                      ["disable_disp","disable_disp","bool"],
54                      ["slit_smearer","slit_smearer","bool"],
55                      ["enable2D","enable2D","bool"],
56                      ["cb1","cb1","bool"],
[5cc7001]57                      ["tcChi","tcChi","float"],
58                     ["smearer", "smearer", "float"],
59                     ["smear_type","smear_type", "string"],
60                     ["dq_l", "dq_l", "string"],
61                     ["dq_r","dq_r", "string"]]
62
[11a7e11]63list_of_model_attributes = [["values", "values"],
64                            ["weights", "weights"]]
65
[61cada5]66list_of_state_parameters = [["parameters", "parameters"] ,                     
67                            ["orientation_parameters", "orientation_params"],
68                            ["dispersity_parameters", "orientation_params_disp"],
69                            ["fixed_param", "fixed_param"],                     
70                            ["fittable_param","fittable_param"]]
[e9b12eaf]71list_of_data_2d_attr = [["xmin", "xmin"],
72                        ["xmax","xmax"],
73                        ["ymin","ymin"],
74                        ["ymax","ymax"],
75                        ["_xaxis","_xaxis"],
76                        ["_xunit", "_xunit"],
77                        ["_yaxis","_yaxis"],
78                        ["_yunit","_yunit"],
79                        ["_zaxis","_zaxis"],
80                        ["_zunit","_zunit"]]
81list_of_data2d_values = [["qx_data","qx_data"],
82                         ["qy_data","qy_data"],
[35b556d]83                         ["dqx_data","dqx_data"],
84                         ["dqy_data","dqy_data"],
[e9b12eaf]85                         ["data","data"],
86                         ["q_data","q_data"],
87                         ["err_data","err_data"],
88                         ["mask","mask"],]
[61cada5]89
[cfc0913]90class PageState(object):
[c77d859]91    """
[5062bbf]92    Contains information to reconstruct a page of the fitpanel.
[c77d859]93    """
[61cada5]94    def __init__(self, parent=None, model=None, data=None):
[c77d859]95       
96        """
[5062bbf]97        Initialize the current state
98       
99        :param model: a selected model within a page
100        :param data:
101       
[c77d859]102        """
[61cada5]103        self.file = None
[11a7e11]104        #Time of state creation
105        self.timestamp = time.time()
[c77d859]106        ## Data member to store the dispersion object created
107        self._disp_obj_dict = {}
[61cada5]108        #------------------------
[c77d859]109        #Data used for fitting
110        self.data = data
[61cada5]111        #save additional information on data that dataloader.reader does not read
112        self.is_data = None
113        self.data_name = ""
114       
115        if self.data is not None:
116            self.data_name = self.data.name
117        self.data_id = None
118        if self.data is not None and hasattr(self.data, "id"):
119            self.data_id = self.data.id
120        self.data_group_id = None
121        if self.data is not None and hasattr(self.data, "group_id"):
122            self.data_group_id = self.data.group_id
123        #-------------------------
124        ## reset True change the state of exsiting button
125        self.reset = False
126       
[c99a6c5]127        #engine type
128        self.engine_type = None
[c77d859]129        # flag to allow data2D plot
[cfc0913]130        self.enable2D = False
[c77d859]131        # model on which the fit would be performed
132        self.model = model
[61cada5]133       
[c77d859]134        #fit page manager
135        self.manager = None
136        #Store the parent of this panel parent
137        # For this application fitpanel is the parent
138        self.parent  = parent
139        # Event_owner is the owner of model event
140        self.event_owner = None
[c9a4377]141        ##page name
[cfc0913]142        self.page_name = ""
[c77d859]143        # Contains link between  model ,all its parameters, and panel organization
[61cada5]144        self.parameters = []
[c77d859]145        # Contains list of parameters that cannot be fitted and reference to
146        #panel objects
[61cada5]147        self.fixed_param = []
[c77d859]148        # Contains list of parameters with dispersity and reference to
149        #panel objects
[61cada5]150        self.fittable_param = []
[60132ef]151        ## orientation parameters
[61cada5]152        self.orientation_params = []
[fc6ea43]153        ## orientation parmaters for gaussian dispersity
[61cada5]154        self.orientation_params_disp = []
[3370922]155        ## smearer info
[61cada5]156        self.smearer = None
[7609f1a]157        self.smear_type = None
158        self.dq_l = None
159        self.dq_r = None
160
[c77d859]161        #list of dispersion paramaters
[cfc0913]162        self.disp_list =[]
[61cada5]163        if self.model is not None:
[71f0373]164            self.disp_list = self.model.getDispParamList()
[61cada5]165        self._disp_obj_dict = {}
166        self.disp_cb_dict = {}
167        self.values = []
168        self.weights = []
[dad49a0]169                   
[c77d859]170        #contains link between a model and selected parameters to fit
[61cada5]171        self.param_toFit = []
[a074145]172        ##dictionary of model type and model class
[cfc0913]173        self.model_list_box = None
[a074145]174        ## save the state of the context menu
[61cada5]175        self.saved_states = {}
[240b9966]176        ## save selection of combobox
[3b9e023]177        self.formfactorcombobox = None
178        self.structurecombobox  = None
[240b9966]179        ## radio box to select type of model
180        self.shape_rbutton = False
181        self.shape_indep_rbutton = False
182        self.struct_rbutton = False
183        self.plugin_rbutton = False
[b787e68c]184        ## the indice of the current selection
185        self.disp_box = 0
[c77d859]186        ## Qrange
[cfc0913]187        ## Q range
[61cada5]188        self.qmin = 0.001
189        self.qmax = 0.1
[cfc0913]190        self.npts = None
[61cada5]191        self.name = ""
[cfc0913]192        ## enable smearering state
193        self.enable_smearer = False
[fc6ea43]194        self.disable_smearer = True
[7609f1a]195        self.pinhole_smearer = False
196        self.slit_smearer   = False
[cfc0913]197        ## disperity selection
[61cada5]198        self.enable_disp = False
199        self.disable_disp = True
[11a7e11]200       
[cfc0913]201        ## state of selected all check button
202        self.cb1 = False
[0aeabc6]203        ## store value of chisqr
[61cada5]204        self.tcChi = None
[240b9966]205   
[6f023e8]206    def clone(self):
[61cada5]207        """
[5062bbf]208        Create a new copy of the current object
[61cada5]209        """
210        model = None
211        if self.model is not None:
[6f023e8]212            model = self.model.clone()
[bb70474]213            model.name = self.model.name
[61cada5]214        obj = PageState(self.parent, model=model)
215        obj.file = copy.deepcopy(self.file)
[6f023e8]216        obj.data = copy.deepcopy(self.data)
[61cada5]217        if self.data is not None:
218            self.data_name = self.data.name
219        obj.data_name = self.data_name
220        obj.is_data = self.is_data
[dcf29d7]221        obj.model_list_box = copy.deepcopy(self.model_list_box)
[c99a6c5]222        obj.engine_type = copy.deepcopy(self.engine_type)
[240b9966]223       
[61cada5]224        obj.formfactorcombobox = self.formfactorcombobox
225        obj.structurecombobox  = self.structurecombobox 
[240b9966]226       
227        obj.shape_rbutton = self.shape_rbutton
228        obj.shape_indep_rbutton = self.shape_indep_rbutton
229        obj.struct_rbutton = self.struct_rbutton
230        obj.plugin_rbutton = self.plugin_rbutton
231       
[dcf29d7]232        obj.manager = self.manager
233        obj.event_owner = self.event_owner
[71f0373]234        obj.disp_list = copy.deepcopy(self.disp_list)
[fc6ea43]235       
[c477b31]236        obj.enable2D = copy.deepcopy(self.enable2D)
[cfc0913]237        obj.parameters = copy.deepcopy(self.parameters)
238        obj.fixed_param = copy.deepcopy(self.fixed_param)
239        obj.fittable_param = copy.deepcopy(self.fittable_param)
[60132ef]240        obj.orientation_params =  copy.deepcopy(self.orientation_params)
[fc6ea43]241        obj.orientation_params_disp =  copy.deepcopy(self.orientation_params_disp)
[b787e68c]242        obj.enable_disp = copy.deepcopy(self.enable_disp)
[fc6ea43]243        obj.disable_disp = copy.deepcopy(self.disable_disp)
[0aeabc6]244        obj.tcChi = self.tcChi
245 
[c477b31]246        if len(self._disp_obj_dict)>0:
247            for k , v in self._disp_obj_dict.iteritems():
248                obj._disp_obj_dict[k]= v
[dad49a0]249        if len(self.disp_cb_dict)>0:
250            for k , v in self.disp_cb_dict.iteritems():
251                obj.disp_cb_dict[k]= v
[b421b1a]252               
[dad49a0]253        obj.values = copy.deepcopy(self.values)
254        obj.weights = copy.deepcopy(self.weights)
[b787e68c]255        obj.enable_smearer = copy.deepcopy(self.enable_smearer)
[fc6ea43]256        obj.disable_smearer = copy.deepcopy(self.disable_smearer)
[7609f1a]257        obj.pinhole_smearer = copy.deepcopy(self.pinhole_smearer)
258        obj.slit_smearer = copy.deepcopy(self.slit_smearer)
259        obj.smear_type = copy.deepcopy(self.smear_type)
260        obj.dq_l = copy.deepcopy(self.dq_l)
261        obj.dq_r = copy.deepcopy(self.dq_r)
262
[b787e68c]263        obj.disp_box = copy.deepcopy(self.disp_box)
264        obj.qmin = copy.deepcopy(self.qmin)
265        obj.qmax = copy.deepcopy(self.qmax)
266        obj.npts = copy.deepcopy(self.npts )
267        obj.cb1 = copy.deepcopy(self.cb1)
[3370922]268        obj.smearer = copy.deepcopy(self.smearer)
269       
[a074145]270        for name, state in self.saved_states.iteritems():
271            copy_name = copy.deepcopy(name)
272            copy_state = state.clone()
273            obj.saved_states[copy_name]= copy_state
[6f023e8]274        return obj
[61cada5]275   
276    def _repr_helper(self, list, rep):
277        """
[5062bbf]278        Helper method to print a state
[61cada5]279        """
280        for item in list:
[240b9966]281            rep += "parameter name: %s \n"%str(item[1])
282            rep += "value: %s\n"%str(item[2])
283            rep += "selected: %s\n"%str(item[0])
284            rep += "error displayed : %s \n"%str(item[4][0])
285            rep += "error value:%s \n"%str(item[4][1])
286            rep += "minimum displayed : %s \n"%str(item[5][0])
287            rep += "minimum value : %s \n"%str(item[5][1])
288            rep += "maximum displayed : %s \n"%str(item[6][0])
289            rep += "maximum value : %s \n"%str(item[6][1])
290            rep += "parameter unit: %s\n\n"%str(item[7])
291        return rep
[61cada5]292   
293    def __repr__(self):
[11a7e11]294        """
[5062bbf]295        output string for printing
[11a7e11]296        """
297        rep = "\nState name: %s\n"%self.file
298        t = time.localtime(self.timestamp)
299        time_str = time.strftime("%b %d %H:%M", t)
300        rep += "State created on : %s\n"%time_str
301        rep += "State form factor combobox selection: %s\n"%self.formfactorcombobox
302        rep += "State structure factor combobox selection: %s\n"%self.structurecombobox
303        rep += "is data : %s\n"%self.is_data
304        rep += "data's name : %s\n"%self.data_name
305        rep += "data's id : %s\n"%self.data_id
306        rep += "model type (form factor) selected: %s\n"%self.shape_rbutton
307        rep += "model type (shape independent) selected: %s\n"%self.shape_indep_rbutton
308        rep += "model type (structure factor) selected: %s\n"%self.struct_rbutton
309        rep += "model type (plug-in ) selected: %s\n"%self.plugin_rbutton
310        rep += "data : %s\n"% str(self.data)
311        rep += "Plotting Range: min: %s, max: %s, steps: %s\n"%(str(self.qmin),
312                                                str(self.qmax),str(self.npts))
313        """
314        rep += "model  : %s\n\n"% str(self.model)
315        rep += "number parameters(self.parameters): %s\n"%len(self.parameters)
316        rep += self._repr_helper( list=self.parameters, rep=rep)
317        rep += "number orientation parameters"
318        rep += "(self.orientation_params): %s\n"%len(self.orientation_params)
319        rep += self._repr_helper( list=self.orientation_params, rep=rep)
320        rep += "number dispersity parameters"
321        rep += "(self.orientation_params_disp): %s\n"%len(self.orientation_params_disp)
322        rep += self._repr_helper( list=self.orientation_params_disp, rep=rep)
323        """
[61cada5]324        return rep
325   
326    def _toXML_helper(self, list, element, newdoc):
327        """
[5062bbf]328        Helper method to create xml file for saving state
[61cada5]329        """
330        for item in list:
331            sub_element = newdoc.createElement('parameter')
332            sub_element.setAttribute('name', str(item[1]))
333            sub_element.setAttribute('value', str(item[2]))
334            sub_element.setAttribute('selected_to_fit', str(item[0]))
335            sub_element.setAttribute('error_displayed', str(item[4][0]))
336            sub_element.setAttribute('error_value', str(item[4][1]))
337            sub_element.setAttribute('minimum_displayed', str(item[5][0]))
338            sub_element.setAttribute('minimum_value', str(item[5][1]))
339            sub_element.setAttribute('maximum_displayed', str(item[6][0]))
340            sub_element.setAttribute('maximum_value', str(item[6][1]))
341            sub_element.setAttribute('unit', str(item[7]))
342            element.appendChild(sub_element)
343       
344    def toXML(self, file="fitting_state.fitv", doc=None, entry_node=None):
345        """
[5062bbf]346        Writes the state of the InversionControl panel to file, as XML.
347       
348        Compatible with standalone writing, or appending to an
349        already existing XML document. In that case, the XML document
350        is required. An optional entry node in the XML document may also be given.
351       
352        :param file: file to write to
353        :param doc: XML document object [optional]
354        :param entry_node: XML node within the XML document at which we will append the data [optional]
355       
[61cada5]356        """
357        from xml.dom.minidom import getDOMImplementation
[71f0373]358
[61cada5]359        # Check whether we have to write a standalone XML file
360        if doc is None:
361            impl = getDOMImplementation()
362            doc_type = impl.createDocumentType(FITTING_NODE_NAME, "1.0", "1.0")     
363            newdoc = impl.createDocument(None, FITTING_NODE_NAME, doc_type)
364            top_element = newdoc.documentElement
365        else:
366            # We are appending to an existing document
367            newdoc = doc
368            top_element = newdoc.createElement(FITTING_NODE_NAME)
369            if entry_node is None:
370                newdoc.documentElement.appendChild(top_element)
371            else:
372                entry_node.appendChild(top_element)
373           
374        attr = newdoc.createAttribute("version")
375        attr.nodeValue = '1.0'
376        top_element.setAttributeNode(attr)
[cfc0913]377       
[61cada5]378        # File name
379        element = newdoc.createElement("filename")
380        if self.file is not None:
381            element.appendChild(newdoc.createTextNode(str(self.file)))
382        else:
383            element.appendChild(newdoc.createTextNode(str(file)))
384        top_element.appendChild(element)
[11a7e11]385       
386        element = newdoc.createElement("timestamp")
387        element.appendChild(newdoc.createTextNode(time.ctime(self.timestamp)))
388        attr = newdoc.createAttribute("epoch")
389        attr.nodeValue = str(self.timestamp)
390        element.setAttributeNode(attr)
391        top_element.appendChild(element)
[61cada5]392        # Inputs
393        inputs = newdoc.createElement("Attributes")
394        top_element.appendChild(inputs)
395       
396        element = newdoc.createElement('data_attributes')
397        if self.data is not None and hasattr(self.data, "group_id"):
398            self.data_group_id = self.data.group_id
399        if self.data is not None and hasattr(self.data, "is_data"):
400            self.is_data = self.data.is_data
401        if self.data is not None:
402            self.data_name = self.data.name
403        if self.data is not None and hasattr(self.data, "id"):
404            self.data_id = self.data.id
405       
[26f3dd5]406        for item in list_of_data_attributes:
[61cada5]407            exec "element.setAttribute(item[0], str(self.%s))"%(item[1])
408        inputs.appendChild(element)   
[26f3dd5]409       
410        for item in list_of_state_attributes:
411            element = newdoc.createElement(item[0])
412            exec "element.setAttribute(item[0], str(self.%s))"%(item[1])
413            inputs.appendChild(element)
[11a7e11]414           
415        for item in list_of_model_attributes:
416            element = newdoc.createElement(item[0])
417            exec "list = self.%s"%item[1]
[e9b12eaf]418            for value in list:
419                exec "element.appendChild(newdoc.createTextNode(str(%s)))" % value
[11a7e11]420            inputs.appendChild(element)
421           
[61cada5]422        for item in list_of_state_parameters:
423            element = newdoc.createElement(item[0])
424            exec "self._toXML_helper(list=self.%s, element=element, newdoc=newdoc)"%item[1]                       
425            inputs.appendChild(element)
426       
427        # Save the file
428        if doc is None:
429            fd = open(file, 'w')
430            fd.write(newdoc.toprettyxml())
431            fd.close()
432            return None
433        else:
434            return newdoc.toprettyxml()
435       
436    def _fromXML_helper(self, node, list):
437        """
[5062bbf]438        Helper function to write state to xml
[61cada5]439        """
440        for item in node:
441            name = item.get('name')
442            value = item.get('value')
443            selected_to_fit = item.get('selected_to_fit')
444            error_displayed = item.get('error_displayed')
445            error_value = item.get('error_value')
446            minimum_displayed = item.get('minimum_displayed')
447            minimum_value = item.get('minimum_value')
448            maximum_displayed = item.get('maximum_displayed')
449            maximum_value = item.get('maximum_value')
450            unit = item.get('unit')
451            list.append([selected_to_fit, name, value, "+/-",[error_displayed, error_value],
452                         [minimum_displayed,minimum_value],[maximum_displayed,maximum_value], unit])
[cfc0913]453       
[61cada5]454    def fromXML(self, file=None, node=None):
455        """
[5062bbf]456        Load fitting state from a file
457       
458        :param file: .fitv file
459        :param node: node of a XML document to read from
460       
[61cada5]461        """
462     
463        if file is not None:
[11a7e11]464            msg = "PageState no longer supports non-CanSAS"
465            msg += " format for fitting files"
466            raise RuntimeError, msg
[61cada5]467           
[11a7e11]468        if node.get('version')and node.get('version') == '1.0':
[61cada5]469           
470            # Get file name
471            entry = get_content('ns:filename', node)
472            if entry is not None:
473                self.file = entry.text.strip()
[11a7e11]474               
475            # Get time stamp
476            entry = get_content('ns:timestamp', node)
477            if entry is not None and entry.get('epoch'):
478                try:
479                    self.timestamp = float(entry.get('epoch'))
480                except:
481                    msg = "PageState.fromXML: Could not"
482                    msg += " read timestamp\n %s" % sys.exc_value
483                    logging.error(msg)
484           
[61cada5]485            # Parse fitting attributes
486            entry = get_content('ns:Attributes', node)
487            if entry is not None:
488                for item in list_of_state_attributes:
489                    field = get_content('ns:%s'%item[0], entry)
490                    if field is not None:
491                        if item[2] == "string":
492                            exec "self.%s= str(field.text)"%item[1]
493                        elif item[2] == "bool":
494                            try:
495                                exec "self.%s= field.get(str(%s))"%(item[1], item[0])
496                            except:
497                                exec "self.%s = None"%item[1]
498                        else:
499                            try:
500                                exec "self.%s = float(field.get(%s))"%(item[1], item[0])
501                            except:
502                                exec "self.%s = None"%item[1]
[11a7e11]503                for item in list_of_model_attributes:
504                    node = get_content("ns:%s"%item[0], entry)
505                    list = []
506                    for value in node:
507                        try:
508                            list.append(float(value)) 
509                        except:
510                            list.append(None)
511                       
512                    exec "self.%s = list"%item[1]
513                   
[61cada5]514                for item in list_of_state_parameters:
[11a7e11]515                    node = get_content("ns:%s"%item[0], entry)
516                    self._fromXML_helper(node=node, list=self.parameters)
[61cada5]517               
518
519class Reader(CansasReader):
520    """
[5062bbf]521    Class to load a .fitv fitting file
[61cada5]522    """
523    ## File type
524    type_name = "Fitting"
525   
526    ## Wildcards
527    type = ["Fitting files (*.fitv)|*.fitv"]
528    ## List of allowed extensions
529    ext=['.fitv', '.FITV'] 
530   
531    def __init__(self, call_back=None, cansas=True):
532        """
[5062bbf]533        Initialize the call-back method to be called
534        after we load a file
535       
536        :param call_back: call-back method
537        :param cansas:  True = files will be written/read in CanSAS format
538                        False = write CanSAS format
[61cada5]539           
540        """
541        ## Call back method to be executed after a file is read
542        self.call_back = call_back
543        ## CanSAS format flag
544        self.cansas = cansas
545       
546    def read(self, path):
547        """
[5062bbf]548        Load a new P(r) inversion state from file
549       
550        :param path: file path
551       
[61cada5]552        """
[e9b12eaf]553        if self.cansas == True:
[61cada5]554            return self._read_cansas(path)
[e9b12eaf]555     
556    def _data2d_to_xml_doc(self, datainfo):
[61cada5]557        """
[5062bbf]558        Create an XML document to contain the content of a Data2D
559       
560        :param datainfo: Data2D object
561       
[61cada5]562        """
563        if not issubclass(datainfo.__class__, Data2D):
564            raise RuntimeError, "The cansas writer expects a Data2D instance"
565       
566        doc = xml.dom.minidom.Document()
567        main_node = doc.createElement("SASroot")
568        main_node.setAttribute("version", self.version)
569     
570        doc.appendChild(main_node)
571       
572        entry_node = doc.createElement("SASentry")
573        main_node.appendChild(entry_node)
574       
575        write_node(doc, entry_node, "Title", datainfo.title)
576       
577        for item in datainfo.run:
578            runname = {}
579            if datainfo.run_name.has_key(item) and len(str(datainfo.run_name[item]))>1:
580                runname = {'name': datainfo.run_name[item] }
581            write_node(doc, entry_node, "Run", item, runname)
582        # Data info
583        node = doc.createElement("SASdata")
584        entry_node.appendChild(node)
[e9b12eaf]585        for item in list_of_data_2d_attr:
586            element = doc.createElement(item[0])
587            exec "element.setAttribute(item[0], str(datainfo.%s))"%(item[1])
588            entry_node.appendChild(element)
589        for item in list_of_data2d_values:
590            element = doc.createElement(item[0])
[35b556d]591            exec "temp_list = datainfo.%s"%item[1]
592            if temp_list is None or len(temp_list)== 0:
593                exec "element.appendChild(doc.createTextNode(str(%s)))"%temp_list
594            else:
595                for value in temp_list:
596                    exec "element.appendChild(doc.createTextNode(str(%s)))"%value
[e9b12eaf]597            entry_node.appendChild(element)
598     
[61cada5]599        # Sample info
600        sample = doc.createElement("SASsample")
601        if datainfo.sample.name is not None:
602            sample.setAttribute("name", str(datainfo.sample.name))
603        entry_node.appendChild(sample)
604        write_node(doc, sample, "ID", str(datainfo.sample.ID))
605        write_node(doc, sample, "thickness", datainfo.sample.thickness, {"unit":datainfo.sample.thickness_unit})
606        write_node(doc, sample, "transmission", datainfo.sample.transmission)
607        write_node(doc, sample, "temperature", datainfo.sample.temperature, {"unit":datainfo.sample.temperature_unit})
608       
609        for item in datainfo.sample.details:
610            write_node(doc, sample, "details", item)
611       
612        pos = doc.createElement("position")
613        written = write_node(doc, pos, "x", datainfo.sample.position.x, {"unit":datainfo.sample.position_unit})
614        written = written | write_node(doc, pos, "y", datainfo.sample.position.y, {"unit":datainfo.sample.position_unit})
615        written = written | write_node(doc, pos, "z", datainfo.sample.position.z, {"unit":datainfo.sample.position_unit})
616        if written == True:
617            sample.appendChild(pos)
618       
619        ori = doc.createElement("orientation")
620        written = write_node(doc, ori, "roll",  datainfo.sample.orientation.x, {"unit":datainfo.sample.orientation_unit})
621        written = written | write_node(doc, ori, "pitch", datainfo.sample.orientation.y, {"unit":datainfo.sample.orientation_unit})
622        written = written | write_node(doc, ori, "yaw",   datainfo.sample.orientation.z, {"unit":datainfo.sample.orientation_unit})
623        if written == True:
624            sample.appendChild(ori)
625       
626        # Instrument info
627        instr = doc.createElement("SASinstrument")
628        entry_node.appendChild(instr)
629       
630        write_node(doc, instr, "name", datainfo.instrument)
631       
632        #   Source
633        source = doc.createElement("SASsource")
634        if datainfo.source.name is not None:
635            source.setAttribute("name", str(datainfo.source.name))
636        instr.appendChild(source)
637       
638        write_node(doc, source, "radiation", datainfo.source.radiation)
639        write_node(doc, source, "beam_shape", datainfo.source.beam_shape)
640        size = doc.createElement("beam_size")
641        if datainfo.source.beam_size_name is not None:
642            size.setAttribute("name", str(datainfo.source.beam_size_name))
643        written = write_node(doc, size, "x", datainfo.source.beam_size.x, {"unit":datainfo.source.beam_size_unit})
644        written = written | write_node(doc, size, "y", datainfo.source.beam_size.y, {"unit":datainfo.source.beam_size_unit})
645        written = written | write_node(doc, size, "z", datainfo.source.beam_size.z, {"unit":datainfo.source.beam_size_unit})
646        if written == True:
647            source.appendChild(size)
648           
649        write_node(doc, source, "wavelength", datainfo.source.wavelength, {"unit":datainfo.source.wavelength_unit})
650        write_node(doc, source, "wavelength_min", datainfo.source.wavelength_min, {"unit":datainfo.source.wavelength_min_unit})
651        write_node(doc, source, "wavelength_max", datainfo.source.wavelength_max, {"unit":datainfo.source.wavelength_max_unit})
652        write_node(doc, source, "wavelength_spread", datainfo.source.wavelength_spread, {"unit":datainfo.source.wavelength_spread_unit})
653       
654        #   Collimation
655        for item in datainfo.collimation:
656            coll = doc.createElement("SAScollimation")
657            if item.name is not None:
658                coll.setAttribute("name", str(item.name))
659            instr.appendChild(coll)
660           
661            write_node(doc, coll, "length", item.length, {"unit":item.length_unit})
662           
663            for apert in item.aperture:
664                ap = doc.createElement("aperture")
665                if apert.name is not None:
666                    ap.setAttribute("name", str(apert.name))
667                if apert.type is not None:
668                    ap.setAttribute("type", str(apert.type))
669                coll.appendChild(ap)
670               
671                write_node(doc, ap, "distance", apert.distance, {"unit":apert.distance_unit})
672               
673                size = doc.createElement("size")
674                if apert.size_name is not None:
675                    size.setAttribute("name", str(apert.size_name))
676                written = write_node(doc, size, "x", apert.size.x, {"unit":apert.size_unit})
677                written = written | write_node(doc, size, "y", apert.size.y, {"unit":apert.size_unit})
678                written = written | write_node(doc, size, "z", apert.size.z, {"unit":apert.size_unit})
679                if written == True:
680                    ap.appendChild(size)
681
682        #   Detectors
683        for item in datainfo.detector:
684            det = doc.createElement("SASdetector")
685            written = write_node(doc, det, "name", item.name)
686            written = written | write_node(doc, det, "SDD", item.distance, {"unit":item.distance_unit})
687            written = written | write_node(doc, det, "slit_length", item.slit_length, {"unit":item.slit_length_unit})
688            if written == True:
689                instr.appendChild(det)
690           
691            off = doc.createElement("offset")
692            written = write_node(doc, off, "x", item.offset.x, {"unit":item.offset_unit})
693            written = written | write_node(doc, off, "y", item.offset.y, {"unit":item.offset_unit})
694            written = written | write_node(doc, off, "z", item.offset.z, {"unit":item.offset_unit})
695            if written == True:
696                det.appendChild(off)
697           
698            center = doc.createElement("beam_center")
699            written = write_node(doc, center, "x", item.beam_center.x, {"unit":item.beam_center_unit})
700            written = written | write_node(doc, center, "y", item.beam_center.y, {"unit":item.beam_center_unit})
701            written = written | write_node(doc, center, "z", item.beam_center.z, {"unit":item.beam_center_unit})
702            if written == True:
703                det.appendChild(center)
704               
705            pix = doc.createElement("pixel_size")
706            written = write_node(doc, pix, "x", item.pixel_size.x, {"unit":item.pixel_size_unit})
707            written = written | write_node(doc, pix, "y", item.pixel_size.y, {"unit":item.pixel_size_unit})
708            written = written | write_node(doc, pix, "z", item.pixel_size.z, {"unit":item.pixel_size_unit})
709            if written == True:
710                det.appendChild(pix)
711               
712            ori = doc.createElement("orientation")
713            written = write_node(doc, ori, "roll",  item.orientation.x, {"unit":item.orientation_unit})
714            written = written | write_node(doc, ori, "pitch", item.orientation.y, {"unit":item.orientation_unit})
715            written = written | write_node(doc, ori, "yaw",   item.orientation.z, {"unit":item.orientation_unit})
716            if written == True:
717                det.appendChild(ori)
718               
719        # Processes info
720        for item in datainfo.process:
721            node = doc.createElement("SASprocess")
722            entry_node.appendChild(node)
723
724            write_node(doc, node, "name", item.name)
725            write_node(doc, node, "date", item.date)
726            write_node(doc, node, "description", item.description)
727            for term in item.term:
728                value = term['value']
729                del term['value']
730                write_node(doc, node, "term", value, term)
731            for note in item.notes:
732                write_node(doc, node, "SASprocessnote", note)
733        # Return the document, and the SASentry node associated with
734        # the data we just wrote
735        return doc, entry_node
[e9b12eaf]736   
[61cada5]737    def _parse_state(self, entry):
738        """
[5062bbf]739        Read a fit result from an XML node
740       
741        :param entry: XML node to read from
742       
743        :return: PageState object
[61cada5]744        """
745        # Create an empty state
746        state = PageState()
747        # Locate the P(r) node
748        try:
749            nodes = entry.xpath('ns:%s' % FITTING_NODE_NAME, namespaces={'ns': CANSAS_NS})
750            state.fromXML(node=nodes[0])
751        except:
752            logging.info("XML document does not contain fitting information.\n %s" % sys.exc_value)
753           
754        return state
755   
[e9b12eaf]756    def _parse_entry_2d(self, dom):
757        """
[5062bbf]758        Parse a SASentry
759       
760        :param node: SASentry node
761       
762        :return: Data2D object
763       
[e9b12eaf]764        """
765        data_info = Data2D()
766       
767        # Look up title     
768        self._store_content('ns:Title', dom, 'title', data_info)
769       
770        # Look up run number   
771        nodes = dom.xpath('ns:Run', namespaces={'ns': CANSAS_NS})
772        for item in nodes:   
773            if item.text is not None:
774                value = item.text.strip()
775                if len(value) > 0:
776                    data_info.run.append(value)
777                    if item.get('name') is not None:
778                        data_info.run_name[value] = item.get('name')
779                           
780        # Look up instrument name             
781        self._store_content('ns:SASinstrument/ns:name', dom, 'instrument', data_info)
782
783        # Notes
784        note_list = dom.xpath('ns:SASnote', namespaces={'ns': CANSAS_NS})
785        for note in note_list:
786            try:
787                if note.text is not None:
788                    note_value = note.text.strip()
789                    if len(note_value) > 0:
790                        data_info.notes.append(note_value)
791            except:
792                err_mess = "cansas_reader.read: error processing entry notes\n  %s" % sys.exc_value
793                self.errors.append(err_mess)
794                logging.error(err_mess)
795       
796        # Sample info ###################
797        entry = get_content('ns:SASsample', dom)
798        if entry is not None:
799            data_info.sample.name = entry.get('name')
800           
801        self._store_content('ns:SASsample/ns:ID', 
802                     dom, 'ID', data_info.sample)                   
803        self._store_float('ns:SASsample/ns:thickness', 
804                     dom, 'thickness', data_info.sample)
805        self._store_float('ns:SASsample/ns:transmission', 
806                     dom, 'transmission', data_info.sample)
807        self._store_float('ns:SASsample/ns:temperature', 
808                     dom, 'temperature', data_info.sample)
809       
810        nodes = dom.xpath('ns:SASsample/ns:details', namespaces={'ns': CANSAS_NS})
811        for item in nodes:
812            try:
813                if item.text is not None:
814                    detail_value = item.text.strip()
815                    if len(detail_value) > 0:
816                        data_info.sample.details.append(detail_value)
817            except:
818                err_mess = "cansas_reader.read: error processing sample details\n  %s" % sys.exc_value
819                self.errors.append(err_mess)
820                logging.error(err_mess)
821       
822        # Position (as a vector)
823        self._store_float('ns:SASsample/ns:position/ns:x', 
824                     dom, 'position.x', data_info.sample)         
825        self._store_float('ns:SASsample/ns:position/ns:y', 
826                     dom, 'position.y', data_info.sample)         
827        self._store_float('ns:SASsample/ns:position/ns:z', 
828                     dom, 'position.z', data_info.sample)         
829       
830        # Orientation (as a vector)
831        self._store_float('ns:SASsample/ns:orientation/ns:roll', 
832                     dom, 'orientation.x', data_info.sample)         
833        self._store_float('ns:SASsample/ns:orientation/ns:pitch', 
834                     dom, 'orientation.y', data_info.sample)         
835        self._store_float('ns:SASsample/ns:orientation/ns:yaw', 
836                     dom, 'orientation.z', data_info.sample)         
837       
838        # Source info ###################
839        entry = get_content('ns:SASinstrument/ns:SASsource', dom)
840        if entry is not None:
841            data_info.source.name = entry.get('name')
842       
843        self._store_content('ns:SASinstrument/ns:SASsource/ns:radiation', 
844                     dom, 'radiation', data_info.source)                   
845        self._store_content('ns:SASinstrument/ns:SASsource/ns:beam_shape', 
846                     dom, 'beam_shape', data_info.source)                   
847        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength', 
848                     dom, 'wavelength', data_info.source)         
849        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_min', 
850                     dom, 'wavelength_min', data_info.source)         
851        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_max', 
852                     dom, 'wavelength_max', data_info.source)         
853        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_spread', 
854                     dom, 'wavelength_spread', data_info.source)   
855       
856        # Beam size (as a vector)   
857        entry = get_content('ns:SASinstrument/ns:SASsource/ns:beam_size', dom)
858        if entry is not None:
859            data_info.source.beam_size_name = entry.get('name')
860           
861        self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:x', 
862                     dom, 'beam_size.x', data_info.source)   
863        self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:y', 
864                     dom, 'beam_size.y', data_info.source)   
865        self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:z', 
866                     dom, 'beam_size.z', data_info.source)   
867       
868        # Collimation info ###################
869        nodes = dom.xpath('ns:SASinstrument/ns:SAScollimation', namespaces={'ns': CANSAS_NS})
870        for item in nodes:
871            collim = Collimation()
872            if item.get('name') is not None:
873                collim.name = item.get('name')
874            self._store_float('ns:length', item, 'length', collim) 
875           
876            # Look for apertures
877            apert_list = item.xpath('ns:aperture', namespaces={'ns': CANSAS_NS})
878            for apert in apert_list:
879                aperture =  Aperture()
880               
881                # Get the name and type of the aperture
882                aperture.name = apert.get('name')
883                aperture.type = apert.get('type')
884                   
885                self._store_float('ns:distance', apert, 'distance', aperture)   
886               
887                entry = get_content('ns:size', apert)
888                if entry is not None:
889                    aperture.size_name = entry.get('name')
890               
891                self._store_float('ns:size/ns:x', apert, 'size.x', aperture)   
892                self._store_float('ns:size/ns:y', apert, 'size.y', aperture)   
893                self._store_float('ns:size/ns:z', apert, 'size.z', aperture)
894               
895                collim.aperture.append(aperture)
896               
897            data_info.collimation.append(collim)
898       
899        # Detector info ######################
900        nodes = dom.xpath('ns:SASinstrument/ns:SASdetector', namespaces={'ns': CANSAS_NS})
901        for item in nodes:
902           
903            detector = Detector()
904           
905            self._store_content('ns:name', item, 'name', detector)
906            self._store_float('ns:SDD', item, 'distance', detector)   
907           
908            # Detector offset (as a vector)
909            self._store_float('ns:offset/ns:x', item, 'offset.x', detector)   
910            self._store_float('ns:offset/ns:y', item, 'offset.y', detector)   
911            self._store_float('ns:offset/ns:z', item, 'offset.z', detector)   
912           
913            # Detector orientation (as a vector)
914            self._store_float('ns:orientation/ns:roll',  item, 'orientation.x', detector)   
915            self._store_float('ns:orientation/ns:pitch', item, 'orientation.y', detector)   
916            self._store_float('ns:orientation/ns:yaw',   item, 'orientation.z', detector)   
917           
918            # Beam center (as a vector)
919            self._store_float('ns:beam_center/ns:x', item, 'beam_center.x', detector)   
920            self._store_float('ns:beam_center/ns:y', item, 'beam_center.y', detector)   
921            self._store_float('ns:beam_center/ns:z', item, 'beam_center.z', detector)   
922           
923            # Pixel size (as a vector)
924            self._store_float('ns:pixel_size/ns:x', item, 'pixel_size.x', detector)   
925            self._store_float('ns:pixel_size/ns:y', item, 'pixel_size.y', detector)   
926            self._store_float('ns:pixel_size/ns:z', item, 'pixel_size.z', detector)   
927           
928            self._store_float('ns:slit_length', item, 'slit_length', detector)
929           
930            data_info.detector.append(detector)   
931
932        # Processes info ######################
933        nodes = dom.xpath('ns:SASprocess', namespaces={'ns': CANSAS_NS})
934        for item in nodes:
935            process = Process()
936            self._store_content('ns:name', item, 'name', process)
937            self._store_content('ns:date', item, 'date', process)
938            self._store_content('ns:description', item, 'description', process)
939           
940            term_list = item.xpath('ns:term', namespaces={'ns': CANSAS_NS})
941            for term in term_list:
942                try:
943                    term_attr = {}
944                    for attr in term.keys():
945                        term_attr[attr] = term.get(attr).strip()
946                    if term.text is not None:
947                        term_attr['value'] = term.text.strip()
948                        process.term.append(term_attr)
949                except:
950                    err_mess = "cansas_reader.read: error processing process term\n  %s" % sys.exc_value
951                    self.errors.append(err_mess)
952                    logging.error(err_mess)
953           
954            note_list = item.xpath('ns:SASprocessnote', namespaces={'ns': CANSAS_NS})
955            for note in note_list:
956                if note.text is not None:
957                    process.notes.append(note.text.strip())
958           
959            data_info.process.append(process)
960           
961           
962        # Data info ######################
963        nodes = dom.xpath('ns:SASdata', namespaces={'ns': CANSAS_NS})
964        if len(nodes)>1:
965            raise RuntimeError, "CanSAS reader is not compatible with multiple SASdata entries"
966       
967        nodes = dom.xpath('ns:SASdata/ns:Idata', namespaces={'ns': CANSAS_NS})
968
969        for item in nodes:
970            _x, attr = get_float('ns:Q', item)
971            _dx, attr_d = get_float('ns:Qdev', item)
972            _dxl, attr_l = get_float('ns:dQl', item)
973            _dxw, attr_w = get_float('ns:dQw', item)
974            if _dx == None:
975                _dx = 0.0
976            if _dxl == None:
977                _dxl = 0.0
978            if _dxw == None:
979                _dxw = 0.0
980               
981            if attr.has_key('unit') and attr['unit'].lower() != data_info.x_unit.lower():
982                if has_converter==True:
983                    try:
984                        data_conv_q = Converter(attr['unit'])
985                        _x = data_conv_q(_x, units=data_info.x_unit)
986                    except:
987                        raise ValueError, "CanSAS reader: could not convert Q unit [%s]; expecting [%s]\n  %s" \
988                        % (attr['unit'], data_info.x_unit, sys.exc_value)
989                else:
990                    raise ValueError, "CanSAS reader: unrecognized Q unit [%s]; expecting [%s]" \
991                        % (attr['unit'], data_info.x_unit)
992            # Error in Q
993            if attr_d.has_key('unit') and attr_d['unit'].lower() != data_info.x_unit.lower():
994                if has_converter==True:
995                    try:
996                        data_conv_q = Converter(attr_d['unit'])
997                        _dx = data_conv_q(_dx, units=data_info.x_unit)
998                    except:
999                        raise ValueError, "CanSAS reader: could not convert dQ unit [%s]; expecting [%s]\n  %s" \
1000                        % (attr['unit'], data_info.x_unit, sys.exc_value)
1001                else:
1002                    raise ValueError, "CanSAS reader: unrecognized dQ unit [%s]; expecting [%s]" \
1003                        % (attr['unit'], data_info.x_unit)
1004            # Slit length
1005            if attr_l.has_key('unit') and attr_l['unit'].lower() != data_info.x_unit.lower():
1006                if has_converter==True:
1007                    try:
1008                        data_conv_q = Converter(attr_l['unit'])
1009                        _dxl = data_conv_q(_dxl, units=data_info.x_unit)
1010                    except:
1011                        raise ValueError, "CanSAS reader: could not convert dQl unit [%s]; expecting [%s]\n  %s" \
1012                        % (attr['unit'], data_info.x_unit, sys.exc_value)
1013                else:
1014                    raise ValueError, "CanSAS reader: unrecognized dQl unit [%s]; expecting [%s]" \
1015                        % (attr['unit'], data_info.x_unit)
1016            # Slit width
1017            if attr_w.has_key('unit') and attr_w['unit'].lower() != data_info.x_unit.lower():
1018                if has_converter==True:
1019                    try:
1020                        data_conv_q = Converter(attr_w['unit'])
1021                        _dxw = data_conv_q(_dxw, units=data_info.x_unit)
1022                    except:
1023                        raise ValueError, "CanSAS reader: could not convert dQw unit [%s]; expecting [%s]\n  %s" \
1024                        % (attr['unit'], data_info.x_unit, sys.exc_value)
1025                else:
1026                    raise ValueError, "CanSAS reader: unrecognized dQw unit [%s]; expecting [%s]" \
1027                        % (attr['unit'], data_info.x_unit)
1028                   
1029            _y, attr = get_float('ns:I', item)
1030            _dy, attr_d = get_float('ns:Idev', item)
1031            if _dy == None:
1032                _dy = 0.0
1033            if attr.has_key('unit') and attr['unit'].lower() != data_info.y_unit.lower():
1034                if has_converter==True:
1035                    try:
1036                        data_conv_i = Converter(attr['unit'])
1037                        _y = data_conv_i(_y, units=data_info.y_unit)
1038                    except:
1039                        raise ValueError, "CanSAS reader: could not convert I(q) unit [%s]; expecting [%s]\n  %s" \
1040                        % (attr['unit'], data_info.y_unit, sys.exc_value)
1041                else:
1042                    raise ValueError, "CanSAS reader: unrecognized I(q) unit [%s]; expecting [%s]" \
1043                        % (attr['unit'], data_info.y_unit)
1044            if attr_d.has_key('unit') and attr_d['unit'].lower() != data_info.y_unit.lower():
1045                if has_converter==True:
1046                    try:
1047                        data_conv_i = Converter(attr_d['unit'])
1048                        _dy = data_conv_i(_dy, units=data_info.y_unit)
1049                    except:
1050                        raise ValueError, "CanSAS reader: could not convert dI(q) unit [%s]; expecting [%s]\n  %s" \
1051                        % (attr_d['unit'], data_info.y_unit, sys.exc_value)
1052                else:
1053                    raise ValueError, "CanSAS reader: unrecognized dI(q) unit [%s]; expecting [%s]" \
1054                        % (attr_d['unit'], data_info.y_unit)
1055               
1056            if _x is not None and _y is not None:
1057                exec "item = numpy.append(x, _x)"
1058   
1059        for item in list_of_model_attributes:
1060            node = get_content("ns:%s"%item[0], entry)
1061            list = []
1062            for value in node:
1063                try:
1064                    list.append(float(value)) 
1065                except:
1066                    list.append(None)
1067               
1068            exec "self.%s = list"%item[1]
1069        data_conv_q = None
1070        data_conv_i = None
1071       
1072        if has_converter == True and data_info.x_unit != '1/A':
1073            data_conv_q = Converter('1/A')
1074            # Test it
1075            data_conv_q(1.0, output.Q_unit)
1076           
1077        if has_converter == True and data_info.y_unit != '1/cm':
1078            data_conv_i = Converter('1/cm')
1079            # Test it
1080            data_conv_i(1.0, output.I_unit)                   
1081               
1082        if data_conv_q is not None:
1083            data_info.xaxis("\\rm{%s}"%str(_xaxis), data_info.x_unit)
1084        else:
1085            data_info.xaxis("\\rm{%s}"%str(_xaxis), 'A^{-1}')
1086        if data_conv_i is not None:
1087            data_info.yaxis("\\rm{%s}"%str(_yaxis), data_info.y_unit)
1088        else:
1089            data_info.yaxis("\\rm{%s}"%str(_yaxis),"cm^{-1}")
1090       
1091        return data_info
1092
[61cada5]1093    def _read_cansas(self, path):
1094        """
[5062bbf]1095        Load data and P(r) information from a CanSAS XML file.
1096       
1097        :param path: file path
1098       
1099        :return: Data1D object if a single SASentry was found,
1100                    or a list of Data1D objects if multiple entries were found,
1101                    or None of nothing was found
1102                   
1103        :raise RuntimeError: when the file can't be opened
1104        :raise ValueError: when the length of the data vectors are inconsistent
1105       
[61cada5]1106        """
1107        output = []
1108        try:
1109            if os.path.isfile(path):
1110                basename  = os.path.basename(path)
1111                root, extension = os.path.splitext(basename)
1112                #TODO: eventually remove the check for .xml once
1113                # the P(r) writer/reader is truly complete.
1114                if  extension.lower() in self.ext or \
1115                    extension.lower() == '.xml':
1116                   
1117                    tree = etree.parse(path, parser=etree.ETCompatXMLParser())
1118                    # Check the format version number
1119                    # Specifying the namespace will take care of the file format version
1120                    root = tree.getroot()
1121                    entry_list = root.xpath('/ns:SASroot/ns:SASentry', namespaces={'ns': CANSAS_NS})
1122                    for entry in entry_list:
[e9b12eaf]1123                        try:
1124                            sas_entry = self._parse_entry(entry)
1125                        except:
1126                            sas_entry = self._parse_entry_2d(entry)
[61cada5]1127                        fitstate = self._parse_state(entry)
1128                        sas_entry.meta_data['fitstate'] = fitstate
1129                        sas_entry.filename = fitstate.file
1130                        output.append(sas_entry)
1131                   
1132            else:
1133                raise RuntimeError, "%s is not a file" % path
1134           
1135            # Return output consistent with the loader's api
1136            if len(output)==0:
1137                return None
1138            elif len(output)==1:
1139                # Call back to post the new state
1140                state = output[0].meta_data['fitstate']
[11a7e11]1141                t = time.localtime(state.timestamp)
1142                time_str = time.strftime("%b %d %H:%M", t)
1143                # Check that no time stamp is already appended
1144                max_char = state.file.find("[")
1145                if max_char < 0:
1146                    max_char = len(state.file)
1147                state.file = state.file[0:max_char] +' [' + time_str + ']'
1148               
1149                   
[61cada5]1150                if state is not None and state.is_data is not None:
1151                    exec 'output[0].is_data = state.is_data' 
1152                 
[11a7e11]1153                output[0].filename = state.file
1154                #output[0].filename = state.data_name
[61cada5]1155                state.data = output[0]
1156                state.data.name = state.data_name
1157                state.data.id = state.data_id
[11a7e11]1158                #state.data.group_id = state.data_group_id
1159                state.data.group_id = output[0].filename
1160             
[61cada5]1161                self.call_back(state=state, datainfo=output[0])
1162                return output[0]
1163            else:
1164                return output               
1165        except:
1166            raise
1167           
1168    def write(self, filename, datainfo=None, fitstate=None):
1169        """
[5062bbf]1170        Write the content of a Data1D as a CanSAS XML file
1171       
1172        :param filename: name of the file to write
1173        :param datainfo: Data1D object
1174        :param fitstate: PageState object
1175       
[61cada5]1176        """
1177        # Sanity check
1178        if self.cansas == True:
1179           
1180            # Add fitting information to the XML document
1181            if fitstate is not None:
1182                if fitstate.data is None:
1183                    data = DataLoader.data_info.Data1D(x=[], y=[])   
1184                elif issubclass(fitstate.data.__class__, DataLoader.data_info.Data1D):
1185                    data = fitstate.data
1186                    doc, sasentry = self._to_xml_doc(data)
1187                else:
1188                    data = fitstate.data
1189                    doc, sasentry = self._data2d_to_xml_doc(data)
[e9b12eaf]1190                fitstate.toXML(doc=doc, file=data.name, entry_node=sasentry)
[61cada5]1191            # Write the XML document
1192            fd = open(filename, 'w')
1193            fd.write(doc.toprettyxml())
1194            fd.close()
1195        else:
1196            fitstate.toXML(file=filename)
1197       
[5062bbf]1198"""     
1199Example: ::
1200 
1201    if __name__ == "__main__":
1202        state = PageState(parent=None)
1203        state.toXML()
1204        print "state", state
1205   
1206"""
Note: See TracBrowser for help on using the repository browser.