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

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 bf6b8d1 was b1e609c, checked in by Doucet, Mathieu <doucetm@…>, 10 years ago

pylint fixes

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