source: sasview/src/sas/perspectives/fitting/pagestate.py @ 379b87a

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 379b87a was b9a5f0e, checked in by krzywon, 10 years ago

90% complete with the conversion.

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