source: sasview/src/sans/perspectives/fitting/pagestate.py @ 79d5b6c

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

Moving things around. Will definitely not build.

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