source: sasview/sansview/perspectives/fitting/pagestate.py @ 232c3db

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 232c3db was 11a7e11, checked in by Gervaise Alina <gervyh@…>, 15 years ago

working on save state

  • Property mode set to 100644
File size: 36.7 KB
Line 
1"""
2This software was developed by the University of Tennessee as part of the
3Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
4project funded by the US National Science Foundation.
5
6See the license text in license.txt
7
8copyright 2009, University of Tennessee
9"""
10import time
11import os
12import sys
13import copy
14import logging
15
16from xml.dom.minidom import parse
17from lxml import etree
18
19import DataLoader
20from DataLoader.readers.cansas_reader import Reader as CansasReader
21from DataLoader.readers.cansas_reader import get_content
22
23#Information to read/write state as xml
24FITTING_NODE_NAME = 'fitting_plug_in'
25CANSAS_NS = "cansas1d/1.0"
26
27list_of_data_attributes = [["is_data", "is_data", "bool"],
28                      ["group_id", "data_group_id", "string"],
29                      ["data_name", "data_name", "string"],
30                      ["data_id", "data_id", "string"],
31                      ["name", "name", "string"],
32                      ["data_name", "data_name", "string"]]
33list_of_state_attributes = [["qmin", "qmin", "float"],
34                      ["qmax", "qmax", "float"],
35                      ["npts", "npts", "float"],
36                      ["shape_rbutton", "shape_rbutton", "bool"],
37                      ["shape_indep_rbutton", "shape_indep_rbutton", "bool"],
38                      ["plugin_rbutton", "plugin_rbutton","bool"],
39                      ["struct_rbutton", "struct_rbutton", "bool"],
40                      ["formfactorcombobox", "formfactorcombobox", "bool"],
41                      ["structurecombobox", "structurecombobox", "bool"],
42                      ["disp_box", "disp_box"],
43                      ["enable_smearer","enable_smearer","bool"],
44                      ["disable_smearer","disable_smearer","bool"],
45                      ["pinhole_smearer","pinhole_smearer","bool"],
46                      ["slit_smearer","slit_smearer","bool"],
47                      ["enable_disp","enable_disp","bool"],
48                      ["disable_disp","disable_disp","bool"],
49                      ["slit_smearer","slit_smearer","bool"],
50                      ["enable2D","enable2D","bool"],
51                      ["cb1","cb1","bool"],
52                      ["tcChi","tcChi","float"],
53                     ["smearer", "smearer", "float"],
54                     ["smear_type","smear_type", "string"],
55                     ["dq_l", "dq_l", "string"],
56                     ["dq_r","dq_r", "string"]]
57
58list_of_model_attributes = [["values", "values"],
59                            ["weights", "weights"]]
60
61list_of_state_parameters = [["parameters", "parameters"] ,                     
62                            ["orientation_parameters", "orientation_params"],
63                            ["dispersity_parameters", "orientation_params_disp"],
64                            ["fixed_param", "fixed_param"],                     
65                            ["fittable_param","fittable_param"]]
66
67class PageState(object):
68    """
69        Contains information to reconstruct a page of the fitpanel.
70    """
71    def __init__(self, parent=None, model=None, data=None):
72       
73        """
74            Initialize the current state
75            @param model: a selected model within a page
76            @param data:
77        """
78        self.file = None
79        #Time of state creation
80        self.timestamp = time.time()
81        ## Data member to store the dispersion object created
82        self._disp_obj_dict = {}
83        #------------------------
84        #Data used for fitting
85        self.data = data
86        #save additional information on data that dataloader.reader does not read
87        self.is_data = None
88        self.data_name = ""
89       
90        if self.data is not None:
91            self.data_name = self.data.name
92        self.data_id = None
93        if self.data is not None and hasattr(self.data, "id"):
94            self.data_id = self.data.id
95        self.data_group_id = None
96        if self.data is not None and hasattr(self.data, "group_id"):
97            self.data_group_id = self.data.group_id
98        #-------------------------
99        ## reset True change the state of exsiting button
100        self.reset = False
101       
102        #engine type
103        self.engine_type = None
104        # flag to allow data2D plot
105        self.enable2D = False
106        # model on which the fit would be performed
107        self.model = model
108       
109        #fit page manager
110        self.manager = None
111        #Store the parent of this panel parent
112        # For this application fitpanel is the parent
113        self.parent  = parent
114        # Event_owner is the owner of model event
115        self.event_owner = None
116        ##page name
117        self.page_name = ""
118        # Contains link between  model ,all its parameters, and panel organization
119        self.parameters = []
120        # Contains list of parameters that cannot be fitted and reference to
121        #panel objects
122        self.fixed_param = []
123        # Contains list of parameters with dispersity and reference to
124        #panel objects
125        self.fittable_param = []
126        ## orientation parameters
127        self.orientation_params = []
128        ## orientation parmaters for gaussian dispersity
129        self.orientation_params_disp = []
130        ## smearer info
131        self.smearer = None
132        self.smear_type = None
133        self.dq_l = None
134        self.dq_r = None
135
136        #list of dispersion paramaters
137        self.disp_list =[]
138        if self.model is not None:
139            self.disp_list = self.model.getDispParamList()
140        self._disp_obj_dict = {}
141        self.disp_cb_dict = {}
142        self.values = []
143        self.weights = []
144                   
145        #contains link between a model and selected parameters to fit
146        self.param_toFit = []
147        ##dictionary of model type and model class
148        self.model_list_box = None
149        ## save the state of the context menu
150        self.saved_states = {}
151        ## save selection of combobox
152        self.formfactorcombobox = None
153        self.structurecombobox  = None
154        ## radio box to select type of model
155        self.shape_rbutton = False
156        self.shape_indep_rbutton = False
157        self.struct_rbutton = False
158        self.plugin_rbutton = False
159        ## the indice of the current selection
160        self.disp_box = 0
161        ## Qrange
162        ## Q range
163        self.qmin = 0.001
164        self.qmax = 0.1
165        self.npts = None
166        self.name = ""
167        ## enable smearering state
168        self.enable_smearer = False
169        self.disable_smearer = True
170        self.pinhole_smearer = False
171        self.slit_smearer   = False
172        ## disperity selection
173        self.enable_disp = False
174        self.disable_disp = True
175       
176        ## state of selected all check button
177        self.cb1 = False
178        ## store value of chisqr
179        self.tcChi = None
180   
181    def clone(self):
182        """
183            Create a new copy of the current object
184        """
185        model = None
186        if self.model is not None:
187            model = self.model.clone()
188       
189        obj = PageState(self.parent, model=model)
190        obj.file = copy.deepcopy(self.file)
191        obj.data = copy.deepcopy(self.data)
192        if self.data is not None:
193            self.data_name = self.data.name
194        obj.data_name = self.data_name
195        obj.is_data = self.is_data
196        obj.model_list_box = copy.deepcopy(self.model_list_box)
197        obj.engine_type = copy.deepcopy(self.engine_type)
198       
199        obj.formfactorcombobox = self.formfactorcombobox
200        obj.structurecombobox  = self.structurecombobox 
201       
202        obj.shape_rbutton = self.shape_rbutton
203        obj.shape_indep_rbutton = self.shape_indep_rbutton
204        obj.struct_rbutton = self.struct_rbutton
205        obj.plugin_rbutton = self.plugin_rbutton
206       
207        obj.manager = self.manager
208        obj.event_owner = self.event_owner
209        obj.disp_list = copy.deepcopy(self.disp_list)
210       
211        obj.enable2D = copy.deepcopy(self.enable2D)
212        obj.parameters = copy.deepcopy(self.parameters)
213        obj.fixed_param = copy.deepcopy(self.fixed_param)
214        obj.fittable_param = copy.deepcopy(self.fittable_param)
215        obj.orientation_params =  copy.deepcopy(self.orientation_params)
216        obj.orientation_params_disp =  copy.deepcopy(self.orientation_params_disp)
217        obj.enable_disp = copy.deepcopy(self.enable_disp)
218        obj.disable_disp = copy.deepcopy(self.disable_disp)
219        obj.tcChi = self.tcChi
220 
221        if len(self._disp_obj_dict)>0:
222            for k , v in self._disp_obj_dict.iteritems():
223                obj._disp_obj_dict[k]= v
224        if len(self.disp_cb_dict)>0:
225            for k , v in self.disp_cb_dict.iteritems():
226                obj.disp_cb_dict[k]= v
227               
228        obj.values = copy.deepcopy(self.values)
229        obj.weights = copy.deepcopy(self.weights)
230        obj.enable_smearer = copy.deepcopy(self.enable_smearer)
231        obj.disable_smearer = copy.deepcopy(self.disable_smearer)
232        obj.pinhole_smearer = copy.deepcopy(self.pinhole_smearer)
233        obj.slit_smearer = copy.deepcopy(self.slit_smearer)
234        obj.smear_type = copy.deepcopy(self.smear_type)
235        obj.dq_l = copy.deepcopy(self.dq_l)
236        obj.dq_r = copy.deepcopy(self.dq_r)
237
238        obj.disp_box = copy.deepcopy(self.disp_box)
239        obj.qmin = copy.deepcopy(self.qmin)
240        obj.qmax = copy.deepcopy(self.qmax)
241        obj.npts = copy.deepcopy(self.npts )
242        obj.cb1 = copy.deepcopy(self.cb1)
243        obj.smearer = copy.deepcopy(self.smearer)
244       
245        for name, state in self.saved_states.iteritems():
246            copy_name = copy.deepcopy(name)
247            copy_state = state.clone()
248            obj.saved_states[copy_name]= copy_state
249        return obj
250   
251    def _repr_helper(self, list, rep):
252        """
253            Helper method to print a state
254        """
255        for item in list:
256            rep += "parameter name: %s \n"%str(item[1])
257            rep += "value: %s\n"%str(item[2])
258            rep += "selected: %s\n"%str(item[0])
259            rep += "error displayed : %s \n"%str(item[4][0])
260            rep += "error value:%s \n"%str(item[4][1])
261            rep += "minimum displayed : %s \n"%str(item[5][0])
262            rep += "minimum value : %s \n"%str(item[5][1])
263            rep += "maximum displayed : %s \n"%str(item[6][0])
264            rep += "maximum value : %s \n"%str(item[6][1])
265            rep += "parameter unit: %s\n\n"%str(item[7])
266        return rep
267   
268    def __repr__(self):
269        """
270            output string for printing
271        """
272        rep = "\nState name: %s\n"%self.file
273        t = time.localtime(self.timestamp)
274        time_str = time.strftime("%b %d %H:%M", t)
275        rep += "State created on : %s\n"%time_str
276        rep += "State form factor combobox selection: %s\n"%self.formfactorcombobox
277        rep += "State structure factor combobox selection: %s\n"%self.structurecombobox
278        rep += "is data : %s\n"%self.is_data
279        rep += "data's name : %s\n"%self.data_name
280        rep += "data's id : %s\n"%self.data_id
281        rep += "model type (form factor) selected: %s\n"%self.shape_rbutton
282        rep += "model type (shape independent) selected: %s\n"%self.shape_indep_rbutton
283        rep += "model type (structure factor) selected: %s\n"%self.struct_rbutton
284        rep += "model type (plug-in ) selected: %s\n"%self.plugin_rbutton
285        rep += "data : %s\n"% str(self.data)
286        rep += "Plotting Range: min: %s, max: %s, steps: %s\n"%(str(self.qmin),
287                                                str(self.qmax),str(self.npts))
288        """
289        rep += "model  : %s\n\n"% str(self.model)
290        rep += "number parameters(self.parameters): %s\n"%len(self.parameters)
291        rep += self._repr_helper( list=self.parameters, rep=rep)
292        rep += "number orientation parameters"
293        rep += "(self.orientation_params): %s\n"%len(self.orientation_params)
294        rep += self._repr_helper( list=self.orientation_params, rep=rep)
295        rep += "number dispersity parameters"
296        rep += "(self.orientation_params_disp): %s\n"%len(self.orientation_params_disp)
297        rep += self._repr_helper( list=self.orientation_params_disp, rep=rep)
298        """
299        return rep
300   
301    def _toXML_helper(self, list, element, newdoc):
302        """
303            Helper method to create xml file for saving state
304        """
305        for item in list:
306            sub_element = newdoc.createElement('parameter')
307            sub_element.setAttribute('name', str(item[1]))
308            sub_element.setAttribute('value', str(item[2]))
309            sub_element.setAttribute('selected_to_fit', str(item[0]))
310            sub_element.setAttribute('error_displayed', str(item[4][0]))
311            sub_element.setAttribute('error_value', str(item[4][1]))
312            sub_element.setAttribute('minimum_displayed', str(item[5][0]))
313            sub_element.setAttribute('minimum_value', str(item[5][1]))
314            sub_element.setAttribute('maximum_displayed', str(item[6][0]))
315            sub_element.setAttribute('maximum_value', str(item[6][1]))
316            sub_element.setAttribute('unit', str(item[7]))
317            element.appendChild(sub_element)
318       
319    def toXML(self, file="fitting_state.fitv", doc=None, entry_node=None):
320        """
321            Writes the state of the InversionControl panel to file, as XML.
322           
323            Compatible with standalone writing, or appending to an
324            already existing XML document. In that case, the XML document
325            is required. An optional entry node in the XML document may also be given.
326           
327            @param file: file to write to
328            @param doc: XML document object [optional]
329            @param entry_node: XML node within the XML document at which we will append the data [optional]
330        """
331        from xml.dom.minidom import getDOMImplementation
332
333        # Check whether we have to write a standalone XML file
334        if doc is None:
335            impl = getDOMImplementation()
336            doc_type = impl.createDocumentType(FITTING_NODE_NAME, "1.0", "1.0")     
337            newdoc = impl.createDocument(None, FITTING_NODE_NAME, doc_type)
338            top_element = newdoc.documentElement
339        else:
340            # We are appending to an existing document
341            newdoc = doc
342            top_element = newdoc.createElement(FITTING_NODE_NAME)
343            if entry_node is None:
344                newdoc.documentElement.appendChild(top_element)
345            else:
346                entry_node.appendChild(top_element)
347           
348        attr = newdoc.createAttribute("version")
349        attr.nodeValue = '1.0'
350        top_element.setAttributeNode(attr)
351       
352        # File name
353        element = newdoc.createElement("filename")
354        if self.file is not None:
355            element.appendChild(newdoc.createTextNode(str(self.file)))
356        else:
357            element.appendChild(newdoc.createTextNode(str(file)))
358        top_element.appendChild(element)
359       
360        element = newdoc.createElement("timestamp")
361        element.appendChild(newdoc.createTextNode(time.ctime(self.timestamp)))
362        attr = newdoc.createAttribute("epoch")
363        attr.nodeValue = str(self.timestamp)
364        element.setAttributeNode(attr)
365        top_element.appendChild(element)
366        # Inputs
367        inputs = newdoc.createElement("Attributes")
368        top_element.appendChild(inputs)
369       
370        element = newdoc.createElement('data_attributes')
371        if self.data is not None and hasattr(self.data, "group_id"):
372            self.data_group_id = self.data.group_id
373        if self.data is not None and hasattr(self.data, "is_data"):
374            self.is_data = self.data.is_data
375        if self.data is not None:
376            self.data_name = self.data.name
377        if self.data is not None and hasattr(self.data, "id"):
378            self.data_id = self.data.id
379       
380        for item in list_of_data_attributes:
381            exec "element.setAttribute(item[0], str(self.%s))"%(item[1])
382        inputs.appendChild(element)   
383       
384        for item in list_of_state_attributes:
385            element = newdoc.createElement(item[0])
386            exec "element.setAttribute(item[0], str(self.%s))"%(item[1])
387            inputs.appendChild(element)
388           
389        for item in list_of_model_attributes:
390            element = newdoc.createElement(item[0])
391            exec "list = self.%s"%item[1]
392            for item in list:
393                exec "element.appendChild(newdoc.createTextNode(str(%s)))" % item
394            inputs.appendChild(element)
395           
396        for item in list_of_state_parameters:
397            element = newdoc.createElement(item[0])
398            exec "self._toXML_helper(list=self.%s, element=element, newdoc=newdoc)"%item[1]                       
399            inputs.appendChild(element)
400       
401        # Save the file
402        if doc is None:
403            fd = open(file, 'w')
404            fd.write(newdoc.toprettyxml())
405            fd.close()
406            return None
407        else:
408            return newdoc.toprettyxml()
409       
410    def _fromXML_helper(self, node, list):
411        """
412            Helper function to write state to xml
413        """
414        for item in node:
415            name = item.get('name')
416            value = item.get('value')
417            selected_to_fit = item.get('selected_to_fit')
418            error_displayed = item.get('error_displayed')
419            error_value = item.get('error_value')
420            minimum_displayed = item.get('minimum_displayed')
421            minimum_value = item.get('minimum_value')
422            maximum_displayed = item.get('maximum_displayed')
423            maximum_value = item.get('maximum_value')
424            unit = item.get('unit')
425            list.append([selected_to_fit, name, value, "+/-",[error_displayed, error_value],
426                         [minimum_displayed,minimum_value],[maximum_displayed,maximum_value], unit])
427       
428    def fromXML(self, file=None, node=None):
429        """
430            Load fitting state from a file
431           
432            @param file: .fitv file
433            @param node: node of a XML document to read from
434        """
435     
436        if file is not None:
437            msg = "PageState no longer supports non-CanSAS"
438            msg += " format for fitting files"
439            raise RuntimeError, msg
440           
441        if node.get('version')and node.get('version') == '1.0':
442           
443            # Get file name
444            entry = get_content('ns:filename', node)
445            if entry is not None:
446                self.file = entry.text.strip()
447               
448            # Get time stamp
449            entry = get_content('ns:timestamp', node)
450            if entry is not None and entry.get('epoch'):
451                try:
452                    self.timestamp = float(entry.get('epoch'))
453                except:
454                    msg = "PageState.fromXML: Could not"
455                    msg += " read timestamp\n %s" % sys.exc_value
456                    logging.error(msg)
457           
458            # Parse fitting attributes
459            entry = get_content('ns:Attributes', node)
460            if entry is not None:
461                for item in list_of_state_attributes:
462                    field = get_content('ns:%s'%item[0], entry)
463                    if field is not None:
464                        if item[2] == "string":
465                            exec "self.%s= str(field.text)"%item[1]
466                        elif item[2] == "bool":
467                            try:
468                                exec "self.%s= field.get(str(%s))"%(item[1], item[0])
469                            except:
470                                exec "self.%s = None"%item[1]
471                        else:
472                            try:
473                                exec "self.%s = float(field.get(%s))"%(item[1], item[0])
474                            except:
475                                exec "self.%s = None"%item[1]
476                for item in list_of_model_attributes:
477                    node = get_content("ns:%s"%item[0], entry)
478                    list = []
479                    for value in node:
480                        try:
481                            list.append(float(value)) 
482                        except:
483                            list.append(None)
484                       
485                    exec "self.%s = list"%item[1]
486                   
487                for item in list_of_state_parameters:
488                    node = get_content("ns:%s"%item[0], entry)
489                    self._fromXML_helper(node=node, list=self.parameters)
490               
491
492class Reader(CansasReader):
493    """
494        Class to load a .fitv fitting file
495    """
496    ## File type
497    type_name = "Fitting"
498   
499    ## Wildcards
500    type = ["Fitting files (*.fitv)|*.fitv"]
501    ## List of allowed extensions
502    ext=['.fitv', '.FITV'] 
503   
504    def __init__(self, call_back=None, cansas=True):
505        """
506            Initialize the call-back method to be called
507            after we load a file
508            @param call_back: call-back method
509            @param cansas:  True = files will be written/read in CanSAS format
510                            False = write CanSAS format
511           
512        """
513        ## Call back method to be executed after a file is read
514        self.call_back = call_back
515        ## CanSAS format flag
516        self.cansas = cansas
517       
518    def read(self, path):
519        """
520            Load a new P(r) inversion state from file
521           
522            @param path: file path
523            @return: None
524        """
525        if self.cansas==True:
526            return self._read_cansas(path)
527        else:
528            return self._read_standalone(path)
529       
530    def _data2d_to_xml_doc(self):
531        """
532            Create an XML document to contain the content of a Data2D
533           
534            @param datainfo: Data2D object
535        """
536        if not issubclass(datainfo.__class__, Data2D):
537            raise RuntimeError, "The cansas writer expects a Data2D instance"
538       
539        doc = xml.dom.minidom.Document()
540        main_node = doc.createElement("SASroot")
541        main_node.setAttribute("version", self.version)
542     
543        doc.appendChild(main_node)
544       
545        entry_node = doc.createElement("SASentry")
546        main_node.appendChild(entry_node)
547       
548        write_node(doc, entry_node, "Title", datainfo.title)
549       
550        for item in datainfo.run:
551            runname = {}
552            if datainfo.run_name.has_key(item) and len(str(datainfo.run_name[item]))>1:
553                runname = {'name': datainfo.run_name[item] }
554            write_node(doc, entry_node, "Run", item, runname)
555       
556        # Data info
557        node = doc.createElement("SASdata")
558        entry_node.appendChild(node)
559       
560        for i in range(len(datainfo.x)):
561            pt = doc.createElement("Idata")
562            node.appendChild(pt)
563            write_node(doc, pt, "Q", datainfo.x[i], {'unit':datainfo.x_unit})
564            if len(datainfo.y)>=i:
565                write_node(doc, pt, "I", datainfo.y[i], {'unit':datainfo.y_unit})
566            if datainfo.dx !=None and len(datainfo.dx)>=i:
567                write_node(doc, pt, "Qdev", datainfo.dx[i], {'unit':datainfo.x_unit})
568            if datainfo.dy !=None and len(datainfo.dy)>=i:
569                write_node(doc, pt, "Idev", datainfo.dy[i], {'unit':datainfo.y_unit})
570
571       
572        # Sample info
573        sample = doc.createElement("SASsample")
574        if datainfo.sample.name is not None:
575            sample.setAttribute("name", str(datainfo.sample.name))
576        entry_node.appendChild(sample)
577        write_node(doc, sample, "ID", str(datainfo.sample.ID))
578        write_node(doc, sample, "thickness", datainfo.sample.thickness, {"unit":datainfo.sample.thickness_unit})
579        write_node(doc, sample, "transmission", datainfo.sample.transmission)
580        write_node(doc, sample, "temperature", datainfo.sample.temperature, {"unit":datainfo.sample.temperature_unit})
581       
582        for item in datainfo.sample.details:
583            write_node(doc, sample, "details", item)
584       
585        pos = doc.createElement("position")
586        written = write_node(doc, pos, "x", datainfo.sample.position.x, {"unit":datainfo.sample.position_unit})
587        written = written | write_node(doc, pos, "y", datainfo.sample.position.y, {"unit":datainfo.sample.position_unit})
588        written = written | write_node(doc, pos, "z", datainfo.sample.position.z, {"unit":datainfo.sample.position_unit})
589        if written == True:
590            sample.appendChild(pos)
591       
592        ori = doc.createElement("orientation")
593        written = write_node(doc, ori, "roll",  datainfo.sample.orientation.x, {"unit":datainfo.sample.orientation_unit})
594        written = written | write_node(doc, ori, "pitch", datainfo.sample.orientation.y, {"unit":datainfo.sample.orientation_unit})
595        written = written | write_node(doc, ori, "yaw",   datainfo.sample.orientation.z, {"unit":datainfo.sample.orientation_unit})
596        if written == True:
597            sample.appendChild(ori)
598       
599        # Instrument info
600        instr = doc.createElement("SASinstrument")
601        entry_node.appendChild(instr)
602       
603        write_node(doc, instr, "name", datainfo.instrument)
604       
605        #   Source
606        source = doc.createElement("SASsource")
607        if datainfo.source.name is not None:
608            source.setAttribute("name", str(datainfo.source.name))
609        instr.appendChild(source)
610       
611        write_node(doc, source, "radiation", datainfo.source.radiation)
612        write_node(doc, source, "beam_shape", datainfo.source.beam_shape)
613        size = doc.createElement("beam_size")
614        if datainfo.source.beam_size_name is not None:
615            size.setAttribute("name", str(datainfo.source.beam_size_name))
616        written = write_node(doc, size, "x", datainfo.source.beam_size.x, {"unit":datainfo.source.beam_size_unit})
617        written = written | write_node(doc, size, "y", datainfo.source.beam_size.y, {"unit":datainfo.source.beam_size_unit})
618        written = written | write_node(doc, size, "z", datainfo.source.beam_size.z, {"unit":datainfo.source.beam_size_unit})
619        if written == True:
620            source.appendChild(size)
621           
622        write_node(doc, source, "wavelength", datainfo.source.wavelength, {"unit":datainfo.source.wavelength_unit})
623        write_node(doc, source, "wavelength_min", datainfo.source.wavelength_min, {"unit":datainfo.source.wavelength_min_unit})
624        write_node(doc, source, "wavelength_max", datainfo.source.wavelength_max, {"unit":datainfo.source.wavelength_max_unit})
625        write_node(doc, source, "wavelength_spread", datainfo.source.wavelength_spread, {"unit":datainfo.source.wavelength_spread_unit})
626       
627        #   Collimation
628        for item in datainfo.collimation:
629            coll = doc.createElement("SAScollimation")
630            if item.name is not None:
631                coll.setAttribute("name", str(item.name))
632            instr.appendChild(coll)
633           
634            write_node(doc, coll, "length", item.length, {"unit":item.length_unit})
635           
636            for apert in item.aperture:
637                ap = doc.createElement("aperture")
638                if apert.name is not None:
639                    ap.setAttribute("name", str(apert.name))
640                if apert.type is not None:
641                    ap.setAttribute("type", str(apert.type))
642                coll.appendChild(ap)
643               
644                write_node(doc, ap, "distance", apert.distance, {"unit":apert.distance_unit})
645               
646                size = doc.createElement("size")
647                if apert.size_name is not None:
648                    size.setAttribute("name", str(apert.size_name))
649                written = write_node(doc, size, "x", apert.size.x, {"unit":apert.size_unit})
650                written = written | write_node(doc, size, "y", apert.size.y, {"unit":apert.size_unit})
651                written = written | write_node(doc, size, "z", apert.size.z, {"unit":apert.size_unit})
652                if written == True:
653                    ap.appendChild(size)
654
655        #   Detectors
656        for item in datainfo.detector:
657            det = doc.createElement("SASdetector")
658            written = write_node(doc, det, "name", item.name)
659            written = written | write_node(doc, det, "SDD", item.distance, {"unit":item.distance_unit})
660            written = written | write_node(doc, det, "slit_length", item.slit_length, {"unit":item.slit_length_unit})
661            if written == True:
662                instr.appendChild(det)
663           
664            off = doc.createElement("offset")
665            written = write_node(doc, off, "x", item.offset.x, {"unit":item.offset_unit})
666            written = written | write_node(doc, off, "y", item.offset.y, {"unit":item.offset_unit})
667            written = written | write_node(doc, off, "z", item.offset.z, {"unit":item.offset_unit})
668            if written == True:
669                det.appendChild(off)
670           
671            center = doc.createElement("beam_center")
672            written = write_node(doc, center, "x", item.beam_center.x, {"unit":item.beam_center_unit})
673            written = written | write_node(doc, center, "y", item.beam_center.y, {"unit":item.beam_center_unit})
674            written = written | write_node(doc, center, "z", item.beam_center.z, {"unit":item.beam_center_unit})
675            if written == True:
676                det.appendChild(center)
677               
678            pix = doc.createElement("pixel_size")
679            written = write_node(doc, pix, "x", item.pixel_size.x, {"unit":item.pixel_size_unit})
680            written = written | write_node(doc, pix, "y", item.pixel_size.y, {"unit":item.pixel_size_unit})
681            written = written | write_node(doc, pix, "z", item.pixel_size.z, {"unit":item.pixel_size_unit})
682            if written == True:
683                det.appendChild(pix)
684               
685            ori = doc.createElement("orientation")
686            written = write_node(doc, ori, "roll",  item.orientation.x, {"unit":item.orientation_unit})
687            written = written | write_node(doc, ori, "pitch", item.orientation.y, {"unit":item.orientation_unit})
688            written = written | write_node(doc, ori, "yaw",   item.orientation.z, {"unit":item.orientation_unit})
689            if written == True:
690                det.appendChild(ori)
691               
692       
693        # Processes info
694        for item in datainfo.process:
695            node = doc.createElement("SASprocess")
696            entry_node.appendChild(node)
697
698            write_node(doc, node, "name", item.name)
699            write_node(doc, node, "date", item.date)
700            write_node(doc, node, "description", item.description)
701            for term in item.term:
702                value = term['value']
703                del term['value']
704                write_node(doc, node, "term", value, term)
705            for note in item.notes:
706                write_node(doc, node, "SASprocessnote", note)
707       
708        # Return the document, and the SASentry node associated with
709        # the data we just wrote
710        return doc, entry_node
711   
712    def _read_standalone(self, path):
713        """
714            Load a new P(r) inversion state from file.
715            The P(r) node is assumed to be the top element.
716           
717            @param path: file path
718            @return: None
719        """
720        # Read the new state from file
721        state = PageState()
722        state.fromXML(file=path)
723        # Call back to post the new state
724        self.call_back(state)
725        return None
726   
727    def _parse_state(self, entry):
728        """
729            Read a fit result from an XML node
730            @param entry: XML node to read from
731            @return: PageState object
732        """
733        # Create an empty state
734        state = PageState()
735        # Locate the P(r) node
736        try:
737            nodes = entry.xpath('ns:%s' % FITTING_NODE_NAME, namespaces={'ns': CANSAS_NS})
738            state.fromXML(node=nodes[0])
739        except:
740            logging.info("XML document does not contain fitting information.\n %s" % sys.exc_value)
741           
742        return state
743   
744    def _read_cansas(self, path):
745        """
746            Load data and P(r) information from a CanSAS XML file.
747           
748            @param path: file path
749            @return: Data1D object if a single SASentry was found,
750                        or a list of Data1D objects if multiple entries were found,
751                        or None of nothing was found
752            @raise RuntimeError: when the file can't be opened
753            @raise ValueError: when the length of the data vectors are inconsistent
754        """
755        output = []
756        try:
757            if os.path.isfile(path):
758                basename  = os.path.basename(path)
759                root, extension = os.path.splitext(basename)
760                #TODO: eventually remove the check for .xml once
761                # the P(r) writer/reader is truly complete.
762                if  extension.lower() in self.ext or \
763                    extension.lower() == '.xml':
764                   
765                    tree = etree.parse(path, parser=etree.ETCompatXMLParser())
766                    # Check the format version number
767                    # Specifying the namespace will take care of the file format version
768                    root = tree.getroot()
769                    entry_list = root.xpath('/ns:SASroot/ns:SASentry', namespaces={'ns': CANSAS_NS})
770                    for entry in entry_list:
771                        sas_entry = self._parse_entry(entry)
772                        fitstate = self._parse_state(entry)
773                        sas_entry.meta_data['fitstate'] = fitstate
774                        sas_entry.filename = fitstate.file
775                        output.append(sas_entry)
776                   
777            else:
778                raise RuntimeError, "%s is not a file" % path
779           
780            # Return output consistent with the loader's api
781            if len(output)==0:
782                return None
783            elif len(output)==1:
784                # Call back to post the new state
785                state = output[0].meta_data['fitstate']
786                t = time.localtime(state.timestamp)
787                time_str = time.strftime("%b %d %H:%M", t)
788                # Check that no time stamp is already appended
789                max_char = state.file.find("[")
790                if max_char < 0:
791                    max_char = len(state.file)
792                state.file = state.file[0:max_char] +' [' + time_str + ']'
793               
794                   
795                if state is not None and state.is_data is not None:
796                    exec 'output[0].is_data = state.is_data' 
797                 
798                output[0].filename = state.file
799                #output[0].filename = state.data_name
800                state.data = output[0]
801                state.data.name = state.data_name
802                state.data.id = state.data_id
803                #state.data.group_id = state.data_group_id
804                state.data.group_id = output[0].filename
805             
806                self.call_back(state=state, datainfo=output[0])
807                return output[0]
808            else:
809                return output               
810        except:
811            raise
812           
813    def write(self, filename, datainfo=None, fitstate=None):
814        """
815            Write the content of a Data1D as a CanSAS XML file
816           
817            @param filename: name of the file to write
818            @param datainfo: Data1D object
819            @param fitstate: PageState object
820        """
821        # Sanity check
822        if self.cansas == True:
823           
824            # Add fitting information to the XML document
825            if fitstate is not None:
826                if fitstate.data is None:
827                    data = DataLoader.data_info.Data1D(x=[], y=[])   
828                elif issubclass(fitstate.data.__class__, DataLoader.data_info.Data1D):
829                    data = fitstate.data
830                    doc, sasentry = self._to_xml_doc(data)
831                else:
832                    data = fitstate.data
833                    doc, sasentry = self._data2d_to_xml_doc(data)
834                fitstate.toXML(doc=doc, entry_node=sasentry)
835            # Write the XML document
836            fd = open(filename, 'w')
837            fd.write(doc.toprettyxml())
838            fd.close()
839        else:
840            fitstate.toXML(file=filename)
841       
842       
843if __name__ == "__main__":
844    state = PageState(parent=None)
845    state.toXML()
846    print "state", state
Note: See TracBrowser for help on using the repository browser.