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

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 d06ae30 was d06ae30, checked in by gonzalezm, 9 years ago

Bug fix for ticket 263 about Report results reporting errors also for fixed parameters

  • Property mode set to 100644
File size: 73.6 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 the 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 == "selected":
511                if value == u' False':
512                    fixed_parameter = True
513                else:
514                    fixed_parameter = False
515            if name == "error value":
516                if fixed_parameter:
517                    param_string += '(fixed),'
518                else:
519                    param_string += value + ','
520            if name == "parameter unit":
521                param_string += value + ':'
522            if name == "Value of Chisqr ":
523                chi2 = ("Chi2/Npts = " + value)
524                chi2_string = CENTRE % chi2
525            if name == "multi_factor ":
526                muti_factor = ("muti_factor = " + value)
527                muti_factor_string = CENTRE % muti_factor
528            if name == "magentic_on ":
529                magentic_on = ("magentic_on = " + value)
530                if string(value) == 'True':
531                    magentic_on_string = CENTRE % magentic_on
532            if name == "Title":
533                if len(value.strip()) == 0:
534                    continue
535                title = value + " [" + repo_time + "]"
536                title_name = HEADER % title
537            if name == "data ":
538                try:
539                    file = ("File name:" + content[2])
540                    file_name = CENTRE % file
541                    if len(title) == 0:
542                        title = content[2] + " [" + repo_time + "]"
543                        title_name = HEADER % title
544                except:
545                    pass
546            if name == "model name ":
547                try:
548                    modelname = "Model name:" + content[1]
549                except:
550                    modelname = "Model name:" + " NAN"
551                model_name = CENTRE % modelname
552                 
553            if name == "Plotting Range":
554                try:
555                    q_range = content[1] + " = " + content[2] \
556                            + " = " + content[3].split(",")[0]
557                    q_name = ("Q Range:    " + q_range)
558                    q_range = CENTRE % q_name
559                except:
560                    pass
561        paramval = ""
562        for lines in param_string.split(":"):
563            line = lines.split(",")
564            if len(lines) > 0:
565                param = line[0]
566                param += " = " + line[1]
567                if len(line[2].split()) > 0 and not line[2].count("None"):
568                    param += " +- " + line[2]
569                if len(line[3].split()) > 0 and not line[3].count("None"):
570                    param += " " + line[3]
571                if not paramval.count(param):
572                    paramval += param + "\n"
573                    paramval_string += CENTRE % param + "\n"
574       
575        text_string = "\n\n\n" + title + "\n\n" + file + \
576                      "\n" + q_name + \
577                      "\n" + chi2 + \
578                      "\n\n" + paramval
579       
580        title_name = self._check_html_format(title_name)
581        file_name = self._check_html_format(file_name)
582        title = self._check_html_format(title)
583                                 
584        html_string = title_name + "\n" + file_name + \
585                                   "\n" + model_name + \
586                                   "\n" + q_range + \
587                                   "\n" + chi2_string + \
588                                   "\n" + ELINE + \
589                                   "\n" + paramval_string + \
590                                   "\n" + ELINE + \
591                                   "\n" + FEET_1 % title + \
592                                   "\n" + FEET_2
593                                                                     
594        return html_string, text_string, title
595   
596    def _check_html_format(self, name):
597        """
598        Check string '%' for html format
599        """
600        if name.count('%'):
601            name = name.replace('%', '&#37')
602       
603        return name
604   
605    def report(self, figs=None, canvases=None):
606        """
607        Invoke report dialog panel
608       
609        : param figs: list of pylab figures [list]
610        """
611        from sas.perspectives.fitting.report_dialog import ReportDialog
612        # get the strings for report
613        html_str, text_str, title = self.set_report_string()
614        # Allow 2 figures to append
615        if len(figs) == 1:
616            add_str = FEET_3
617        elif len(figs) == 2:
618            add_str = ELINE
619            add_str += FEET_2 % ("%s")
620            add_str += ELINE
621            add_str += FEET_3
622        elif len(figs) > 2:
623            add_str = ELINE
624            add_str += FEET_2 % ("%s")
625            add_str += ELINE
626            add_str += FEET_2 % ("%s")
627            add_str += ELINE
628            add_str += FEET_3
629        else:
630            add_str = ""
631
632        # final report html strings
633        report_str = html_str % ("%s") + add_str
634
635        # make plot image
636        images = self.set_plot_state(figs, canvases)
637        report_list = [report_str, text_str, images]
638        dialog = ReportDialog(report_list, None, -1, "")
639        dialog.ShowModal()
640       
641    def _toXML_helper(self, thelist, element, newdoc):
642        """
643        Helper method to create xml file for saving state
644        """
645        for item in thelist:
646            sub_element = newdoc.createElement('parameter')
647            sub_element.setAttribute('name', str(item[1]))
648            sub_element.setAttribute('value', str(item[2]))
649            sub_element.setAttribute('selected_to_fit', str(item[0]))
650            sub_element.setAttribute('error_displayed', str(item[4][0]))
651            sub_element.setAttribute('error_value', str(item[4][1]))
652            sub_element.setAttribute('minimum_displayed', str(item[5][0]))
653            sub_element.setAttribute('minimum_value', str(item[5][1]))
654            sub_element.setAttribute('maximum_displayed', str(item[6][0]))
655            sub_element.setAttribute('maximum_value', str(item[6][1]))
656            sub_element.setAttribute('unit', str(item[7]))
657            element.appendChild(sub_element)
658       
659    def toXML(self, file="fitting_state.fitv", doc=None, entry_node=None):
660        """
661        Writes the state of the InversionControl panel to file, as XML.
662       
663        Compatible with standalone writing, or appending to an
664        already existing XML document. In that case, the XML document
665        is required. An optional entry node in the XML document may also be given.
666       
667        :param file: file to write to
668        :param doc: XML document object [optional]
669        :param entry_node: XML node within the XML document at which we will append the data [optional]
670       
671        """
672        from xml.dom.minidom import getDOMImplementation
673
674        # Check whether we have to write a standalone XML file
675        if doc is None:
676            impl = getDOMImplementation()
677            doc_type = impl.createDocumentType(FITTING_NODE_NAME, "1.0", "1.0")
678            newdoc = impl.createDocument(None, FITTING_NODE_NAME, doc_type)
679            top_element = newdoc.documentElement
680        else:
681            # We are appending to an existing document
682            newdoc = doc
683            try:
684                top_element = newdoc.createElement(FITTING_NODE_NAME)
685            except:
686                string = etree.tostring(doc, pretty_print=True)
687                newdoc = parseString(string)
688                top_element = newdoc.createElement(FITTING_NODE_NAME)
689            if entry_node is None:
690                newdoc.documentElement.appendChild(top_element)
691            else:
692                try:
693                    entry_node.appendChild(top_element)
694                except:
695                    node_name = entry_node.tag
696                    node_list = newdoc.getElementsByTagName(node_name)
697                    entry_node = node_list.item(0)
698                    entry_node.appendChild(top_element)
699                   
700        attr = newdoc.createAttribute("version")
701        attr.nodeValue = '1.0'
702        top_element.setAttributeNode(attr)
703       
704        # File name
705        element = newdoc.createElement("filename")
706        if self.file is not None:
707            element.appendChild(newdoc.createTextNode(str(self.file)))
708        else:
709            element.appendChild(newdoc.createTextNode(str(file)))
710        top_element.appendChild(element)
711       
712        element = newdoc.createElement("timestamp")
713        element.appendChild(newdoc.createTextNode(time.ctime(self.timestamp)))
714        attr = newdoc.createAttribute("epoch")
715        attr.nodeValue = str(self.timestamp)
716        element.setAttributeNode(attr)
717        top_element.appendChild(element)
718        # Inputs
719        inputs = newdoc.createElement("Attributes")
720        top_element.appendChild(inputs)
721       
722        if self.data is not None and hasattr(self.data, "group_id"):
723            self.data_group_id = self.data.group_id
724        if self.data is not None and hasattr(self.data, "is_data"):
725            self.is_data = self.data.is_data
726        if self.data is not None:
727            self.data_name = self.data.name
728        if self.data is not None and hasattr(self.data, "id"):
729            self.data_id = self.data.id
730       
731        for item in list_of_data_attributes:
732            element = newdoc.createElement(item[0])
733            exec "element.setAttribute(item[0], str(self.%s))" % (item[1])
734            inputs.appendChild(element)
735       
736        for item in list_of_state_attributes:
737            element = newdoc.createElement(item[0])
738            exec "element.setAttribute(item[0], str(self.%s))" % (item[1])
739            inputs.appendChild(element)
740           
741        # For self.values ={ disp_param_name: [vals,...],...}
742        # and for self.weights ={ disp_param_name: [weights,...],...}
743        list = {}
744        for item in list_of_model_attributes:
745            element = newdoc.createElement(item[0])
746            exec "list = self.%s" % item[1]
747            for key, value in list.iteritems():
748                sub_element = newdoc.createElement(key)
749                sub_element.setAttribute('name', str(key))
750                for val in value:
751                    com = "sub_element.appendChild"
752                    com += "(newdoc.createTextNode(str(%s)))"
753                    exec com % val
754                   
755                element.appendChild(sub_element)
756            inputs.appendChild(element)
757       
758        # Create doc for the dictionary of self._disp_obj_dic
759        for item in list_of_obj_dic:
760            element = newdoc.createElement(item[0])
761            exec "list = self.%s" % item[1]
762            for key, val in list.iteritems():
763                value = repr(val)
764                sub_element = newdoc.createElement(key)
765                sub_element.setAttribute('name', str(key))
766                sub_element.setAttribute('value', str(value))
767                element.appendChild(sub_element)
768            inputs.appendChild(element)
769                 
770        for item in list_of_state_parameters:
771            element = newdoc.createElement(item[0])
772            com = "self._toXML_helper(thelist=self.%s,"
773            com += " element=element, newdoc=newdoc)"
774            exec com % item[1]
775            inputs.appendChild(element)
776       
777        # Save the file
778        if doc is None:
779            fd = open(file, 'w')
780            fd.write(newdoc.toprettyxml())
781            fd.close()
782            return None
783        else:
784            return newdoc
785       
786    def _fromXML_helper(self, node, list):
787        """
788        Helper function to write state to xml
789        """
790        for item in node:
791            try:
792                name = item.get('name')
793            except:
794                name = None
795            try:
796                value = item.get('value')
797            except:
798                value = None
799            try:
800                selected_to_fit = (item.get('selected_to_fit') == "True")
801            except:
802                selected_to_fit = None
803            try:
804                error_displayed = (item.get('error_displayed') == "True")
805            except:
806                error_displayed = None
807            try:
808                error_value = item.get('error_value')
809            except:
810                error_value = None
811            try:
812                minimum_displayed = (item.get('minimum_displayed') == "True")
813            except:
814                minimum_displayed = None
815            try:
816                minimum_value = item.get('minimum_value')
817            except:
818                minimum_value = None
819            try:
820                maximum_displayed = (item.get('maximum_displayed') == "True")
821            except:
822                maximum_displayed = None
823            try:
824                maximum_value = item.get('maximum_value')
825            except:
826                maximum_value = None
827            try:
828                unit = item.get('unit')
829            except:
830                unit = None
831            list.append([selected_to_fit, name, value, "+/-",
832                         [error_displayed, error_value],
833                         [minimum_displayed, minimum_value],
834                         [maximum_displayed, maximum_value], unit])
835       
836    def fromXML(self, file=None, node=None):
837        """
838        Load fitting state from a file
839       
840        :param file: .fitv file
841        :param node: node of a XML document to read from
842       
843        """
844        if file is not None:
845            msg = "PageState no longer supports non-CanSAS"
846            msg += " format for fitting files"
847            raise RuntimeError, msg
848           
849        if node.get('version')and node.get('version') == '1.0':
850           
851            # Get file name
852            entry = get_content('ns:filename', node)
853            if entry is not None:
854                self.file = entry.text.strip()
855               
856            # Get time stamp
857            entry = get_content('ns:timestamp', node)
858            if entry is not None and entry.get('epoch'):
859                try:
860                    self.timestamp = float(entry.get('epoch'))
861                except:
862                    msg = "PageState.fromXML: Could not"
863                    msg += " read timestamp\n %s" % sys.exc_value
864                    logging.error(msg)
865           
866            # Parse fitting attributes
867            entry = get_content('ns:Attributes', node)
868            for item in list_of_data_attributes:
869                node = get_content('ns:%s' % item[0], entry)
870                try:
871                    exec "self.%s = parse_entry_helper(node, item)" % item[0]
872                   
873                except:
874                    raise
875
876            if entry is not None:
877               
878                for item in list_of_state_attributes:
879                    node = get_content('ns:%s' % item[0], entry)
880                    try:
881                        exec "self.%s = parse_entry_helper(node, item)" % \
882                                                                str(item[0])
883                    except:
884                        raise
885                   
886                for item in list_of_state_parameters:
887                    node = get_content("ns:%s" % item[0], entry)
888                    exec "self._fromXML_helper(node=node, list=self.%s)" % \
889                                                                    item[1]
890               
891                # Recover _disp_obj_dict from xml file
892                self._disp_obj_dict = {}
893                disp_model = None
894                for item in list_of_obj_dic:
895                    # Get node
896                    node = get_content("ns:%s" % item[0], entry)
897                    for attr in node:
898                        name = attr.get('name')
899                        val = attr.get('value')
900                        value = val.split(" instance")[0]
901                        disp_name = value.split("<")[1]
902                        try:
903                            # Try to recover disp_model object from strings
904                            com = "from sas.models.dispersion_models "
905                            com += "import %s as disp"
906                            com_name = disp_name.split(".")[3]
907                            exec com % com_name
908                            disp_model = disp()
909                            exec "self.%s['%s'] = com_name" % (item[1], name)
910                        except:
911                            pass
912                       
913                # get self.values and self.weights dic. if exists
914                for item in list_of_model_attributes:
915                    node = get_content("ns:%s" % item[0], entry)
916                    dic = {}
917                    list = []
918                    for par in node:
919                        name = par.get('name')
920                        values = par.text.split('\n')
921                        # Get lines only with numbers
922                        for line in values:
923                            try:
924                                val = float(line)
925                                list.append(val)
926                            except:
927                                # pass if line is empty (it happens)
928                                pass
929                    dic[name] = numpy.array(list)
930                    exec "self.%s = dic" % item[1]
931                   
932    def set_plot_state(self, figs, canvases):
933        """
934        Build image state that wx.html understand
935        by plotting, putting it into wx.FileSystem image object
936
937        """
938        images = []
939        # some imports
940        import wx
941
942        # Reset memory
943        self.imgRAM = None
944        wx.MemoryFSHandler()
945       
946        # For no figures in the list, prepare empty plot
947        if figs == None or len(figs) == 0:
948            figs = [None]
949           
950        # Loop over the list of figures
951        # use wx.MemoryFSHandler
952        self.imgRAM = wx.MemoryFSHandler()
953        for fig in figs:
954            if figs != None:
955                ind = figs.index(fig)
956                canvas = canvases[ind]
957               
958            #store the image in wx.FileSystem Object
959            wx.FileSystem.AddHandler(wx.MemoryFSHandler())
960           
961            # index of the fig
962            ind = figs.index(fig)
963           
964            #AddFile, image can be retrieved with 'memory:filename'
965            self.imgRAM.AddFile('img_fit%s.png' % ind,
966                                canvas.bitmap, wx.BITMAP_TYPE_PNG)
967           
968            #append figs
969            images.append(fig)
970           
971        return images
972
973
974class Reader(CansasReader):
975    """
976    Class to load a .fitv fitting file
977    """
978    ## File type
979    type_name = "Fitting"
980   
981    ## Wildcards
982    type = ["Fitting files (*.fitv)|*.fitv"
983            "SASView file (*.svs)|*.svs"]
984    ## List of allowed extensions
985    ext = ['.fitv', '.FITV', '.svs', 'SVS']
986   
987    def __init__(self, call_back=None, cansas=True):
988        CansasReader.__init__(self)
989        """
990        Initialize the call-back method to be called
991        after we load a file
992       
993        :param call_back: call-back method
994        :param cansas:  True = files will be written/read in CanSAS format
995                        False = write CanSAS format
996           
997        """
998        ## Call back method to be executed after a file is read
999        self.call_back = call_back
1000        ## CanSAS format flag
1001        self.cansas = cansas
1002        self.state = None
1003       
1004    def get_state(self):
1005        return self.state
1006       
1007    def read(self, path):
1008        """
1009        Load a new P(r) inversion state from file
1010       
1011        :param path: file path
1012       
1013        """
1014        if self.cansas == True:
1015            return self._read_cansas(path)
1016     
1017    def _data2d_to_xml_doc(self, datainfo):
1018        """
1019        Create an XML document to contain the content of a Data2D
1020       
1021        :param datainfo: Data2D object
1022       
1023        """
1024        if not issubclass(datainfo.__class__, Data2D):
1025            raise RuntimeError, "The cansas writer expects a Data2D instance"
1026       
1027        doc = xml.dom.minidom.Document()
1028        main_node = doc.createElement("SASroot")
1029        main_node.setAttribute("version", self.version)
1030        main_node.setAttribute("xmlns", "cansas1d/%s" % self.version)
1031        main_node.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance")
1032        main_node.setAttribute("xsi:schemaLocation", "cansas1d/%s http://svn.smallangles.net/svn/canSAS/1dwg/trunk/cansas1d.xsd" % self.version)
1033       
1034        doc.appendChild(main_node)
1035       
1036        entry_node = doc.createElement("SASentry")
1037        main_node.appendChild(entry_node)
1038       
1039        write_node(doc, entry_node, "Title", datainfo.title)
1040        if datainfo is not None:
1041            write_node(doc, entry_node, "data_class", datainfo.__class__.__name__)
1042        for item in datainfo.run:
1043            runname = {}
1044            if datainfo.run_name.has_key(item) and len(str(datainfo.run_name[item]))>1:
1045                runname = {'name': datainfo.run_name[item] }
1046            write_node(doc, entry_node, "Run", item, runname)
1047        # Data info
1048        new_node = doc.createElement("SASdata")
1049        entry_node.appendChild(new_node)
1050        for item in list_of_data_2d_attr:
1051            element = doc.createElement(item[0])
1052            exec "element.setAttribute(item[0], str(datainfo.%s))" % (item[1])
1053            new_node.appendChild(element)
1054           
1055        for item in list_of_data2d_values:
1056            root_node = doc.createElement(item[0])
1057            new_node.appendChild(root_node)
1058            temp_list = None
1059            exec "temp_list = datainfo.%s" % item[1]
1060
1061            if temp_list is None or len(temp_list)== 0:
1062                element = doc.createElement(item[0])
1063                exec "element.appendChild(doc.createTextNode(str(%s)))" % temp_list
1064                root_node.appendChild(element)
1065            else:
1066                for value in temp_list:
1067                    element = doc.createElement(item[0])
1068                    exec "element.setAttribute(item[0], str(%s))" % value
1069                    root_node.appendChild(element)
1070       
1071        # Sample info
1072        sample = doc.createElement("SASsample")
1073        if datainfo.sample.name is not None:
1074            sample.setAttribute("name", str(datainfo.sample.name))
1075        entry_node.appendChild(sample)
1076        write_node(doc, sample, "ID", str(datainfo.sample.ID))
1077        write_node(doc, sample, "thickness", datainfo.sample.thickness,
1078                   {"unit": datainfo.sample.thickness_unit})
1079        write_node(doc, sample, "transmission", datainfo.sample.transmission)
1080        write_node(doc, sample, "temperature", datainfo.sample.temperature,
1081                   {"unit": datainfo.sample.temperature_unit})
1082       
1083        for item in datainfo.sample.details:
1084            write_node(doc, sample, "details", item)
1085       
1086        pos = doc.createElement("position")
1087        written = write_node(doc, pos, "x", datainfo.sample.position.x,
1088                             {"unit": datainfo.sample.position_unit})
1089        written = written | write_node(doc, pos, "y",
1090                                       datainfo.sample.position.y,
1091                                       {"unit": datainfo.sample.position_unit})
1092        written = written | write_node(doc, pos, "z",
1093                                       datainfo.sample.position.z,
1094                                       {"unit": datainfo.sample.position_unit})
1095        if written == True:
1096            sample.appendChild(pos)
1097       
1098        ori = doc.createElement("orientation")
1099        written = write_node(doc, ori, "roll", datainfo.sample.orientation.x,
1100                             {"unit": datainfo.sample.orientation_unit})
1101        written = written | write_node(doc, ori, "pitch",
1102                                       datainfo.sample.orientation.y,
1103                                       {"unit": datainfo.sample.orientation_unit})
1104        written = written | write_node(doc, ori, "yaw",
1105                                       datainfo.sample.orientation.z,
1106                                       {"unit": datainfo.sample.orientation_unit})
1107        if written == True:
1108            sample.appendChild(ori)
1109       
1110        # Instrument info
1111        instr = doc.createElement("SASinstrument")
1112        entry_node.appendChild(instr)
1113       
1114        write_node(doc, instr, "name", datainfo.instrument)
1115       
1116        #   Source
1117        source = doc.createElement("SASsource")
1118        if datainfo.source.name is not None:
1119            source.setAttribute("name", str(datainfo.source.name))
1120        instr.appendChild(source)
1121       
1122        write_node(doc, source, "radiation", datainfo.source.radiation)
1123        write_node(doc, source, "beam_shape", datainfo.source.beam_shape)
1124        size = doc.createElement("beam_size")
1125        if datainfo.source.beam_size_name is not None:
1126            size.setAttribute("name", str(datainfo.source.beam_size_name))
1127        written = write_node(doc, size, "x", datainfo.source.beam_size.x,
1128                             {"unit": datainfo.source.beam_size_unit})
1129        written = written | write_node(doc, size, "y",
1130                                       datainfo.source.beam_size.y,
1131                                       {"unit": datainfo.source.beam_size_unit})
1132        written = written | write_node(doc, size, "z",
1133                                       datainfo.source.beam_size.z,
1134                                       {"unit": datainfo.source.beam_size_unit})
1135        if written == True:
1136            source.appendChild(size)
1137           
1138        write_node(doc, source, "wavelength", datainfo.source.wavelength,
1139                   {"unit": datainfo.source.wavelength_unit})
1140        write_node(doc, source, "wavelength_min",
1141                   datainfo.source.wavelength_min,
1142                   {"unit": datainfo.source.wavelength_min_unit})
1143        write_node(doc, source, "wavelength_max",
1144                   datainfo.source.wavelength_max,
1145                   {"unit": datainfo.source.wavelength_max_unit})
1146        write_node(doc, source, "wavelength_spread",
1147                   datainfo.source.wavelength_spread,
1148                   {"unit": datainfo.source.wavelength_spread_unit})
1149       
1150        #   Collimation
1151        for item in datainfo.collimation:
1152            coll = doc.createElement("SAScollimation")
1153            if item.name is not None:
1154                coll.setAttribute("name", str(item.name))
1155            instr.appendChild(coll)
1156           
1157            write_node(doc, coll, "length", item.length,
1158                       {"unit": item.length_unit})
1159           
1160            for apert in item.aperture:
1161                ap = doc.createElement("aperture")
1162                if apert.name is not None:
1163                    ap.setAttribute("name", str(apert.name))
1164                if apert.type is not None:
1165                    ap.setAttribute("type", str(apert.type))
1166                coll.appendChild(ap)
1167               
1168                write_node(doc, ap, "distance", apert.distance,
1169                           {"unit": apert.distance_unit})
1170               
1171                size = doc.createElement("size")
1172                if apert.size_name is not None:
1173                    size.setAttribute("name", str(apert.size_name))
1174                written = write_node(doc, size, "x", apert.size.x,
1175                                     {"unit": apert.size_unit})
1176                written = written | write_node(doc, size, "y", apert.size.y,
1177                                               {"unit": apert.size_unit})
1178                written = written | write_node(doc, size, "z", apert.size.z,
1179                                               {"unit": apert.size_unit})
1180                if written == True:
1181                    ap.appendChild(size)
1182
1183        #   Detectors
1184        for item in datainfo.detector:
1185            det = doc.createElement("SASdetector")
1186            written = write_node(doc, det, "name", item.name)
1187            written = written | write_node(doc, det, "SDD", item.distance,
1188                                           {"unit": item.distance_unit})
1189            written = written | write_node(doc, det, "slit_length",
1190                                           item.slit_length,
1191                                           {"unit": item.slit_length_unit})
1192            if written == True:
1193                instr.appendChild(det)
1194           
1195            off = doc.createElement("offset")
1196            written = write_node(doc, off, "x", item.offset.x,
1197                                 {"unit": item.offset_unit})
1198            written = written | write_node(doc, off, "y", item.offset.y,
1199                                           {"unit": item.offset_unit})
1200            written = written | write_node(doc, off, "z", item.offset.z,
1201                                           {"unit": item.offset_unit})
1202            if written == True:
1203                det.appendChild(off)
1204           
1205            center = doc.createElement("beam_center")
1206            written = write_node(doc, center, "x", item.beam_center.x,
1207                                 {"unit": item.beam_center_unit})
1208            written = written | write_node(doc, center, "y",
1209                                           item.beam_center.y,
1210                                           {"unit": item.beam_center_unit})
1211            written = written | write_node(doc, center, "z",
1212                                           item.beam_center.z,
1213                                           {"unit": item.beam_center_unit})
1214            if written == True:
1215                det.appendChild(center)
1216               
1217            pix = doc.createElement("pixel_size")
1218            written = write_node(doc, pix, "x", item.pixel_size.x,
1219                                 {"unit": item.pixel_size_unit})
1220            written = written | write_node(doc, pix, "y", item.pixel_size.y,
1221                                           {"unit": item.pixel_size_unit})
1222            written = written | write_node(doc, pix, "z", item.pixel_size.z,
1223                                           {"unit": item.pixel_size_unit})
1224            if written == True:
1225                det.appendChild(pix)
1226               
1227            ori = doc.createElement("orientation")
1228            written = write_node(doc, ori, "roll", item.orientation.x,
1229                                 {"unit": item.orientation_unit})
1230            written = written | write_node(doc, ori, "pitch",
1231                                           item.orientation.y,
1232                                           {"unit": item.orientation_unit})
1233            written = written | write_node(doc, ori, "yaw", item.orientation.z,
1234                                           {"unit": item.orientation_unit})
1235            if written == True:
1236                det.appendChild(ori)
1237               
1238        # Processes info
1239        for item in datainfo.process:
1240            node = doc.createElement("SASprocess")
1241            entry_node.appendChild(node)
1242
1243            write_node(doc, node, "name", item.name)
1244            write_node(doc, node, "date", item.date)
1245            write_node(doc, node, "description", item.description)
1246            for term in item.term:
1247                value = term['value']
1248                del term['value']
1249                write_node(doc, node, "term", value, term)
1250            for note in item.notes:
1251                write_node(doc, node, "SASprocessnote", note)
1252        # Return the document, and the SASentry node associated with
1253        # the data we just wrote
1254        return doc, entry_node
1255   
1256    def _parse_state(self, entry):
1257        """
1258        Read a fit result from an XML node
1259       
1260        :param entry: XML node to read from
1261       
1262        :return: PageState object
1263        """
1264        # Create an empty state
1265        state = None   
1266        # Locate the P(r) node
1267        try:
1268            nodes = entry.xpath('ns:%s' % FITTING_NODE_NAME,
1269                                namespaces={'ns': CANSAS_NS})
1270            if nodes != []:
1271                # Create an empty state
1272                state = PageState()
1273                state.fromXML(node=nodes[0])
1274               
1275        except:
1276            logging.info("XML document does not contain fitting information.\n %s" % sys.exc_value)
1277           
1278        return state
1279     
1280    def _parse_save_state_entry(self, dom):
1281        """
1282        Parse a SASentry
1283       
1284        :param node: SASentry node
1285       
1286        :return: Data1D/Data2D object
1287       
1288        """
1289        node = dom.xpath('ns:data_class', namespaces={'ns': CANSAS_NS})
1290        if not node or node[0].text.lstrip().rstrip() != "Data2D":
1291            return_value, _ = self._parse_entry(dom)
1292            numpy.trim_zeros(return_value.x)
1293            numpy.trim_zeros(return_value.y)
1294            numpy.trim_zeros(return_value.dy)
1295            size_dx = return_value.dx.size
1296            size_dxl = return_value.dxl.size
1297            size_dxw = return_value.dxw.size
1298            if size_dxl == 0 and size_dxw == 0:
1299                return_value.dxl = None
1300                return_value.dxw = None
1301                numpy.trim_zeros(return_value.dx)
1302            elif size_dx == 0:
1303                return_value.dx = None
1304                size_dx = size_dxl
1305                numpy.trim_zeros(return_value.dxl)
1306                numpy.trim_zeros(return_value.dxw)
1307                           
1308            return return_value, _
1309       
1310        #Parse 2D
1311        data_info = Data2D()
1312       
1313        # Look up title
1314        self._store_content('ns:Title', dom, 'title', data_info)
1315       
1316        # Look up run number
1317        nodes = dom.xpath('ns:Run', namespaces={'ns': CANSAS_NS})
1318        for item in nodes:
1319            if item.text is not None:
1320                value = item.text.strip()
1321                if len(value) > 0:
1322                    data_info.run.append(value)
1323                    if item.get('name') is not None:
1324                        data_info.run_name[value] = item.get('name')
1325                           
1326        # Look up instrument name
1327        self._store_content('ns:SASinstrument/ns:name', dom,
1328                            'instrument', data_info)
1329
1330        # Notes
1331        note_list = dom.xpath('ns:SASnote', namespaces={'ns': CANSAS_NS})
1332        for note in note_list:
1333            try:
1334                if note.text is not None:
1335                    note_value = note.text.strip()
1336                    if len(note_value) > 0:
1337                        data_info.notes.append(note_value)
1338            except:
1339                err_mess = "cansas_reader.read: error processing entry notes\n  %s" % sys.exc_value
1340                self.errors.append(err_mess)
1341                logging.error(err_mess)
1342       
1343        # Sample info ###################
1344        entry = get_content('ns:SASsample', dom)
1345        if entry is not None:
1346            data_info.sample.name = entry.get('name')
1347           
1348        self._store_content('ns:SASsample/ns:ID',
1349                     dom, 'ID', data_info.sample)
1350        self._store_float('ns:SASsample/ns:thickness',
1351                     dom, 'thickness', data_info.sample)
1352        self._store_float('ns:SASsample/ns:transmission',
1353                     dom, 'transmission', data_info.sample)
1354        self._store_float('ns:SASsample/ns:temperature',
1355                     dom, 'temperature', data_info.sample)
1356       
1357        nodes = dom.xpath('ns:SASsample/ns:details',
1358                          namespaces={'ns': CANSAS_NS})
1359        for item in nodes:
1360            try:
1361                if item.text is not None:
1362                    detail_value = item.text.strip()
1363                    if len(detail_value) > 0:
1364                        data_info.sample.details.append(detail_value)
1365            except:
1366                err_mess = "cansas_reader.read: error processing sample details\n  %s" % sys.exc_value
1367                self.errors.append(err_mess)
1368                logging.error(err_mess)
1369       
1370        # Position (as a vector)
1371        self._store_float('ns:SASsample/ns:position/ns:x',
1372                     dom, 'position.x', data_info.sample)
1373        self._store_float('ns:SASsample/ns:position/ns:y',
1374                     dom, 'position.y', data_info.sample)
1375        self._store_float('ns:SASsample/ns:position/ns:z',
1376                     dom, 'position.z', data_info.sample)
1377       
1378        # Orientation (as a vector)
1379        self._store_float('ns:SASsample/ns:orientation/ns:roll',
1380                     dom, 'orientation.x', data_info.sample)
1381        self._store_float('ns:SASsample/ns:orientation/ns:pitch',
1382                     dom, 'orientation.y', data_info.sample)
1383        self._store_float('ns:SASsample/ns:orientation/ns:yaw',
1384                     dom, 'orientation.z', data_info.sample)
1385       
1386        # Source info ###################
1387        entry = get_content('ns:SASinstrument/ns:SASsource', dom)
1388        if entry is not None:
1389            data_info.source.name = entry.get('name')
1390       
1391        self._store_content('ns:SASinstrument/ns:SASsource/ns:radiation',
1392                     dom, 'radiation', data_info.source)
1393        self._store_content('ns:SASinstrument/ns:SASsource/ns:beam_shape',
1394                     dom, 'beam_shape', data_info.source)
1395        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength',
1396                     dom, 'wavelength', data_info.source)
1397        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_min',
1398                     dom, 'wavelength_min', data_info.source)
1399        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_max',
1400                     dom, 'wavelength_max', data_info.source)
1401        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_spread',
1402                     dom, 'wavelength_spread', data_info.source)
1403       
1404        # Beam size (as a vector)
1405        entry = get_content('ns:SASinstrument/ns:SASsource/ns:beam_size', dom)
1406        if entry is not None:
1407            data_info.source.beam_size_name = entry.get('name')
1408           
1409        self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:x',
1410                     dom, 'beam_size.x', data_info.source)
1411        self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:y',
1412                     dom, 'beam_size.y', data_info.source)
1413        self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:z',
1414                     dom, 'beam_size.z', data_info.source)
1415       
1416        # Collimation info ###################
1417        nodes = dom.xpath('ns:SASinstrument/ns:SAScollimation',
1418                          namespaces={'ns': CANSAS_NS})
1419        for item in nodes:
1420            collim = Collimation()
1421            if item.get('name') is not None:
1422                collim.name = item.get('name')
1423            self._store_float('ns:length', item, 'length', collim)
1424           
1425            # Look for apertures
1426            apert_list = item.xpath('ns:aperture',
1427                                    namespaces={'ns': CANSAS_NS})
1428            for apert in apert_list:
1429                aperture = Aperture()
1430               
1431                # Get the name and type of the aperture
1432                aperture.name = apert.get('name')
1433                aperture.type = apert.get('type')
1434                   
1435                self._store_float('ns:distance', apert, 'distance', aperture)
1436               
1437                entry = get_content('ns:size', apert)
1438                if entry is not None:
1439                    aperture.size_name = entry.get('name')
1440               
1441                self._store_float('ns:size/ns:x', apert, 'size.x', aperture)
1442                self._store_float('ns:size/ns:y', apert, 'size.y', aperture)
1443                self._store_float('ns:size/ns:z', apert, 'size.z', aperture)
1444               
1445                collim.aperture.append(aperture)
1446               
1447            data_info.collimation.append(collim)
1448       
1449        # Detector info ######################
1450        nodes = dom.xpath('ns:SASinstrument/ns:SASdetector',
1451                          namespaces={'ns': CANSAS_NS})
1452        for item in nodes:
1453           
1454            detector = Detector()
1455           
1456            self._store_content('ns:name', item, 'name', detector)
1457            self._store_float('ns:SDD', item, 'distance', detector)
1458           
1459            # Detector offset (as a vector)
1460            self._store_float('ns:offset/ns:x', item, 'offset.x', detector)
1461            self._store_float('ns:offset/ns:y', item, 'offset.y', detector)
1462            self._store_float('ns:offset/ns:z', item, 'offset.z', detector)
1463           
1464            # Detector orientation (as a vector)
1465            self._store_float('ns:orientation/ns:roll', item,
1466                              'orientation.x', detector)
1467            self._store_float('ns:orientation/ns:pitch', item,
1468                              'orientation.y', detector)
1469            self._store_float('ns:orientation/ns:yaw', item,
1470                              'orientation.z', detector)
1471           
1472            # Beam center (as a vector)
1473            self._store_float('ns:beam_center/ns:x', item,
1474                              'beam_center.x', detector)
1475            self._store_float('ns:beam_center/ns:y', item,
1476                              'beam_center.y', detector)
1477            self._store_float('ns:beam_center/ns:z', item,
1478                              'beam_center.z', detector)
1479           
1480            # Pixel size (as a vector)
1481            self._store_float('ns:pixel_size/ns:x', item,
1482                              'pixel_size.x', detector)
1483            self._store_float('ns:pixel_size/ns:y', item,
1484                              'pixel_size.y', detector)
1485            self._store_float('ns:pixel_size/ns:z', item,
1486                              'pixel_size.z', detector)
1487           
1488            self._store_float('ns:slit_length', item, 'slit_length', detector)
1489           
1490            data_info.detector.append(detector)
1491
1492        # Processes info ######################
1493        nodes = dom.xpath('ns:SASprocess', namespaces={'ns': CANSAS_NS})
1494        for item in nodes:
1495            process = Process()
1496            self._store_content('ns:name', item, 'name', process)
1497            self._store_content('ns:date', item, 'date', process)
1498            self._store_content('ns:description', item, 'description', process)
1499           
1500            term_list = item.xpath('ns:term', namespaces={'ns': CANSAS_NS})
1501            for term in term_list:
1502                try:
1503                    term_attr = {}
1504                    for attr in term.keys():
1505                        term_attr[attr] = term.get(attr).strip()
1506                    if term.text is not None:
1507                        term_attr['value'] = term.text.strip()
1508                        process.term.append(term_attr)
1509                except:
1510                    err_mess = "cansas_reader.read: error processing process term\n  %s" % sys.exc_value
1511                    self.errors.append(err_mess)
1512                    logging.error(err_mess)
1513           
1514            note_list = item.xpath('ns:SASprocessnote',
1515                                   namespaces={'ns': CANSAS_NS})
1516            for note in note_list:
1517                if note.text is not None:
1518                    process.notes.append(note.text.strip())
1519           
1520            data_info.process.append(process)
1521           
1522        # Data info ######################
1523        nodes = dom.xpath('ns:SASdata', namespaces={'ns': CANSAS_NS})
1524        if len(nodes) > 1:
1525            raise RuntimeError, "CanSAS reader is not compatible with multiple SASdata entries"
1526       
1527        for entry in nodes:
1528            for item in list_of_data_2d_attr:
1529                #get node
1530                node = get_content('ns:%s' % item[0], entry)
1531                exec "data_info.%s = parse_entry_helper(node, item)" % item[1]
1532                   
1533            for item in list_of_data2d_values:
1534                field = get_content('ns:%s' % item[0], entry)
1535                list = []
1536                if field is not None:
1537                    list = [parse_entry_helper(node, item) for node in field]
1538                if len(list) < 2:
1539                    exec "data_info.%s = None" % item[0]
1540                else:
1541                    exec "data_info.%s = numpy.array(list)" % item[0]
1542       
1543        return data_info
1544
1545    def _read_cansas(self, path):
1546        """
1547        Load data and P(r) information from a CanSAS XML file.
1548       
1549        :param path: file path
1550       
1551        :return: Data1D object if a single SASentry was found,
1552                    or a list of Data1D objects if multiple entries were found,
1553                    or None of nothing was found
1554                   
1555        :raise RuntimeError: when the file can't be opened
1556        :raise ValueError: when the length of the data vectors are inconsistent
1557       
1558        """
1559        output = []
1560        basename = os.path.basename(path)
1561        root, extension = os.path.splitext(basename)
1562        ext = extension.lower()
1563        try:
1564            if os.path.isfile(path):
1565               
1566                #TODO: eventually remove the check for .xml once
1567                # the P(r) writer/reader is truly complete.
1568                if  ext in self.ext or \
1569                    ext == '.xml':
1570                   
1571                    tree = etree.parse(path, parser=etree.ETCompatXMLParser())
1572                    # Check the format version number
1573                    # Specifying the namespace will take care of the file format version
1574                    root = tree.getroot()
1575                    entry_list = root.xpath('ns:SASentry',
1576                                            namespaces={'ns': CANSAS_NS})
1577                    for entry in entry_list:
1578                        try:
1579                            sas_entry, _ = self._parse_save_state_entry(entry)
1580                        except:
1581                            raise
1582                        fitstate = self._parse_state(entry)
1583                       
1584                        #state could be None when .svs file is loaded
1585                        #in this case, skip appending to output
1586                        if fitstate != None:
1587                            sas_entry.meta_data['fitstate'] = fitstate
1588                            sas_entry.filename = fitstate.file
1589                            output.append(sas_entry)
1590            else:
1591                self.call_back(format=ext)
1592                raise RuntimeError, "%s is not a file" % path
1593
1594            # Return output consistent with the loader's api
1595            if len(output) == 0:
1596                self.call_back(state=None, datainfo=None, format=ext)
1597                return None
1598            else:
1599                for ind in range(len(output)):
1600                    # Call back to post the new state
1601                    state = output[ind].meta_data['fitstate']
1602                    t = time.localtime(state.timestamp)
1603                    time_str = time.strftime("%b %d %H:%M", t)
1604                    # Check that no time stamp is already appended
1605                    max_char = state.file.find("[")
1606                    if max_char < 0:
1607                        max_char = len(state.file)
1608                    original_fname = state.file[0:max_char]
1609                    state.file = original_fname + ' [' + time_str + ']'
1610                     
1611                    if state is not None and state.is_data is not None:
1612                        exec 'output[%d].is_data = state.is_data' % ind
1613                     
1614                    output[ind].filename = state.file
1615                    state.data = output[ind]
1616                    state.data.name = output[ind].filename  # state.data_name
1617                    state.data.id = state.data_id
1618                    if state.is_data is not None:
1619                        state.data.is_data = state.is_data
1620                    if output[ind].run_name is not None\
1621                        and len(output[ind].run_name) != 0:
1622                        name = output[ind].run_name
1623                    else:
1624                        name = original_fname
1625                    state.data.group_id = name
1626                    #store state in fitting
1627                    self.call_back(state=state,
1628                                   datainfo=output[ind], format=ext)
1629                    self.state = state
1630                return output
1631             
1632        except:
1633            self.call_back(format=ext)
1634            #self.state= state
1635            raise
1636           
1637    def write(self, filename, datainfo=None, fitstate=None):
1638        """
1639        Write the content of a Data1D as a CanSAS XML file only for standalone
1640       
1641        :param filename: name of the file to write
1642        :param datainfo: Data1D object
1643        :param fitstate: PageState object
1644       
1645        """
1646        # Sanity check
1647        if self.cansas == True:
1648            # Add fitting information to the XML document
1649            doc = self.write_toXML(datainfo, fitstate)
1650            # Write the XML document
1651        else:
1652            doc = fitstate.toXML(file=filename)
1653       
1654        # Save the document no matter the type
1655        fd = open(filename, 'w')
1656        fd.write(doc.toprettyxml())
1657        fd.close()
1658       
1659    def write_toXML(self, datainfo=None, state=None):
1660        """
1661        Write toXML, a helper for write(),
1662        could be used by guimanager._on_save()
1663       
1664        : return: xml doc
1665        """
1666
1667        if state.data is None:
1668            data = sas.dataloader.data_info.Data1D(x=[], y=[])
1669            return None
1670        elif not state.data.is_data:
1671            return None
1672        else:
1673            #make sure title and data run is filled up.
1674            if state.data.title == None or state.data.title == '':
1675                state.data.title = state.data.name
1676            if state.data.run_name == None or state.data.run_name == {}:
1677                state.data.run = [str(state.data.name)]
1678                state.data.run_name[0] = state.data.name
1679           
1680            if issubclass(state.data.__class__,
1681                          sas.dataloader.data_info.Data1D):
1682                data = state.data
1683                doc, sasentry = self._to_xml_doc(data)
1684            else:
1685                data = state.data
1686                doc, sasentry = self._data2d_to_xml_doc(data)
1687           
1688        if state is not None:
1689            doc = state.toXML(doc=doc, file=data.filename, entry_node=sasentry)
1690       
1691        return doc
1692   
1693# Simple html report templet
1694HEADER = "<html>\n"
1695HEADER += "<head>\n"
1696HEADER += "<meta http-equiv=Content-Type content='text/html; "
1697HEADER += "charset=windows-1252'> \n"
1698HEADER += "<meta name=Generator >\n"
1699HEADER += "</head>\n"
1700HEADER += "<body lang=EN-US>\n"
1701HEADER += "<div class=WordSection1>\n"
1702HEADER += "<p class=MsoNormal><b><span ><center><font size='4' >"
1703HEADER += "%s</font></center></span></center></b></p>"
1704HEADER += "<p class=MsoNormal>&nbsp;</p>"
1705PARA = "<p class=MsoNormal><font size='4' > %s \n"
1706PARA += "</font></p>"
1707CENTRE = "<p class=MsoNormal><center><font size='4' > %s \n"
1708CENTRE += "</font></center></p>"
1709FEET_1 = \
1710"""
1711<p class=MsoNormal>&nbsp;</p>
1712<br>
1713<p class=MsoNormal><b><span ><center> <font size='4' > Graph
1714</font></span></center></b></p>
1715<p class=MsoNormal>&nbsp;</p>
1716<center>
1717<br><font size='4' >Model Computation</font>
1718<br><font size='4' >Data: "%s"</font><br>
1719"""
1720FEET_2 = \
1721"""
1722<img src="%s" >
1723</img>
1724"""
1725FEET_3 = \
1726"""
1727</center>
1728</div>
1729</body>
1730</html>
1731"""
1732ELINE = "<p class=MsoNormal>&nbsp;</p>"
1733
1734if __name__ == "__main__":
1735    state = PageState(parent=None)
1736    #state.toXML()
1737    """
1738   
1739    file = open("test_state", "w")
1740    pickle.dump(state, file)
1741    print pickle.dumps(state)
1742    state.data_name = "hello---->"
1743    pickle.dump(state, file)
1744    file = open("test_state", "r")
1745    new_state= pickle.load(file)
1746    print "new state", new_state
1747    new_state= pickle.load(file)
1748    print "new state", new_state
1749    #print "state", state
1750    """
1751    import bsddb
1752    import pickle
1753    db = bsddb.btopen('file_state.db', 'c')
1754    val = (pickle.dumps(state), "hello", "hi")
1755    db['state1'] = pickle.dumps(val)
1756    print pickle.loads(db['state1'])
1757    state.data_name = "hello---->22"
1758    db['state2'] = pickle.dumps(state)
1759    state.data_name = "hello---->2"
1760    db['state3'] = pickle.dumps(state)
1761    del db['state3']
1762    state.data_name = "hello---->3"
1763    db['state4'] = pickle.dumps(state)
1764    new_state = pickle.loads(db['state1'])
1765    #print db.last()
1766    db.set_location('state2')
1767    state.data_name = "hello---->5"
1768    db['aastate5'] = pickle.dumps(state)
1769    db.keys().sort()
1770    print pickle.loads(db['state2'])
1771 
1772    db.close()
Note: See TracBrowser for help on using the repository browser.