source: sasview/sansview/perspectives/fitting/pagestate.py @ aad74b3

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 aad74b3 was 2296316, checked in by Jae Cho <jhjcho@…>, 14 years ago

moving features from the branch

  • Property mode set to 100644
File size: 66.2 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
[df7ed14]18import numpy
[c77d859]19
[35b556d]20import xml.dom.minidom
[11a7e11]21from xml.dom.minidom import parse
22from lxml import etree
23
24import DataLoader
[61cada5]25from DataLoader.readers.cansas_reader import Reader as CansasReader
[e9b12eaf]26from DataLoader.readers.cansas_reader import get_content, write_node
[df7ed14]27from DataLoader.data_info import Data2D, Detector
[61cada5]28
[11a7e11]29#Information to read/write state as xml
[61cada5]30FITTING_NODE_NAME = 'fitting_plug_in'
31CANSAS_NS = "cansas1d/1.0"
32
[26f3dd5]33list_of_data_attributes = [["is_data", "is_data", "bool"],
[61cada5]34                      ["group_id", "data_group_id", "string"],
35                      ["data_name", "data_name", "string"],
36                      ["data_id", "data_id", "string"],
37                      ["name", "name", "string"],
[26f3dd5]38                      ["data_name", "data_name", "string"]]
[e3d1423]39list_of_state_attributes = [["engine_type", "engine_type", "string"],
40                       ["qmin", "qmin", "float"],
[61cada5]41                      ["qmax", "qmax", "float"],
42                      ["npts", "npts", "float"],
43                      ["shape_rbutton", "shape_rbutton", "bool"],
44                      ["shape_indep_rbutton", "shape_indep_rbutton", "bool"],
45                      ["plugin_rbutton", "plugin_rbutton","bool"],
46                      ["struct_rbutton", "struct_rbutton", "bool"],
[0b12abb5]47                      ["formfactorcombobox", "formfactorcombobox", "float"],
48                      ["structurecombobox", "structurecombobox", "float"],
[26f3dd5]49                      ["enable_smearer","enable_smearer","bool"],
50                      ["disable_smearer","disable_smearer","bool"],
51                      ["pinhole_smearer","pinhole_smearer","bool"],
52                      ["slit_smearer","slit_smearer","bool"],
53                      ["enable_disp","enable_disp","bool"],
54                      ["disable_disp","disable_disp","bool"],
55                      ["slit_smearer","slit_smearer","bool"],
56                      ["enable2D","enable2D","bool"],
57                      ["cb1","cb1","bool"],
[5cc7001]58                      ["tcChi","tcChi","float"],
59                     ["smearer", "smearer", "float"],
60                     ["smear_type","smear_type", "string"],
61                     ["dq_l", "dq_l", "string"],
62                     ["dq_r","dq_r", "string"]]
63
[11a7e11]64list_of_model_attributes = [["values", "values"],
65                            ["weights", "weights"]]
66
[2296316]67list_of_obj_dic =  [["disp_obj_dict", "_disp_obj_dict", "string"]]
68
[fb59ed9]69list_of_state_parameters = [["parameters", "parameters"] , 
70                            ["str_parameters", "str_parameters"] ,                     
[61cada5]71                            ["orientation_parameters", "orientation_params"],
72                            ["dispersity_parameters", "orientation_params_disp"],
73                            ["fixed_param", "fixed_param"],                     
74                            ["fittable_param","fittable_param"]]
[df7ed14]75list_of_data_2d_attr = [["xmin", "xmin","float"],
76                        ["xmax","xmax","float"],
77                        ["ymin","ymin","float"],
78                        ["ymax","ymax","float"],
79                        ["_xaxis","_xaxis", "string"],
80                        ["_xunit", "_xunit", "string"],
81                        ["_yaxis","_yaxis","string"],
82                        ["_yunit","_yunit","string"],
83                        ["_zaxis","_zaxis","string"],
84                        ["_zunit","_zunit","string"]]
85list_of_data2d_values = [["qx_data","qx_data","float"],
86                         ["qy_data","qy_data","float"],
87                         ["dqx_data","dqx_data","float"],
88                         ["dqy_data","dqy_data","float"],
89                         ["data","data","float"],
90                         ["q_data","q_data","float"],
91                         ["err_data","err_data","float"],
92                         ["mask","mask","bool"]]
[61cada5]93
[0b12abb5]94def parse_entry_helper( node, item):
95    """
96    Create a numpy list from value extrated from the node
97   
98    :param node: node from each the value is stored
99    :param item: list name of three strings.the two first are name of data
100        attribute and the third one is the type of the value of that
101        attribute. type can be string, float, bool, etc.
102   
103    : return: numpy array
104    """
105    if node is not None:
106        if item[2] == "string":
107            return str(node.get(item[0]).strip())
108        elif item[2] == "bool":
109            try:
[e3d1423]110                return node.get(item[0]).strip() == "True"
111               
[0b12abb5]112            except:
113                return None
114        else:
115            try:
116                return float(node.get(item[0]))
117            except:
118                return None
119           
120           
[cfc0913]121class PageState(object):
[c77d859]122    """
[5062bbf]123    Contains information to reconstruct a page of the fitpanel.
[c77d859]124    """
[61cada5]125    def __init__(self, parent=None, model=None, data=None):
[c77d859]126       
127        """
[5062bbf]128        Initialize the current state
129       
130        :param model: a selected model within a page
131        :param data:
132       
[c77d859]133        """
[61cada5]134        self.file = None
[11a7e11]135        #Time of state creation
136        self.timestamp = time.time()
[c77d859]137        ## Data member to store the dispersion object created
138        self._disp_obj_dict = {}
[61cada5]139        #------------------------
[c77d859]140        #Data used for fitting
141        self.data = data
[2296316]142        # model data
143        self.theory_data = None
144        #Is 2D
145        self.is_2D = False
146        self.images = None
147       
[61cada5]148        #save additional information on data that dataloader.reader does not read
149        self.is_data = None
150        self.data_name = ""
151       
152        if self.data is not None:
153            self.data_name = self.data.name
154        self.data_id = None
155        if self.data is not None and hasattr(self.data, "id"):
156            self.data_id = self.data.id
157        self.data_group_id = None
158        if self.data is not None and hasattr(self.data, "group_id"):
159            self.data_group_id = self.data.group_id
[0b12abb5]160       
[61cada5]161        ## reset True change the state of exsiting button
162        self.reset = False
163       
[c99a6c5]164        #engine type
165        self.engine_type = None
[c77d859]166        # flag to allow data2D plot
[cfc0913]167        self.enable2D = False
[c77d859]168        # model on which the fit would be performed
169        self.model = model
[dce84c0]170        #list of process done to model
171        self.process = []
[c77d859]172        #fit page manager
173        self.manager = None
174        #Store the parent of this panel parent
175        # For this application fitpanel is the parent
176        self.parent  = parent
177        # Event_owner is the owner of model event
178        self.event_owner = None
[c9a4377]179        ##page name
[cfc0913]180        self.page_name = ""
[c77d859]181        # Contains link between  model ,all its parameters, and panel organization
[61cada5]182        self.parameters = []
[fb59ed9]183        # String parameter list that can not be fitted
184        self.str_parameters = []
[c77d859]185        # Contains list of parameters that cannot be fitted and reference to
186        #panel objects
[61cada5]187        self.fixed_param = []
[c77d859]188        # Contains list of parameters with dispersity and reference to
189        #panel objects
[61cada5]190        self.fittable_param = []
[60132ef]191        ## orientation parameters
[61cada5]192        self.orientation_params = []
[fc6ea43]193        ## orientation parmaters for gaussian dispersity
[61cada5]194        self.orientation_params_disp = []
[3370922]195        ## smearer info
[61cada5]196        self.smearer = None
[7609f1a]197        self.smear_type = None
198        self.dq_l = None
199        self.dq_r = None
200
[c77d859]201        #list of dispersion paramaters
[cfc0913]202        self.disp_list =[]
[61cada5]203        if self.model is not None:
[71f0373]204            self.disp_list = self.model.getDispParamList()
[2296316]205
[61cada5]206        self.disp_cb_dict = {}
[2296316]207        self.values = {}
208        self.weights = {}
209
[dad49a0]210                   
[c77d859]211        #contains link between a model and selected parameters to fit
[61cada5]212        self.param_toFit = []
[a074145]213        ##dictionary of model type and model class
[cfc0913]214        self.model_list_box = None
[a074145]215        ## save the state of the context menu
[61cada5]216        self.saved_states = {}
[240b9966]217        ## save selection of combobox
[3b9e023]218        self.formfactorcombobox = None
219        self.structurecombobox  = None
[240b9966]220        ## radio box to select type of model
221        self.shape_rbutton = False
222        self.shape_indep_rbutton = False
223        self.struct_rbutton = False
224        self.plugin_rbutton = False
[b787e68c]225        ## the indice of the current selection
226        self.disp_box = 0
[c77d859]227        ## Qrange
[cfc0913]228        ## Q range
[61cada5]229        self.qmin = 0.001
230        self.qmax = 0.1
[0b12abb5]231        #reset data range
232        self.qmax_x = None
233        self.qmin_x = None
234       
[cfc0913]235        self.npts = None
[61cada5]236        self.name = ""
[4523b68]237        self.multi_factor = None
[cfc0913]238        ## enable smearering state
239        self.enable_smearer = False
[fc6ea43]240        self.disable_smearer = True
[7609f1a]241        self.pinhole_smearer = False
242        self.slit_smearer   = False
[cfc0913]243        ## disperity selection
[61cada5]244        self.enable_disp = False
245        self.disable_disp = True
[11a7e11]246       
[cfc0913]247        ## state of selected all check button
248        self.cb1 = False
[0aeabc6]249        ## store value of chisqr
[61cada5]250        self.tcChi = None
[240b9966]251   
[6f023e8]252    def clone(self):
[61cada5]253        """
[5062bbf]254        Create a new copy of the current object
[61cada5]255        """
256        model = None
257        if self.model is not None:
[6f023e8]258            model = self.model.clone()
[bb70474]259            model.name = self.model.name
[61cada5]260        obj = PageState(self.parent, model=model)
261        obj.file = copy.deepcopy(self.file)
[6f023e8]262        obj.data = copy.deepcopy(self.data)
[61cada5]263        if self.data is not None:
264            self.data_name = self.data.name
265        obj.data_name = self.data_name
266        obj.is_data = self.is_data
[dcf29d7]267        obj.model_list_box = copy.deepcopy(self.model_list_box)
[c99a6c5]268        obj.engine_type = copy.deepcopy(self.engine_type)
[240b9966]269       
[61cada5]270        obj.formfactorcombobox = self.formfactorcombobox
271        obj.structurecombobox  = self.structurecombobox 
[240b9966]272       
273        obj.shape_rbutton = self.shape_rbutton
274        obj.shape_indep_rbutton = self.shape_indep_rbutton
275        obj.struct_rbutton = self.struct_rbutton
276        obj.plugin_rbutton = self.plugin_rbutton
277       
[dcf29d7]278        obj.manager = self.manager
279        obj.event_owner = self.event_owner
[71f0373]280        obj.disp_list = copy.deepcopy(self.disp_list)
[fc6ea43]281       
[c477b31]282        obj.enable2D = copy.deepcopy(self.enable2D)
[cfc0913]283        obj.parameters = copy.deepcopy(self.parameters)
[fb59ed9]284        obj.str_parameters = copy.deepcopy(self.str_parameters)
[cfc0913]285        obj.fixed_param = copy.deepcopy(self.fixed_param)
286        obj.fittable_param = copy.deepcopy(self.fittable_param)
[60132ef]287        obj.orientation_params =  copy.deepcopy(self.orientation_params)
[fc6ea43]288        obj.orientation_params_disp =  copy.deepcopy(self.orientation_params_disp)
[b787e68c]289        obj.enable_disp = copy.deepcopy(self.enable_disp)
[fc6ea43]290        obj.disable_disp = copy.deepcopy(self.disable_disp)
[0aeabc6]291        obj.tcChi = self.tcChi
292 
[c477b31]293        if len(self._disp_obj_dict)>0:
294            for k , v in self._disp_obj_dict.iteritems():
295                obj._disp_obj_dict[k]= v
[dad49a0]296        if len(self.disp_cb_dict)>0:
297            for k , v in self.disp_cb_dict.iteritems():
298                obj.disp_cb_dict[k]= v
[2296316]299        if len(self.values)>0: 
300            for k , v in self.values.iteritems():       
301                obj.values[k] = v
302        if len(self.weights)>0: 
303            for k , v in self.weights.iteritems():       
304                obj.weights[k] = v
[b787e68c]305        obj.enable_smearer = copy.deepcopy(self.enable_smearer)
[fc6ea43]306        obj.disable_smearer = copy.deepcopy(self.disable_smearer)
[7609f1a]307        obj.pinhole_smearer = copy.deepcopy(self.pinhole_smearer)
308        obj.slit_smearer = copy.deepcopy(self.slit_smearer)
309        obj.smear_type = copy.deepcopy(self.smear_type)
310        obj.dq_l = copy.deepcopy(self.dq_l)
311        obj.dq_r = copy.deepcopy(self.dq_r)
312
[b787e68c]313        obj.disp_box = copy.deepcopy(self.disp_box)
314        obj.qmin = copy.deepcopy(self.qmin)
315        obj.qmax = copy.deepcopy(self.qmax)
[4523b68]316        obj.multi_factor = copy.deepcopy(self.multi_factor)
[b787e68c]317        obj.npts = copy.deepcopy(self.npts )
318        obj.cb1 = copy.deepcopy(self.cb1)
[3370922]319        obj.smearer = copy.deepcopy(self.smearer)
320       
[a074145]321        for name, state in self.saved_states.iteritems():
322            copy_name = copy.deepcopy(name)
323            copy_state = state.clone()
324            obj.saved_states[copy_name]= copy_state
[6f023e8]325        return obj
[61cada5]326   
327    def _repr_helper(self, list, rep):
328        """
[5062bbf]329        Helper method to print a state
[61cada5]330        """
331        for item in list:
[240b9966]332            rep += "parameter name: %s \n"%str(item[1])
333            rep += "value: %s\n"%str(item[2])
334            rep += "selected: %s\n"%str(item[0])
335            rep += "error displayed : %s \n"%str(item[4][0])
336            rep += "error value:%s \n"%str(item[4][1])
337            rep += "minimum displayed : %s \n"%str(item[5][0])
338            rep += "minimum value : %s \n"%str(item[5][1])
339            rep += "maximum displayed : %s \n"%str(item[6][0])
340            rep += "maximum value : %s \n"%str(item[6][1])
341            rep += "parameter unit: %s\n\n"%str(item[7])
342        return rep
[5ef55d2]343 
[61cada5]344    def __repr__(self):
[11a7e11]345        """
[5062bbf]346        output string for printing
[11a7e11]347        """
348        rep = "\nState name: %s\n"%self.file
349        t = time.localtime(self.timestamp)
[2296316]350        time_str = time.strftime("%b %d %H;%M of %Y", t)
[11a7e11]351        rep += "State created on : %s\n"%time_str
352        rep += "State form factor combobox selection: %s\n"%self.formfactorcombobox
353        rep += "State structure factor combobox selection: %s\n"%self.structurecombobox
354        rep += "is data : %s\n"%self.is_data
355        rep += "data's name : %s\n"%self.data_name
356        rep += "data's id : %s\n"%self.data_id
357        rep += "model type (form factor) selected: %s\n"%self.shape_rbutton
358        rep += "model type (shape independent) selected: %s\n"%self.shape_indep_rbutton
359        rep += "model type (structure factor) selected: %s\n"%self.struct_rbutton
360        rep += "model type (plug-in ) selected: %s\n"%self.plugin_rbutton
361        rep += "data : %s\n"% str(self.data)
362        rep += "Plotting Range: min: %s, max: %s, steps: %s\n"%(str(self.qmin),
363                                                str(self.qmax),str(self.npts))
[0b12abb5]364        rep += "Dispersion selection : %s\n"%str(self.disp_box)
365        rep += "Smearing enable : %s\n"%str(self.enable_smearer)
366        rep += "Smearing disable : %s\n"%str(self.disable_smearer)
367        rep += "Pinhole smearer enable : %s\n"%str(self.pinhole_smearer)
368        rep += "Slit smearer enable : %s\n"%str(self.slit_smearer)
369        rep += "Dispersity enable : %s\n"%str(self.enable_disp)
370        rep += "Dispersity disable : %s\n"%str(self.disable_disp)
371        rep += "Slit smearer enable: %s\n"%str(self.slit_smearer)
372        rep += "2D enable : %s\n"%str(self.enable2D)
373        rep += "All parameters checkbox selected: %s\n"%(self.cb1)
374        rep += "Value of Chisqr : %s\n"%str(self.tcChi)
375        rep += "Smear object : %s\n"%str(self.smearer)
376        rep += "Smear type : %s\n"%(self.smear_type)
377        rep += "dq_l  : %s\n"%self.dq_l
378        rep += "dq_r  : %s\n"%self.dq_r
379       
[11a7e11]380        rep += "model  : %s\n\n"% str(self.model)
[2296316]381        temp_parameters = []
382        temp_fittable_param = []
383        if self.data.__class__.__name__ == "Data2D":
384            self.is_2D = True
385        else:
386            self.is_2D = False
387        if self.data is not None:
388            if not self.is_2D:
389                for item in self.parameters:
390                    if not item in self.orientation_params:
391                        temp_parameters.append(item)
392                for item in self.fittable_param:
393                    if not item in self.orientation_params_disp:
394                        temp_fittable_param.append(item)
395            else:
396                temp_parameters = self.parameters
397                temp_fittable_param = self.fittable_param
398               
399            rep += "number parameters(self.parameters): %s\n"%len(temp_parameters)
400            rep = self._repr_helper( list=temp_parameters, rep=rep)
401            rep += "number str_parameters(self.str_parameters): %s\n"%len(self.str_parameters)
402            rep = self._repr_helper( list=self.str_parameters, rep=rep) 
403            rep += "number fittable_param(self.fittable_param): %s\n"%len(temp_fittable_param)
404            rep = self._repr_helper( list=temp_fittable_param, rep=rep) 
405            """
406            if is_2D:
407                rep += "number orientation parameters"
408                rep += "(self.orientation_params): %s\n"%len(self.orientation_params)
409                rep = self._repr_helper( list=self.orientation_params, rep=rep)
410                rep += "number dispersity parameters"
411                rep += "(self.orientation_params_disp): %s\n"%len(self.orientation_params_disp)
412                rep = self._repr_helper( list=self.orientation_params_disp, rep=rep)
413            """
[0b12abb5]414       
[61cada5]415        return rep
[2296316]416
417
418    def set_report_string(self):
419        """
420        Get the values (strings) from __str__ for report
421        """
422        # Dictionary of teh report strings
423        repo_time = ""
424        model_name = ""
425        title = ""
426        title_name = ""
427        file_name = ""
428        param_string = ""
429        paramval_string = ""
430        chi2_string = ""
431        q_range = ""
432        strings = self.__repr__()
433        lines = strings.split('\n')
434
435        # get all string values from __str__()
436        for line in lines:
437            value = ""
438            content = line.split(":")
439            name = content[0]
440            try:
441                value = content[1]
442            except:
443                pass
444            if name.count("State created on"):
445                repo_time = "Created on " + value
446            if name.count("parameter name"):
447                val_name = value.split(".")
448                if len(val_name) > 1:
449                    if val_name[1].count("width"):
450                        param_string += value + ','
451                    else:
452                        continue
453                else:
454                    param_string += value + ','
455            if name == "value":
456                param_string += value + ','
457            if name == "error value":
458                param_string += value + ','
459            if name == "parameter unit":
460                param_string += value + ':' 
461            if name == "Value of Chisqr ":
462                chi2 = ("Chi2/Npts = " + value)
463                chi2_string = CENTRE % chi2
464            if name == "Title":
465                if len(value.strip()) == 0:
466                    continue
467                title = value + " [" + repo_time + "]"
468                title_name = HEADER % title
469            if name == "data ":
470                try:
471                    file = ("File name:" + content[2])
472                    file_name = CENTRE % file
473                    if len(title) == 0:
474                        title = content[2] + " [" + repo_time + "]"
475                        title_name = HEADER % title
476                except:
477                    pass
478            if name == "Plotting Range":
479                try:
480                    q_range = content[1] + " = " + content[2] \
481                            + " = " + content[3].split(",")[0]
482                    q_name = ("Q Range:    " + q_range)
483                    q_range = CENTRE % q_name
484                except:
485                    pass
486        paramval = ""
487        for lines in param_string.split(":"): 
488            line = lines.split(",")
489            if len(lines) > 0: 
490                param = line[0] 
491                param += " = " + line[1]
492                if len(line[2].split()) > 0 and not line[2].count("None"):
493                    param += " +- " + line[2]
494                if len(line[3].split()) > 0 and not line[3].count("None"):
495                    param += " " + line[3]
496                if not paramval.count(param):
497                    paramval +=  param + "\n"
498                    paramval_string += CENTRE % param + "\n"
499       
500        html_string = title_name + "\n" + file_name + \
501                                   "\n" + q_range + \
502                                   "\n" + chi2_string + \
503                                   "\n" + ELINE + \
504                                   "\n" + paramval_string + \
505                                   "\n" + ELINE + \
506                                   "\n" + FEET_1 % title + \
507                                   "\n" + FEET_2
508                           
509        text_string = "\n\n\n" + title + "\n\n" + file + \
510                              "\n" + q_name + \
511                              "\n" + chi2 + \
512                              "\n\n" + paramval
513                                           
514        return html_string, text_string, title
515   
516    def report(self, figs=None, canvases=None):
517        """
518        Invoke report dialog panel
519       
520        : param figs: list of pylab figures [list]
521        """
522        from report_dialog import ReportDialog
523        # get the strings for report
524        html_str, text_str, title = self.set_report_string()
525        # Allow 2 figures to append
526        if len(figs) == 1:
527            add_str = FEET_3
528        elif len(figs) == 2:
529            add_str = ELINE
530            add_str += FEET_2 % ("%s") 
531            add_str += ELINE
532            add_str += FEET_3
533        elif len(figs) > 2:
534            add_str = ELINE
535            add_str += FEET_2 % ("%s") 
536            add_str += ELINE
537            add_str += FEET_2 % ("%s") 
538            add_str += ELINE
539            add_str += FEET_3
540        else:
541            add_str = ""
542        # final report html strings
543        report_str = html_str % ("%s") + add_str
544
545        # make plot image
546        images = self.set_plot_state(figs, canvases)
547        report_list = [report_str, text_str, images ]
548        dialog = ReportDialog(report_list, None, -1, "")
549        dialog.ShowModal()
550         
[61cada5]551    def _toXML_helper(self, list, element, newdoc):
552        """
[5062bbf]553        Helper method to create xml file for saving state
[61cada5]554        """
555        for item in list:
556            sub_element = newdoc.createElement('parameter')
557            sub_element.setAttribute('name', str(item[1]))
558            sub_element.setAttribute('value', str(item[2]))
559            sub_element.setAttribute('selected_to_fit', str(item[0]))
560            sub_element.setAttribute('error_displayed', str(item[4][0]))
561            sub_element.setAttribute('error_value', str(item[4][1]))
562            sub_element.setAttribute('minimum_displayed', str(item[5][0]))
563            sub_element.setAttribute('minimum_value', str(item[5][1]))
564            sub_element.setAttribute('maximum_displayed', str(item[6][0]))
565            sub_element.setAttribute('maximum_value', str(item[6][1]))
566            sub_element.setAttribute('unit', str(item[7]))
567            element.appendChild(sub_element)
568       
569    def toXML(self, file="fitting_state.fitv", doc=None, entry_node=None):
570        """
[5062bbf]571        Writes the state of the InversionControl panel to file, as XML.
572       
573        Compatible with standalone writing, or appending to an
574        already existing XML document. In that case, the XML document
575        is required. An optional entry node in the XML document may also be given.
576       
577        :param file: file to write to
578        :param doc: XML document object [optional]
579        :param entry_node: XML node within the XML document at which we will append the data [optional]
580       
[61cada5]581        """
582        from xml.dom.minidom import getDOMImplementation
[71f0373]583
[61cada5]584        # Check whether we have to write a standalone XML file
585        if doc is None:
586            impl = getDOMImplementation()
587            doc_type = impl.createDocumentType(FITTING_NODE_NAME, "1.0", "1.0")     
588            newdoc = impl.createDocument(None, FITTING_NODE_NAME, doc_type)
589            top_element = newdoc.documentElement
590        else:
591            # We are appending to an existing document
592            newdoc = doc
593            top_element = newdoc.createElement(FITTING_NODE_NAME)
594            if entry_node is None:
595                newdoc.documentElement.appendChild(top_element)
596            else:
597                entry_node.appendChild(top_element)
598           
599        attr = newdoc.createAttribute("version")
600        attr.nodeValue = '1.0'
601        top_element.setAttributeNode(attr)
[cfc0913]602       
[61cada5]603        # File name
604        element = newdoc.createElement("filename")
605        if self.file is not None:
606            element.appendChild(newdoc.createTextNode(str(self.file)))
607        else:
608            element.appendChild(newdoc.createTextNode(str(file)))
609        top_element.appendChild(element)
[11a7e11]610       
611        element = newdoc.createElement("timestamp")
612        element.appendChild(newdoc.createTextNode(time.ctime(self.timestamp)))
613        attr = newdoc.createAttribute("epoch")
614        attr.nodeValue = str(self.timestamp)
615        element.setAttributeNode(attr)
616        top_element.appendChild(element)
[61cada5]617        # Inputs
618        inputs = newdoc.createElement("Attributes")
619        top_element.appendChild(inputs)
620       
[0b12abb5]621
[61cada5]622        if self.data is not None and hasattr(self.data, "group_id"):
623            self.data_group_id = self.data.group_id
624        if self.data is not None and hasattr(self.data, "is_data"):
625            self.is_data = self.data.is_data
626        if self.data is not None:
627            self.data_name = self.data.name
628        if self.data is not None and hasattr(self.data, "id"):
629            self.data_id = self.data.id
630       
[26f3dd5]631        for item in list_of_data_attributes:
[0b12abb5]632            element = newdoc.createElement(item[0])
[2296316]633            exec "element.setAttribute(item[0], str(self.%s))" % (item[1])
[0b12abb5]634            inputs.appendChild(element)   
[26f3dd5]635       
636        for item in list_of_state_attributes:
637            element = newdoc.createElement(item[0])
[2296316]638            exec "element.setAttribute(item[0], str(self.%s))" % (item[1])
[26f3dd5]639            inputs.appendChild(element)
[2296316]640
641        # For self.values ={ disp_param_name: [vals,...],...}   
642        # and for self.weights ={ disp_param_name: [weights,...],...}             
[11a7e11]643        for item in list_of_model_attributes:
644            element = newdoc.createElement(item[0])
[2296316]645            exec "list = self.%s" % item[1]
646            for key, value in list.iteritems():
647                sub_element = newdoc.createElement(key)
648                sub_element.setAttribute('name', str(key))
649                for val in value:
650                    com = "sub_element.appendChild"
651                    com += "(newdoc.createTextNode(str(%s)))"
652                    exec com % val
653                   
654                element.appendChild(sub_element) 
[11a7e11]655            inputs.appendChild(element)
[2296316]656       
657        # Create doc for the dictionary of self._disp_obj_dic
658        for item in list_of_obj_dic:
659             element = newdoc.createElement(item[0])
660             exec "list = self.%s" % item[1]
661             for key, val in list.iteritems():
662                 value = repr(val)
663                 sub_element = newdoc.createElement(key)
664                 sub_element.setAttribute('name', str(key))
665                 sub_element.setAttribute('value', str(value))
666                 element.appendChild(sub_element) 
667             inputs.appendChild(element)   
668                 
[61cada5]669        for item in list_of_state_parameters:
670            element = newdoc.createElement(item[0])
[2296316]671            com = "self._toXML_helper(list=self.%s,"
672            com += " element=element, newdoc=newdoc)"
673            exec com % item[1]                       
[61cada5]674            inputs.appendChild(element)
[2296316]675               
[61cada5]676        # Save the file
677        if doc is None:
678            fd = open(file, 'w')
679            fd.write(newdoc.toprettyxml())
680            fd.close()
681            return None
682        else:
683            return newdoc.toprettyxml()
684       
685    def _fromXML_helper(self, node, list):
686        """
[5062bbf]687        Helper function to write state to xml
[61cada5]688        """
689        for item in node:
[0b12abb5]690            try:
691                name = item.get('name')
692            except:
693                name = None
694            try:
695                value = item.get('value')
696            except:
697                value = None
698            try:
[3ad91de]699                selected_to_fit = (item.get('selected_to_fit') == "True")
[0b12abb5]700            except:
701                selected_to_fit = None
702            try:
[3ad91de]703                error_displayed = (item.get('error_displayed') == "True")
[0b12abb5]704            except:
705                error_displayed = None
706            try: 
707                error_value = item.get('error_value')
708            except:
709                error_value = None
710            try:
[3ad91de]711                minimum_displayed = (item.get('minimum_displayed')== "True")
[0b12abb5]712            except:
713                minimum_displayed = None
714            try:
715                minimum_value = item.get('minimum_value')
716            except:
717                minimum_value = None
718            try:
[3ad91de]719                maximum_displayed = (item.get('maximum_displayed') == "True")
[0b12abb5]720            except:
721                maximum_displayed = None
722            try:
723                maximum_value = item.get('maximum_value')
724            except:
725                maximum_value = None
726            try:
727                unit = item.get('unit')
728            except:
729                unit = None
[2296316]730            list.append([selected_to_fit, name, value, "+/-",
731                         [error_displayed, error_value],
732                         [minimum_displayed,minimum_value],
733                         [maximum_displayed,maximum_value], unit])
[cfc0913]734       
[61cada5]735    def fromXML(self, file=None, node=None):
736        """
[5062bbf]737        Load fitting state from a file
738       
739        :param file: .fitv file
740        :param node: node of a XML document to read from
741       
[61cada5]742        """
743        if file is not None:
[11a7e11]744            msg = "PageState no longer supports non-CanSAS"
745            msg += " format for fitting files"
746            raise RuntimeError, msg
[61cada5]747           
[11a7e11]748        if node.get('version')and node.get('version') == '1.0':
[61cada5]749           
750            # Get file name
751            entry = get_content('ns:filename', node)
752            if entry is not None:
753                self.file = entry.text.strip()
[11a7e11]754               
755            # Get time stamp
756            entry = get_content('ns:timestamp', node)
757            if entry is not None and entry.get('epoch'):
758                try:
759                    self.timestamp = float(entry.get('epoch'))
760                except:
761                    msg = "PageState.fromXML: Could not"
762                    msg += " read timestamp\n %s" % sys.exc_value
763                    logging.error(msg)
764           
[61cada5]765            # Parse fitting attributes
766            entry = get_content('ns:Attributes', node)
[0b12abb5]767            for item in list_of_data_attributes:
768                node = get_content('ns:%s'%item[0], entry)
769                try:
[2296316]770                    exec "self.%s = parse_entry_helper(node, item)" % item[0]
[0b12abb5]771                   
772                except:
773                    raise
[2296316]774
[61cada5]775            if entry is not None:
[0b12abb5]776               
[61cada5]777                for item in list_of_state_attributes:
[2296316]778                    node = get_content('ns:%s' % item[0], entry)
[0b12abb5]779                    try:
[2296316]780                        exec "self.%s = parse_entry_helper(node, item)" % \
781                                                                str(item[0])
[0b12abb5]782                    except:
783                        raise
784                   
[2296316]785                for item in list_of_state_parameters:
786                    node = get_content("ns:%s" % item[0], entry)
787                    exec "self._fromXML_helper(node=node, list=self.%s)" % \
788                                                                    item[1]
789               
790                # Recover _disp_obj_dict from xml file   
791                self._disp_obj_dict = {}   
792                for item in list_of_obj_dic:
793                    # Get node
794                    node = get_content("ns:%s" % item[0], entry)
795                    for attr in node:
796                        name = attr.get('name')
797                        val  = attr.get('value')
798                        value = val.split(" instance")[0]
799                        disp_name = value.split("<")[1]
[11a7e11]800                        try:
[2296316]801                            # Try to recover disp_model object from strings
802                            com  = "from sans.models.dispersion_models "
803                            com += "import %s as disp"
804                            com_name = disp_name.split(".")[3]
805                            exec com % com_name
806                            disp_model = disp()
807                            exec "self.%s['%s'] = com_name" % (item[1], name)
[11a7e11]808                        except:
[2296316]809                            pass
810                       
811                # get self.values and self.weights dic. if exists   
812                for item in list_of_model_attributes:
813                    node = get_content("ns:%s" % item[0], entry)
814                    dic = {}
815                    list = []
816                    for par in node:
817                        name = par.get('name')
818                        values = par.text.split('\n')
819                        # Get lines only with numbers
820                        for line in values:
821                            try:
822                                val= float(line)
823                                list.append(val) 
824                            except:
825                                # pass if line is empty (it happens)
826                                pass
827                    dic[name] = numpy.array(list)
828                    exec "self.%s = dic" % item[1]
829                   
830    def set_plot_state(self, figs, canvases):
831        """
832        Build image state that wx.html understand
833        by plotting, putting it into wx.FileSystem image object
834
835        """
836        images = []
837        # some imports
838        import wx
839
840        # Reset memory
841        self.imgRAM = None
842        wx.MemoryFSHandler()
843       
844        # For no figures in the list, prepare empty plot
845        if figs == None or len(figs) == 0:
846            figs = [None]
847           
848        # Loop over the list of figures   
849        # use wx.MemoryFSHandler
850        self.imgRAM = wx.MemoryFSHandler() 
851        for fig in figs:
852            if figs != None:
853                #fig.set_facecolor('w')
854                ind = figs.index(fig)
855                canvas = canvases[ind]
[0b12abb5]856               
[2296316]857            #bmp = wx.BitmapDataObject()
858            #bmp.SetBitmap(canvas.bitmap)
859            #store the image in wx.FileSystem Object
860            wx.FileSystem.AddHandler(wx.MemoryFSHandler())
861           
862            # index of the fig
863            ind = figs.index(fig)
864           
865            #AddFile, image can be retrieved with 'memory:filename'
866            self.imgRAM.AddFile('img_fit%s.png' % ind , 
867                                canvas.bitmap, wx.BITMAP_TYPE_PNG)
868           
869            #append figs
870            images.append(fig)
871           
872        return images                   
[61cada5]873
874class Reader(CansasReader):
875    """
[5062bbf]876    Class to load a .fitv fitting file
[61cada5]877    """
878    ## File type
879    type_name = "Fitting"
880   
881    ## Wildcards
[b35d3d1]882    type = ["Fitting files (*.fitv)|*.fitv"
883            "SANSView file (*.svs)|*.svs"]
[61cada5]884    ## List of allowed extensions
[b35d3d1]885    ext=['.fitv', '.FITV', '.svs', 'SVS']   
[61cada5]886   
887    def __init__(self, call_back=None, cansas=True):
[df7ed14]888        CansasReader.__init__(self)
[61cada5]889        """
[5062bbf]890        Initialize the call-back method to be called
891        after we load a file
892       
893        :param call_back: call-back method
894        :param cansas:  True = files will be written/read in CanSAS format
895                        False = write CanSAS format
[61cada5]896           
897        """
898        ## Call back method to be executed after a file is read
899        self.call_back = call_back
900        ## CanSAS format flag
901        self.cansas = cansas
[75fbd17]902        self.state = None
903    def get_state(self):
904        return self.state
[61cada5]905       
906    def read(self, path):
907        """
[5062bbf]908        Load a new P(r) inversion state from file
909       
910        :param path: file path
911       
[61cada5]912        """
[e9b12eaf]913        if self.cansas == True:
[61cada5]914            return self._read_cansas(path)
[e9b12eaf]915     
916    def _data2d_to_xml_doc(self, datainfo):
[61cada5]917        """
[5062bbf]918        Create an XML document to contain the content of a Data2D
919       
920        :param datainfo: Data2D object
921       
[61cada5]922        """
923        if not issubclass(datainfo.__class__, Data2D):
924            raise RuntimeError, "The cansas writer expects a Data2D instance"
925       
926        doc = xml.dom.minidom.Document()
927        main_node = doc.createElement("SASroot")
928        main_node.setAttribute("version", self.version)
[df7ed14]929        main_node.setAttribute("xmlns", "cansas1d/%s" % self.version)
930        main_node.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance")
931        main_node.setAttribute("xsi:schemaLocation", "cansas1d/%s http://svn.smallangles.net/svn/canSAS/1dwg/trunk/cansas1d.xsd" % self.version)
932       
[61cada5]933        doc.appendChild(main_node)
934       
935        entry_node = doc.createElement("SASentry")
936        main_node.appendChild(entry_node)
[df7ed14]937       
[61cada5]938        write_node(doc, entry_node, "Title", datainfo.title)
[df7ed14]939        if datainfo is not None:
940            write_node(doc, entry_node, "data_class", datainfo.__class__.__name__)
[61cada5]941        for item in datainfo.run:
942            runname = {}
943            if datainfo.run_name.has_key(item) and len(str(datainfo.run_name[item]))>1:
944                runname = {'name': datainfo.run_name[item] }
945            write_node(doc, entry_node, "Run", item, runname)
946        # Data info
[df7ed14]947        new_node = doc.createElement("SASdata")
948        entry_node.appendChild(new_node)
[e9b12eaf]949        for item in list_of_data_2d_attr:
950            element = doc.createElement(item[0])
951            exec "element.setAttribute(item[0], str(datainfo.%s))"%(item[1])
[df7ed14]952            new_node.appendChild(element)
953           
[e9b12eaf]954        for item in list_of_data2d_values:
[df7ed14]955            root_node = doc.createElement(item[0])
956            new_node.appendChild(root_node)
957           
[35b556d]958            exec "temp_list = datainfo.%s"%item[1]
[df7ed14]959
[35b556d]960            if temp_list is None or len(temp_list)== 0:
[df7ed14]961                element = doc.createElement(item[0])
[35b556d]962                exec "element.appendChild(doc.createTextNode(str(%s)))"%temp_list
[df7ed14]963                root_node.appendChild(element)
[35b556d]964            else:
965                for value in temp_list:
[df7ed14]966                    element = doc.createElement(item[0])
967                    exec "element.setAttribute(item[0], str(%s))"%value
968                    root_node.appendChild(element)
969       
[61cada5]970        # Sample info
971        sample = doc.createElement("SASsample")
972        if datainfo.sample.name is not None:
973            sample.setAttribute("name", str(datainfo.sample.name))
974        entry_node.appendChild(sample)
975        write_node(doc, sample, "ID", str(datainfo.sample.ID))
976        write_node(doc, sample, "thickness", datainfo.sample.thickness, {"unit":datainfo.sample.thickness_unit})
977        write_node(doc, sample, "transmission", datainfo.sample.transmission)
978        write_node(doc, sample, "temperature", datainfo.sample.temperature, {"unit":datainfo.sample.temperature_unit})
979       
980        for item in datainfo.sample.details:
981            write_node(doc, sample, "details", item)
982       
983        pos = doc.createElement("position")
984        written = write_node(doc, pos, "x", datainfo.sample.position.x, {"unit":datainfo.sample.position_unit})
985        written = written | write_node(doc, pos, "y", datainfo.sample.position.y, {"unit":datainfo.sample.position_unit})
986        written = written | write_node(doc, pos, "z", datainfo.sample.position.z, {"unit":datainfo.sample.position_unit})
987        if written == True:
988            sample.appendChild(pos)
989       
990        ori = doc.createElement("orientation")
991        written = write_node(doc, ori, "roll",  datainfo.sample.orientation.x, {"unit":datainfo.sample.orientation_unit})
992        written = written | write_node(doc, ori, "pitch", datainfo.sample.orientation.y, {"unit":datainfo.sample.orientation_unit})
993        written = written | write_node(doc, ori, "yaw",   datainfo.sample.orientation.z, {"unit":datainfo.sample.orientation_unit})
994        if written == True:
995            sample.appendChild(ori)
996       
997        # Instrument info
998        instr = doc.createElement("SASinstrument")
999        entry_node.appendChild(instr)
1000       
1001        write_node(doc, instr, "name", datainfo.instrument)
1002       
1003        #   Source
1004        source = doc.createElement("SASsource")
1005        if datainfo.source.name is not None:
1006            source.setAttribute("name", str(datainfo.source.name))
1007        instr.appendChild(source)
1008       
1009        write_node(doc, source, "radiation", datainfo.source.radiation)
1010        write_node(doc, source, "beam_shape", datainfo.source.beam_shape)
1011        size = doc.createElement("beam_size")
1012        if datainfo.source.beam_size_name is not None:
1013            size.setAttribute("name", str(datainfo.source.beam_size_name))
1014        written = write_node(doc, size, "x", datainfo.source.beam_size.x, {"unit":datainfo.source.beam_size_unit})
1015        written = written | write_node(doc, size, "y", datainfo.source.beam_size.y, {"unit":datainfo.source.beam_size_unit})
1016        written = written | write_node(doc, size, "z", datainfo.source.beam_size.z, {"unit":datainfo.source.beam_size_unit})
1017        if written == True:
1018            source.appendChild(size)
1019           
1020        write_node(doc, source, "wavelength", datainfo.source.wavelength, {"unit":datainfo.source.wavelength_unit})
1021        write_node(doc, source, "wavelength_min", datainfo.source.wavelength_min, {"unit":datainfo.source.wavelength_min_unit})
1022        write_node(doc, source, "wavelength_max", datainfo.source.wavelength_max, {"unit":datainfo.source.wavelength_max_unit})
1023        write_node(doc, source, "wavelength_spread", datainfo.source.wavelength_spread, {"unit":datainfo.source.wavelength_spread_unit})
1024       
1025        #   Collimation
1026        for item in datainfo.collimation:
1027            coll = doc.createElement("SAScollimation")
1028            if item.name is not None:
1029                coll.setAttribute("name", str(item.name))
1030            instr.appendChild(coll)
1031           
1032            write_node(doc, coll, "length", item.length, {"unit":item.length_unit})
1033           
1034            for apert in item.aperture:
1035                ap = doc.createElement("aperture")
1036                if apert.name is not None:
1037                    ap.setAttribute("name", str(apert.name))
1038                if apert.type is not None:
1039                    ap.setAttribute("type", str(apert.type))
1040                coll.appendChild(ap)
1041               
1042                write_node(doc, ap, "distance", apert.distance, {"unit":apert.distance_unit})
1043               
1044                size = doc.createElement("size")
1045                if apert.size_name is not None:
1046                    size.setAttribute("name", str(apert.size_name))
1047                written = write_node(doc, size, "x", apert.size.x, {"unit":apert.size_unit})
1048                written = written | write_node(doc, size, "y", apert.size.y, {"unit":apert.size_unit})
1049                written = written | write_node(doc, size, "z", apert.size.z, {"unit":apert.size_unit})
1050                if written == True:
1051                    ap.appendChild(size)
1052
1053        #   Detectors
1054        for item in datainfo.detector:
1055            det = doc.createElement("SASdetector")
1056            written = write_node(doc, det, "name", item.name)
1057            written = written | write_node(doc, det, "SDD", item.distance, {"unit":item.distance_unit})
1058            written = written | write_node(doc, det, "slit_length", item.slit_length, {"unit":item.slit_length_unit})
1059            if written == True:
1060                instr.appendChild(det)
1061           
1062            off = doc.createElement("offset")
1063            written = write_node(doc, off, "x", item.offset.x, {"unit":item.offset_unit})
1064            written = written | write_node(doc, off, "y", item.offset.y, {"unit":item.offset_unit})
1065            written = written | write_node(doc, off, "z", item.offset.z, {"unit":item.offset_unit})
1066            if written == True:
1067                det.appendChild(off)
1068           
1069            center = doc.createElement("beam_center")
1070            written = write_node(doc, center, "x", item.beam_center.x, {"unit":item.beam_center_unit})
1071            written = written | write_node(doc, center, "y", item.beam_center.y, {"unit":item.beam_center_unit})
1072            written = written | write_node(doc, center, "z", item.beam_center.z, {"unit":item.beam_center_unit})
1073            if written == True:
1074                det.appendChild(center)
1075               
1076            pix = doc.createElement("pixel_size")
1077            written = write_node(doc, pix, "x", item.pixel_size.x, {"unit":item.pixel_size_unit})
1078            written = written | write_node(doc, pix, "y", item.pixel_size.y, {"unit":item.pixel_size_unit})
1079            written = written | write_node(doc, pix, "z", item.pixel_size.z, {"unit":item.pixel_size_unit})
1080            if written == True:
1081                det.appendChild(pix)
1082               
1083            ori = doc.createElement("orientation")
1084            written = write_node(doc, ori, "roll",  item.orientation.x, {"unit":item.orientation_unit})
1085            written = written | write_node(doc, ori, "pitch", item.orientation.y, {"unit":item.orientation_unit})
1086            written = written | write_node(doc, ori, "yaw",   item.orientation.z, {"unit":item.orientation_unit})
1087            if written == True:
1088                det.appendChild(ori)
1089               
1090        # Processes info
1091        for item in datainfo.process:
1092            node = doc.createElement("SASprocess")
1093            entry_node.appendChild(node)
1094
1095            write_node(doc, node, "name", item.name)
1096            write_node(doc, node, "date", item.date)
1097            write_node(doc, node, "description", item.description)
1098            for term in item.term:
1099                value = term['value']
1100                del term['value']
1101                write_node(doc, node, "term", value, term)
1102            for note in item.notes:
1103                write_node(doc, node, "SASprocessnote", note)
1104        # Return the document, and the SASentry node associated with
1105        # the data we just wrote
1106        return doc, entry_node
[e9b12eaf]1107   
[61cada5]1108    def _parse_state(self, entry):
1109        """
[5062bbf]1110        Read a fit result from an XML node
1111       
1112        :param entry: XML node to read from
1113       
1114        :return: PageState object
[61cada5]1115        """
1116        # Create an empty state
[b35d3d1]1117        state = None   
[61cada5]1118        # Locate the P(r) node
1119        try:
1120            nodes = entry.xpath('ns:%s' % FITTING_NODE_NAME, namespaces={'ns': CANSAS_NS})
[b35d3d1]1121            if nodes !=[]:
1122                # Create an empty state
1123                state =  PageState()
1124                state.fromXML(node=nodes[0])
[61cada5]1125        except:
1126            logging.info("XML document does not contain fitting information.\n %s" % sys.exc_value)
1127           
1128        return state
1129   
[0b12abb5]1130   
[df7ed14]1131                   
1132    def _parse_entry(self, dom):
[e9b12eaf]1133        """
[5062bbf]1134        Parse a SASentry
1135       
1136        :param node: SASentry node
1137       
[df7ed14]1138        :return: Data1D/Data2D object
[5062bbf]1139       
[e9b12eaf]1140        """
[df7ed14]1141        node = dom.xpath('ns:data_class', namespaces={'ns': CANSAS_NS})
1142        if not node or node[0].text.lstrip().rstrip() != "Data2D":
1143            return CansasReader._parse_entry(self, dom)
1144       
1145        #Parse 2D
[e9b12eaf]1146        data_info = Data2D()
1147       
1148        # Look up title     
1149        self._store_content('ns:Title', dom, 'title', data_info)
1150       
1151        # Look up run number   
1152        nodes = dom.xpath('ns:Run', namespaces={'ns': CANSAS_NS})
1153        for item in nodes:   
1154            if item.text is not None:
1155                value = item.text.strip()
1156                if len(value) > 0:
1157                    data_info.run.append(value)
1158                    if item.get('name') is not None:
1159                        data_info.run_name[value] = item.get('name')
1160                           
1161        # Look up instrument name             
1162        self._store_content('ns:SASinstrument/ns:name', dom, 'instrument', data_info)
1163
1164        # Notes
1165        note_list = dom.xpath('ns:SASnote', namespaces={'ns': CANSAS_NS})
1166        for note in note_list:
1167            try:
1168                if note.text is not None:
1169                    note_value = note.text.strip()
1170                    if len(note_value) > 0:
1171                        data_info.notes.append(note_value)
1172            except:
1173                err_mess = "cansas_reader.read: error processing entry notes\n  %s" % sys.exc_value
1174                self.errors.append(err_mess)
1175                logging.error(err_mess)
1176       
1177        # Sample info ###################
1178        entry = get_content('ns:SASsample', dom)
1179        if entry is not None:
1180            data_info.sample.name = entry.get('name')
1181           
1182        self._store_content('ns:SASsample/ns:ID', 
1183                     dom, 'ID', data_info.sample)                   
1184        self._store_float('ns:SASsample/ns:thickness', 
1185                     dom, 'thickness', data_info.sample)
1186        self._store_float('ns:SASsample/ns:transmission', 
1187                     dom, 'transmission', data_info.sample)
1188        self._store_float('ns:SASsample/ns:temperature', 
1189                     dom, 'temperature', data_info.sample)
1190       
1191        nodes = dom.xpath('ns:SASsample/ns:details', namespaces={'ns': CANSAS_NS})
1192        for item in nodes:
1193            try:
1194                if item.text is not None:
1195                    detail_value = item.text.strip()
1196                    if len(detail_value) > 0:
1197                        data_info.sample.details.append(detail_value)
1198            except:
1199                err_mess = "cansas_reader.read: error processing sample details\n  %s" % sys.exc_value
1200                self.errors.append(err_mess)
1201                logging.error(err_mess)
1202       
1203        # Position (as a vector)
1204        self._store_float('ns:SASsample/ns:position/ns:x', 
1205                     dom, 'position.x', data_info.sample)         
1206        self._store_float('ns:SASsample/ns:position/ns:y', 
1207                     dom, 'position.y', data_info.sample)         
1208        self._store_float('ns:SASsample/ns:position/ns:z', 
1209                     dom, 'position.z', data_info.sample)         
1210       
1211        # Orientation (as a vector)
1212        self._store_float('ns:SASsample/ns:orientation/ns:roll', 
1213                     dom, 'orientation.x', data_info.sample)         
1214        self._store_float('ns:SASsample/ns:orientation/ns:pitch', 
1215                     dom, 'orientation.y', data_info.sample)         
1216        self._store_float('ns:SASsample/ns:orientation/ns:yaw', 
1217                     dom, 'orientation.z', data_info.sample)         
1218       
1219        # Source info ###################
1220        entry = get_content('ns:SASinstrument/ns:SASsource', dom)
1221        if entry is not None:
1222            data_info.source.name = entry.get('name')
1223       
1224        self._store_content('ns:SASinstrument/ns:SASsource/ns:radiation', 
1225                     dom, 'radiation', data_info.source)                   
1226        self._store_content('ns:SASinstrument/ns:SASsource/ns:beam_shape', 
1227                     dom, 'beam_shape', data_info.source)                   
1228        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength', 
1229                     dom, 'wavelength', data_info.source)         
1230        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_min', 
1231                     dom, 'wavelength_min', data_info.source)         
1232        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_max', 
1233                     dom, 'wavelength_max', data_info.source)         
1234        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_spread', 
1235                     dom, 'wavelength_spread', data_info.source)   
1236       
1237        # Beam size (as a vector)   
1238        entry = get_content('ns:SASinstrument/ns:SASsource/ns:beam_size', dom)
1239        if entry is not None:
1240            data_info.source.beam_size_name = entry.get('name')
1241           
1242        self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:x', 
1243                     dom, 'beam_size.x', data_info.source)   
1244        self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:y', 
1245                     dom, 'beam_size.y', data_info.source)   
1246        self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:z', 
1247                     dom, 'beam_size.z', data_info.source)   
1248       
1249        # Collimation info ###################
1250        nodes = dom.xpath('ns:SASinstrument/ns:SAScollimation', namespaces={'ns': CANSAS_NS})
1251        for item in nodes:
1252            collim = Collimation()
1253            if item.get('name') is not None:
1254                collim.name = item.get('name')
1255            self._store_float('ns:length', item, 'length', collim) 
1256           
1257            # Look for apertures
1258            apert_list = item.xpath('ns:aperture', namespaces={'ns': CANSAS_NS})
1259            for apert in apert_list:
1260                aperture =  Aperture()
1261               
1262                # Get the name and type of the aperture
1263                aperture.name = apert.get('name')
1264                aperture.type = apert.get('type')
1265                   
1266                self._store_float('ns:distance', apert, 'distance', aperture)   
1267               
1268                entry = get_content('ns:size', apert)
1269                if entry is not None:
1270                    aperture.size_name = entry.get('name')
1271               
1272                self._store_float('ns:size/ns:x', apert, 'size.x', aperture)   
1273                self._store_float('ns:size/ns:y', apert, 'size.y', aperture)   
1274                self._store_float('ns:size/ns:z', apert, 'size.z', aperture)
1275               
1276                collim.aperture.append(aperture)
1277               
1278            data_info.collimation.append(collim)
1279       
1280        # Detector info ######################
1281        nodes = dom.xpath('ns:SASinstrument/ns:SASdetector', namespaces={'ns': CANSAS_NS})
1282        for item in nodes:
1283           
1284            detector = Detector()
1285           
1286            self._store_content('ns:name', item, 'name', detector)
1287            self._store_float('ns:SDD', item, 'distance', detector)   
1288           
1289            # Detector offset (as a vector)
1290            self._store_float('ns:offset/ns:x', item, 'offset.x', detector)   
1291            self._store_float('ns:offset/ns:y', item, 'offset.y', detector)   
1292            self._store_float('ns:offset/ns:z', item, 'offset.z', detector)   
1293           
1294            # Detector orientation (as a vector)
1295            self._store_float('ns:orientation/ns:roll',  item, 'orientation.x', detector)   
1296            self._store_float('ns:orientation/ns:pitch', item, 'orientation.y', detector)   
1297            self._store_float('ns:orientation/ns:yaw',   item, 'orientation.z', detector)   
1298           
1299            # Beam center (as a vector)
1300            self._store_float('ns:beam_center/ns:x', item, 'beam_center.x', detector)   
1301            self._store_float('ns:beam_center/ns:y', item, 'beam_center.y', detector)   
1302            self._store_float('ns:beam_center/ns:z', item, 'beam_center.z', detector)   
1303           
1304            # Pixel size (as a vector)
1305            self._store_float('ns:pixel_size/ns:x', item, 'pixel_size.x', detector)   
1306            self._store_float('ns:pixel_size/ns:y', item, 'pixel_size.y', detector)   
1307            self._store_float('ns:pixel_size/ns:z', item, 'pixel_size.z', detector)   
1308           
1309            self._store_float('ns:slit_length', item, 'slit_length', detector)
1310           
1311            data_info.detector.append(detector)   
1312
1313        # Processes info ######################
1314        nodes = dom.xpath('ns:SASprocess', namespaces={'ns': CANSAS_NS})
1315        for item in nodes:
1316            process = Process()
1317            self._store_content('ns:name', item, 'name', process)
1318            self._store_content('ns:date', item, 'date', process)
1319            self._store_content('ns:description', item, 'description', process)
1320           
1321            term_list = item.xpath('ns:term', namespaces={'ns': CANSAS_NS})
1322            for term in term_list:
1323                try:
1324                    term_attr = {}
1325                    for attr in term.keys():
1326                        term_attr[attr] = term.get(attr).strip()
1327                    if term.text is not None:
1328                        term_attr['value'] = term.text.strip()
1329                        process.term.append(term_attr)
1330                except:
1331                    err_mess = "cansas_reader.read: error processing process term\n  %s" % sys.exc_value
1332                    self.errors.append(err_mess)
1333                    logging.error(err_mess)
1334           
1335            note_list = item.xpath('ns:SASprocessnote', namespaces={'ns': CANSAS_NS})
1336            for note in note_list:
1337                if note.text is not None:
1338                    process.notes.append(note.text.strip())
1339           
1340            data_info.process.append(process)
1341           
1342           
1343        # Data info ######################
1344        nodes = dom.xpath('ns:SASdata', namespaces={'ns': CANSAS_NS})
1345        if len(nodes)>1:
1346            raise RuntimeError, "CanSAS reader is not compatible with multiple SASdata entries"
[df7ed14]1347       
1348        for entry in nodes:
1349            for item in list_of_data_2d_attr:
1350                #get node
1351                node = get_content('ns:%s'%item[0], entry)
[0b12abb5]1352                exec "data_info.%s = parse_entry_helper(node, item)"%(item[1])
[e9b12eaf]1353                   
[df7ed14]1354            for item in list_of_data2d_values:
1355                field = get_content('ns:%s'%item[0], entry)
1356                list = []
1357                if field is not None:
[0b12abb5]1358                    list = [parse_entry_helper(node, item) for node in field]
[df7ed14]1359                exec "data_info.%s = numpy.array(list)"%item[0]
[e9b12eaf]1360       
1361        return data_info
1362
[61cada5]1363    def _read_cansas(self, path):
1364        """
[5062bbf]1365        Load data and P(r) information from a CanSAS XML file.
1366       
1367        :param path: file path
1368       
1369        :return: Data1D object if a single SASentry was found,
1370                    or a list of Data1D objects if multiple entries were found,
1371                    or None of nothing was found
1372                   
1373        :raise RuntimeError: when the file can't be opened
1374        :raise ValueError: when the length of the data vectors are inconsistent
1375       
[61cada5]1376        """
1377        output = []
[b63dc6e]1378        basename  = os.path.basename(path)
1379        root, extension = os.path.splitext(basename)
1380        ext = extension.lower()
[61cada5]1381        try:
1382            if os.path.isfile(path):
[b63dc6e]1383               
[61cada5]1384                #TODO: eventually remove the check for .xml once
1385                # the P(r) writer/reader is truly complete.
[b63dc6e]1386                if  ext in self.ext or \
1387                    ext == '.xml':
[61cada5]1388                   
1389                    tree = etree.parse(path, parser=etree.ETCompatXMLParser())
1390                    # Check the format version number
1391                    # Specifying the namespace will take care of the file format version
1392                    root = tree.getroot()
[df7ed14]1393                    entry_list = root.xpath('ns:SASentry', namespaces={'ns': CANSAS_NS})
[b35d3d1]1394                    for entry in entry_list:   
[e9b12eaf]1395                        try:
1396                            sas_entry = self._parse_entry(entry)
1397                        except:
[df7ed14]1398                            raise
[61cada5]1399                        fitstate = self._parse_state(entry)
[b35d3d1]1400                       
1401                        #state could be None when .svs file is loaded
1402                        #in this case, skip appending to output
1403                        if fitstate != None:
1404                            sas_entry.meta_data['fitstate'] = fitstate
1405                            sas_entry.filename = fitstate.file
1406                            output.append(sas_entry)
[61cada5]1407            else:
[b63dc6e]1408                self.call_back(format=ext)
[61cada5]1409                raise RuntimeError, "%s is not a file" % path
[b35d3d1]1410
[61cada5]1411            # Return output consistent with the loader's api
1412            if len(output)==0:
[b63dc6e]1413                self.call_back(state=None, datainfo=None,format=ext)
[61cada5]1414                return None
1415            else:
[b35d3d1]1416                for ind in range(len(output)):
1417                    # Call back to post the new state
1418                    state = output[ind].meta_data['fitstate']
1419                    t = time.localtime(state.timestamp)
1420                    time_str = time.strftime("%b %d %H:%M", t)
1421                    # Check that no time stamp is already appended
1422                    max_char = state.file.find("[")
1423                    if max_char < 0:
1424                        max_char = len(state.file)
[ef16f59]1425                    original_fname = state.file[0:max_char]
1426                    state.file = original_fname +' [' + time_str + ']'
[b35d3d1]1427                   
1428                       
1429                    if state is not None and state.is_data is not None:
1430                        exec 'output[%d].is_data = state.is_data'% ind
1431                     
1432                    output[ind].filename = state.file
1433                    state.data = output[ind]
1434                    state.data.name = output[ind].filename #state.data_name
1435                    state.data.id = state.data_id
1436                    if state.is_data is not None:
1437                        state.data.is_data = state.is_data
[ef16f59]1438                    if output[ind].run_name is not None and len(output[ind].run_name) != 0 :
1439                        name = output[ind].run_name
1440                    else: 
1441                        name=original_fname
1442                    state.data.group_id = name
1443                    #store state in fitting
[90a7bbd]1444                    self.call_back(state=state, datainfo=output[ind],format=ext)
[75fbd17]1445                    self.state= state
[ef16f59]1446                return output
[b35d3d1]1447             
[61cada5]1448        except:
[75fbd17]1449            #self.call_back(format=ext)
1450            self.state= state
[61cada5]1451            raise
1452           
1453    def write(self, filename, datainfo=None, fitstate=None):
1454        """
[b35d3d1]1455        Write the content of a Data1D as a CanSAS XML file only for standalone
[5062bbf]1456       
1457        :param filename: name of the file to write
1458        :param datainfo: Data1D object
1459        :param fitstate: PageState object
1460       
[61cada5]1461        """
1462        # Sanity check
1463        if self.cansas == True:
1464           
1465            # Add fitting information to the XML document
[ef16f59]1466            doc = self.write_toXML(datainfo, fitstate)
[61cada5]1467            # Write the XML document
1468            fd = open(filename, 'w')
1469            fd.write(doc.toprettyxml())
1470            fd.close()
1471        else:
1472            fitstate.toXML(file=filename)
1473       
[b35d3d1]1474    def write_toXML(self, datainfo=None, state=None):
1475        """
1476        Write toXML, a helper for write() , could be used by guimanager._on_save()
1477       
1478        : return: xml doc
1479        """
[3c44c66]1480
[b35d3d1]1481        if state.data is None:
1482            data = DataLoader.data_info.Data1D(x=[], y=[]) 
1483        else: 
1484            #make sure title and data run is filled up.
[028a0e8]1485            if state.data.title == None or state.data.title=='': state.data.title = state.data.name
1486            if state.data.run_name == None or state.data.run_name=={}: 
1487                state.data.run = [str(state.data.name)]
1488                state.data.run_name[0] = state.data.name
[b35d3d1]1489   
1490            if issubclass(state.data.__class__, DataLoader.data_info.Data1D):
[028a0e8]1491
[b35d3d1]1492                data = state.data
1493                doc, sasentry = self._to_xml_doc(data)
1494            else:
1495                data = state.data
1496                doc, sasentry = self._data2d_to_xml_doc(data)
1497           
1498
1499        if state is not None:
1500            state.toXML(doc=doc, file=data.name, entry_node=sasentry)
1501           
1502        return doc
[2296316]1503
1504   
1505# Simple html report templet 
1506HEADER = "<html>\n"
1507HEADER += "<head>\n"
1508HEADER += "<meta http-equiv=Content-Type content='text/html; "
1509HEADER += "charset=windows-1252'> \n"
1510HEADER += "<meta name=Generator >\n"
1511HEADER += "</head>\n"
1512HEADER += "<body lang=EN-US>\n"
1513HEADER += "<div class=WordSection1>\n"
1514HEADER += "<p class=MsoNormal><b><span ><center>"
1515HEADER += "%s</center></span></center></b></p>"
1516HEADER += "<p class=MsoNormal>&nbsp;</p>"
1517PARA = "<p class=MsoNormal> %s \n"
1518PARA += "</p>"
1519CENTRE = "<p class=MsoNormal><center> %s \n"
1520CENTRE += "</center></p>"
1521FEET_1 = \
1522"""
1523<p class=MsoNormal>&nbsp;</p>
1524<p class=MsoNormal><b><span ><center> Graph</span></center></b></p>
1525<p class=MsoNormal>&nbsp;</p> 
1526<center> 
1527<br>Model Computation<br> 
1528<br>Data: "%s"<br> 
1529"""
1530FEET_2 = \
1531"""
1532<img src="%s" > 
1533</img>
1534"""
1535FEET_3 = \
1536"""
1537</center>
1538</div>
1539</body>
1540</html>
1541"""
1542ELINE = "<p class=MsoNormal>&nbsp;</p>"
1543
1544 
[3c44c66]1545if __name__ == "__main__":
1546    state = PageState(parent=None)
1547    #state.toXML()
1548    """
[5062bbf]1549   
[3c44c66]1550    file = open("test_state", "w")
1551    pickle.dump(state, file)
1552    print pickle.dumps(state)
1553    state.data_name = "hello---->"
1554    pickle.dump(state, file)
1555    file = open("test_state", "r")
1556    new_state= pickle.load(file)
1557    print "new state", new_state
1558    new_state= pickle.load(file)
1559    print "new state", new_state
1560    #print "state", state
1561    """
1562    import bsddb
1563    import pickle
1564    db= bsddb.btopen('file_state.db', 'c')
1565    val = (pickle.dumps(state), "hello", "hi")
1566    db['state1']= pickle.dumps(val)
1567    print pickle.loads(db['state1'])
1568    state.data_name = "hello---->22"
1569    db['state2']= pickle.dumps(state)
1570    state.data_name = "hello---->2"
1571    db['state3']= pickle.dumps(state)
1572    del db['state3']
1573    state.data_name = "hello---->3"
1574    db['state4']= pickle.dumps(state)
1575    new_state = pickle.loads(db['state1'])
1576    #print db.last()
1577    db.set_location('state2')
1578    state.data_name = "hello---->5"
1579    db['aastate5']= pickle.dumps(state)
1580    db.keys().sort()
1581    print pickle.loads(db['state2'])
1582 
1583    db.close()
Note: See TracBrowser for help on using the repository browser.