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
RevLine 
[f32d144]1"""
2    Class that holds a fit page state
3"""
[b1e609c]4#TODO: Refactor code so we don't need to use getattr/setattr
[5062bbf]5################################################################################
6#This software was developed by the University of Tennessee as part of the
7#Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
[f32d144]8#project funded by the US National Science Foundation.
[5062bbf]9#
10#See the license text in license.txt
11#
12#copyright 2009, University of Tennessee
13################################################################################
[11a7e11]14import time
15import os
16import sys
[6f023e8]17import copy
[11a7e11]18import logging
[df7ed14]19import numpy
[eddb6ec]20import string
[c77d859]21
[35b556d]22import xml.dom.minidom
[ac5b69d]23from xml.dom.minidom import parseString
[11a7e11]24from lxml import etree
25
[79492222]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
[11a7e11]34#Information to read/write state as xml
[61cada5]35FITTING_NODE_NAME = 'fitting_plug_in'
36CANSAS_NS = "cansas1d/1.0"
37
[b1e609c]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"],
[11a7e11]76                            ["weights", "weights"]]
77
[b1e609c]78DISPERSION_LIST = [["disp_obj_dict", "_disp_obj_dict", "string"]]
[2296316]79
[b1e609c]80LIST_OF_STATE_PARAMETERS = [["parameters", "parameters"],
[f32d144]81                            ["str_parameters", "str_parameters"],
[61cada5]82                            ["orientation_parameters", "orientation_params"],
83                            ["dispersity_parameters", "orientation_params_disp"],
[f32d144]84                            ["fixed_param", "fixed_param"],
85                            ["fittable_param", "fittable_param"]]
[b1e609c]86LIST_OF_DATA_2D_ATTR = [["xmin", "xmin", "float"],
[f32d144]87                        ["xmax", "xmax", "float"],
88                        ["ymin", "ymin", "float"],
89                        ["ymax", "ymax", "float"],
90                        ["_xaxis", "_xaxis", "string"],
[df7ed14]91                        ["_xunit", "_xunit", "string"],
[f32d144]92                        ["_yaxis", "_yaxis", "string"],
93                        ["_yunit", "_yunit", "string"],
94                        ["_zaxis", "_zaxis", "string"],
95                        ["_zunit", "_zunit", "string"]]
[b1e609c]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"]]
[61cada5]104
[f32d144]105
106def parse_entry_helper(node, item):
[0b12abb5]107    """
108    Create a numpy list from value extrated from the node
[2f4b430]109
[0b12abb5]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
[f32d144]112        attribute and the third one is the type of the value of that
[0b12abb5]113        attribute. type can be string, float, bool, etc.
[2f4b430]114
[0b12abb5]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:
[e3d1423]122                return node.get(item[0]).strip() == "True"
[2f4b430]123
[0b12abb5]124            except:
125                return None
126        else:
127            try:
128                return float(node.get(item[0]))
129            except:
130                return None
[2f4b430]131
132
[cfc0913]133class PageState(object):
[c77d859]134    """
[5062bbf]135    Contains information to reconstruct a page of the fitpanel.
[c77d859]136    """
[61cada5]137    def __init__(self, parent=None, model=None, data=None):
[f32d144]138        """
[5062bbf]139        Initialize the current state
[2f4b430]140
[5062bbf]141        :param model: a selected model within a page
[f32d144]142        :param data:
[2f4b430]143
[c77d859]144        """
[61cada5]145        self.file = None
[11a7e11]146        #Time of state creation
147        self.timestamp = time.time()
[c77d859]148        ## Data member to store the dispersion object created
149        self._disp_obj_dict = {}
[61cada5]150        #------------------------
[f32d144]151        #Data used for fitting
[c77d859]152        self.data = data
[2296316]153        # model data
154        self.theory_data = None
155        #Is 2D
156        self.is_2D = False
157        self.images = None
[2f4b430]158
[61cada5]159        #save additional information on data that dataloader.reader does not read
160        self.is_data = None
161        self.data_name = ""
[c0ff8cc]162
[61cada5]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
[2f4b430]171
[61cada5]172        ## reset True change the state of exsiting button
173        self.reset = False
[2f4b430]174
[c99a6c5]175        #engine type
176        self.engine_type = None
[c77d859]177        # flag to allow data2D plot
[cfc0913]178        self.enable2D = False
[c77d859]179        # model on which the fit would be performed
180        self.model = model
[93f0a862]181        self.m_name = None
[dce84c0]182        #list of process done to model
183        self.process = []
[f32d144]184        #fit page manager
[c77d859]185        self.manager = None
186        #Store the parent of this panel parent
187        # For this application fitpanel is the parent
[f32d144]188        self.parent = parent
[c77d859]189        # Event_owner is the owner of model event
190        self.event_owner = None
[c9a4377]191        ##page name
[cfc0913]192        self.page_name = ""
[eddb6ec]193        # Contains link between model, all its parameters, and panel organization
[61cada5]194        self.parameters = []
[fb59ed9]195        # String parameter list that can not be fitted
196        self.str_parameters = []
[f32d144]197        # Contains list of parameters that cannot be fitted and reference to
198        #panel objects
[61cada5]199        self.fixed_param = []
[f32d144]200        # Contains list of parameters with dispersity and reference to
201        #panel objects
[61cada5]202        self.fittable_param = []
[60132ef]203        ## orientation parameters
[61cada5]204        self.orientation_params = []
[eddb6ec]205        ## orientation parameters for gaussian dispersity
[61cada5]206        self.orientation_params_disp = []
[3370922]207        ## smearer info
[61cada5]208        self.smearer = None
[7609f1a]209        self.smear_type = None
210        self.dq_l = None
211        self.dq_r = None
[db8fd5b]212        self.dx_max = None
213        self.dx_min = None
214        self.dxl = None
215        self.dxw = None
[eddb6ec]216        #list of dispersion parameters
[f32d144]217        self.disp_list = []
[61cada5]218        if self.model is not None:
[71f0373]219            self.disp_list = self.model.getDispParamList()
[2296316]220
[61cada5]221        self.disp_cb_dict = {}
[2296316]222        self.values = {}
223        self.weights = {}
[2f4b430]224
[f32d144]225        #contains link between a model and selected parameters to fit
[61cada5]226        self.param_toFit = []
[a074145]227        ##dictionary of model type and model class
[cfc0913]228        self.model_list_box = None
[a074145]229        ## save the state of the context menu
[61cada5]230        self.saved_states = {}
[240b9966]231        ## save selection of combobox
[3b9e023]232        self.formfactorcombobox = None
[ea5fa58]233        self.categorycombobox = None
[f32d144]234        self.structurecombobox = None
[c0ff8cc]235
[240b9966]236        ## radio box to select type of model
[ea5fa58]237        #self.shape_rbutton = False
238        #self.shape_indep_rbutton = False
239        #self.struct_rbutton = False
240        #self.plugin_rbutton = False
[b787e68c]241        ## the indice of the current selection
242        self.disp_box = 0
[c77d859]243        ## Qrange
[cfc0913]244        ## Q range
[61cada5]245        self.qmin = 0.001
246        self.qmax = 0.1
[0b12abb5]247        #reset data range
248        self.qmax_x = None
249        self.qmin_x = None
[2f4b430]250
[cfc0913]251        self.npts = None
[61cada5]252        self.name = ""
[4523b68]253        self.multi_factor = None
[318b5bbb]254        self.magnetic_on = False
[cfc0913]255        ## enable smearering state
256        self.enable_smearer = False
[fc6ea43]257        self.disable_smearer = True
[7609f1a]258        self.pinhole_smearer = False
[f32d144]259        self.slit_smearer = False
[55bb249c]260        # weighting options
261        self.dI_noweight = False
262        self.dI_didata = True
263        self.dI_sqrdata = False
[f32d144]264        self.dI_idata = False
[cfc0913]265        ## disperity selection
[61cada5]266        self.enable_disp = False
267        self.disable_disp = True
[2f4b430]268
[cfc0913]269        ## state of selected all check button
270        self.cb1 = False
[0aeabc6]271        ## store value of chisqr
[61cada5]272        self.tcChi = None
[2f4b430]273
[6f023e8]274    def clone(self):
[61cada5]275        """
[5062bbf]276        Create a new copy of the current object
[61cada5]277        """
278        model = None
279        if self.model is not None:
[6f023e8]280            model = self.model.clone()
[bb70474]281            model.name = self.model.name
[61cada5]282        obj = PageState(self.parent, model=model)
283        obj.file = copy.deepcopy(self.file)
[6f023e8]284        obj.data = copy.deepcopy(self.data)
[61cada5]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
[dcf29d7]289        obj.model_list_box = copy.deepcopy(self.model_list_box)
[c99a6c5]290        obj.engine_type = copy.deepcopy(self.engine_type)
[2f4b430]291
[ea5fa58]292        obj.categorycombobox = self.categorycombobox
[61cada5]293        obj.formfactorcombobox = self.formfactorcombobox
[f32d144]294        obj.structurecombobox = self.structurecombobox
[2f4b430]295
[ea5fa58]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
[2f4b430]300
[dcf29d7]301        obj.manager = self.manager
302        obj.event_owner = self.event_owner
[71f0373]303        obj.disp_list = copy.deepcopy(self.disp_list)
[2f4b430]304
[c477b31]305        obj.enable2D = copy.deepcopy(self.enable2D)
[cfc0913]306        obj.parameters = copy.deepcopy(self.parameters)
[fb59ed9]307        obj.str_parameters = copy.deepcopy(self.str_parameters)
[cfc0913]308        obj.fixed_param = copy.deepcopy(self.fixed_param)
309        obj.fittable_param = copy.deepcopy(self.fittable_param)
[f32d144]310        obj.orientation_params = copy.deepcopy(self.orientation_params)
311        obj.orientation_params_disp = copy.deepcopy(self.orientation_params_disp)
[b787e68c]312        obj.enable_disp = copy.deepcopy(self.enable_disp)
[fc6ea43]313        obj.disable_disp = copy.deepcopy(self.disable_disp)
[0aeabc6]314        obj.tcChi = self.tcChi
[2f4b430]315
[f32d144]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():
[2296316]324                obj.values[k] = v
[f32d144]325        if len(self.weights) > 0:
326            for k, v in self.weights.iteritems():
[2296316]327                obj.weights[k] = v
[b787e68c]328        obj.enable_smearer = copy.deepcopy(self.enable_smearer)
[fc6ea43]329        obj.disable_smearer = copy.deepcopy(self.disable_smearer)
[7609f1a]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)
[55bb249c]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)
[7609f1a]337        obj.dq_l = copy.deepcopy(self.dq_l)
338        obj.dq_r = copy.deepcopy(self.dq_r)
[db8fd5b]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)
[2f4b430]342        obj.dxw = copy.deepcopy(self.dxw)
[b787e68c]343        obj.disp_box = copy.deepcopy(self.disp_box)
344        obj.qmin = copy.deepcopy(self.qmin)
345        obj.qmax = copy.deepcopy(self.qmax)
[c0ff8cc]346        obj.multi_factor = self.multi_factor
[318b5bbb]347        obj.magnetic_on = self.magnetic_on
[f32d144]348        obj.npts = copy.deepcopy(self.npts)
[b787e68c]349        obj.cb1 = copy.deepcopy(self.cb1)
[3370922]350        obj.smearer = copy.deepcopy(self.smearer)
[2f4b430]351
[a074145]352        for name, state in self.saved_states.iteritems():
353            copy_name = copy.deepcopy(name)
354            copy_state = state.clone()
[f32d144]355            obj.saved_states[copy_name] = copy_state
[6f023e8]356        return obj
[2f4b430]357
[61cada5]358    def _repr_helper(self, list, rep):
359        """
[5062bbf]360        Helper method to print a state
[61cada5]361        """
362        for item in list:
[f32d144]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])
[240b9966]373        return rep
[2f4b430]374
[61cada5]375    def __repr__(self):
[f32d144]376        """
[5062bbf]377        output string for printing
[11a7e11]378        """
[f32d144]379        rep = "\nState name: %s\n" % self.file
[11a7e11]380        t = time.localtime(self.timestamp)
[deff488]381        time_str = time.strftime("%b %d %Y %H;%M;%S ", t)
[c0ff8cc]382
[f32d144]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
[74dc0a4]389        if self.model != None:
[93f0a862]390            m_name = self.model.__class__.__name__
391            if m_name == 'Model':
392                m_name = self.m_name
[f32d144]393            rep += "model name : %s\n" % m_name
[74dc0a4]394        else:
395            rep += "model name : None\n"
[f32d144]396        rep += "multi_factor : %s\n" % str(self.multi_factor)
[2f4b430]397        rep += "magnetic_on : %s\n" % str(self.magnetic_on)
[ea5fa58]398        rep += "model type (Category) selected: %s\n" % self.categorycombobox
[f32d144]399        rep += "data : %s\n" % str(self.data)
400        rep += "Plotting Range: min: %s, max: %s, steps: %s\n" % (str(self.qmin),
[b1e609c]401                                                                  str(self.qmax), str(self.npts))
[f32d144]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)
[2f4b430]410
[f32d144]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)
[2f4b430]415
[f32d144]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
[db8fd5b]423        rep += "dx_max  : %s\n" % str(self.dx_max)
[2f4b430]424        rep += "dx_min : %s\n" % str(self.dx_min)
[db8fd5b]425        rep += "dxl  : %s\n" % str(self.dxl)
[2f4b430]426        rep += "dxw : %s\n" % str(self.dxw)
[f32d144]427        rep += "model  : %s\n\n" % str(self.model)
[2296316]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
[2f4b430]445
[f32d144]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)
[61cada5]452        return rep
[2296316]453
454    def set_report_string(self):
455        """
[f32d144]456        Get the values (strings) from __str__ for report
[2296316]457        """
[d06ae30]458        # Dictionary of the report strings
[2296316]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:
[b1e609c]479                logging.error(sys.exc_value)
[b7c6a4a]480            if name.count("State created"):
481                repo_time = "" + value
[2296316]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 + ','
[b1e609c]493            if name == "selected":
494                if value == u' False':
495                    fixed_parameter = True
496                else:
497                    fixed_parameter = False
[2296316]498            if name == "error value":
[b1e609c]499                if fixed_parameter:
500                    param_string += '(fixed),'
501                else:
[d06ae30]502                    param_string += value + ','
[2296316]503            if name == "parameter unit":
[f32d144]504                param_string += value + ':'
[2296316]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:
[b1e609c]515                    file_value = ("File name:" + content[2])
516                    file_name = CENTRE % file_value
[2296316]517                    if len(title) == 0:
518                        title = content[2] + " [" + repo_time + "]"
519                        title_name = HEADER % title
520                except:
[b1e609c]521                    logging.error(sys.exc_value)
[74dc0a4]522            if name == "model name ":
523                try:
524                    modelname = "Model name:" + content[1]
525                except:
526                    modelname = "Model name:" + " NAN"
[f32d144]527                model_name = CENTRE % modelname
[2f4b430]528
[2296316]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:
[b1e609c]536                    logging.error(sys.exc_value)
[2296316]537        paramval = ""
[f32d144]538        for lines in param_string.split(":"):
[2296316]539            line = lines.split(",")
[f32d144]540            if len(lines) > 0:
541                param = line[0]
[2296316]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):
[f32d144]548                    paramval += param + "\n"
[2296316]549                    paramval_string += CENTRE % param + "\n"
[2f4b430]550
[f5bdb4a]551        text_string = "\n\n\n" + title + "\n\n" + file + \
[f32d144]552                      "\n" + q_name + \
553                      "\n" + chi2 + \
554                      "\n\n" + paramval
[2f4b430]555
[f5bdb4a]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)
[2f4b430]559
[2296316]560        html_string = title_name + "\n" + file_name + \
[f32d144]561                                   "\n" + model_name + \
[2296316]562                                   "\n" + q_range + \
563                                   "\n" + chi2_string + \
564                                   "\n" + ELINE + \
565                                   "\n" + paramval_string + \
566                                   "\n" + ELINE + \
567                                   "\n" + FEET_1 % title + \
[f32d144]568                                   "\n" + FEET_2
[2f4b430]569
[2296316]570        return html_string, text_string, title
[2f4b430]571
[f5bdb4a]572    def _check_html_format(self, name):
573        """
574        Check string '%' for html format
575        """
576        if name.count('%'):
577            name = name.replace('%', '&#37')
[2f4b430]578
[f5bdb4a]579        return name
[2f4b430]580
[2296316]581    def report(self, figs=None, canvases=None):
582        """
583        Invoke report dialog panel
[2f4b430]584
[2296316]585        : param figs: list of pylab figures [list]
586        """
[79492222]587        from sas.perspectives.fitting.report_dialog import ReportDialog
[2296316]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:
[f32d144]592            add_str = FEET_3
[2296316]593        elif len(figs) == 2:
[f32d144]594            add_str = ELINE
595            add_str += FEET_2 % ("%s")
596            add_str += ELINE
597            add_str += FEET_3
[2296316]598        elif len(figs) > 2:
[f32d144]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
[2296316]605        else:
606            add_str = ""
[c0ff8cc]607
[2296316]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)
[f32d144]613        report_list = [report_str, text_str, images]
[2296316]614        dialog = ReportDialog(report_list, None, -1, "")
[d838715]615        dialog.Show()
[2f4b430]616
[eddb6ec]617    def _toXML_helper(self, thelist, element, newdoc):
[61cada5]618        """
[5062bbf]619        Helper method to create xml file for saving state
[61cada5]620        """
[eddb6ec]621        for item in thelist:
[61cada5]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)
[2f4b430]634
[61cada5]635    def toXML(self, file="fitting_state.fitv", doc=None, entry_node=None):
636        """
[5062bbf]637        Writes the state of the InversionControl panel to file, as XML.
[2f4b430]638
[5062bbf]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.
[2f4b430]642
[5062bbf]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]
[2f4b430]646
[61cada5]647        """
648        from xml.dom.minidom import getDOMImplementation
[71f0373]649
[61cada5]650        # Check whether we have to write a standalone XML file
651        if doc is None:
652            impl = getDOMImplementation()
[f32d144]653            doc_type = impl.createDocumentType(FITTING_NODE_NAME, "1.0", "1.0")
[61cada5]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
[ac5b69d]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)
[61cada5]665            if entry_node is None:
666                newdoc.documentElement.appendChild(top_element)
667            else:
[ac5b69d]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)
[2f4b430]675
[61cada5]676        attr = newdoc.createAttribute("version")
677        attr.nodeValue = '1.0'
678        top_element.setAttributeNode(attr)
[2f4b430]679
[61cada5]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)
[2f4b430]687
[11a7e11]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)
[61cada5]694        # Inputs
695        inputs = newdoc.createElement("Attributes")
696        top_element.appendChild(inputs)
[2f4b430]697
[61cada5]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
[2f4b430]706
[b1e609c]707        for item in LIST_OF_DATA_ATTRIBUTES:
[0b12abb5]708            element = newdoc.createElement(item[0])
[b1e609c]709            element.setAttribute(item[0], str(getattr(self, item[1])))
[f32d144]710            inputs.appendChild(element)
[2f4b430]711
[b1e609c]712        for item in LIST_OF_STATE_ATTRIBUTES:
[26f3dd5]713            element = newdoc.createElement(item[0])
[b1e609c]714            element.setAttribute(item[0], str(getattr(self, item[1])))
[26f3dd5]715            inputs.appendChild(element)
[2f4b430]716
[f32d144]717        # For self.values ={ disp_param_name: [vals,...],...}
718        # and for self.weights ={ disp_param_name: [weights,...],...}
[b1e609c]719        value_list = {}
720        for item in LIST_OF_MODEL_ATTRIBUTES:
[11a7e11]721            element = newdoc.createElement(item[0])
[b1e609c]722            value_list = getattr(self, item[1])
723            for key, value in value_list.iteritems():
[2296316]724                sub_element = newdoc.createElement(key)
725                sub_element.setAttribute('name', str(key))
726                for val in value:
[b1e609c]727                    sub_element.appendChild(newdoc.createTextNode(str(val)))
[2f4b430]728
[f32d144]729                element.appendChild(sub_element)
[11a7e11]730            inputs.appendChild(element)
[2f4b430]731
[2296316]732        # Create doc for the dictionary of self._disp_obj_dic
[b1e609c]733        for item in DISPERSION_LIST:
[f32d144]734            element = newdoc.createElement(item[0])
[b1e609c]735            value_list = getattr(self, item[1])
736            for key, val in value_list.iteritems():
[f32d144]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)
[2f4b430]743
[b1e609c]744        for item in LIST_OF_STATE_PARAMETERS:
[61cada5]745            element = newdoc.createElement(item[0])
[b1e609c]746            self._toXML_helper(thelist=getattr(self, item[1]), element=element, newdoc=newdoc)
[61cada5]747            inputs.appendChild(element)
[2f4b430]748
[61cada5]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:
[ac5b69d]756            return newdoc
[2f4b430]757
[61cada5]758    def _fromXML_helper(self, node, list):
759        """
[5062bbf]760        Helper function to write state to xml
[61cada5]761        """
762        for item in node:
[0b12abb5]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:
[3ad91de]772                selected_to_fit = (item.get('selected_to_fit') == "True")
[0b12abb5]773            except:
774                selected_to_fit = None
775            try:
[3ad91de]776                error_displayed = (item.get('error_displayed') == "True")
[0b12abb5]777            except:
778                error_displayed = None
[f32d144]779            try:
[0b12abb5]780                error_value = item.get('error_value')
781            except:
782                error_value = None
783            try:
[f32d144]784                minimum_displayed = (item.get('minimum_displayed') == "True")
[0b12abb5]785            except:
786                minimum_displayed = None
787            try:
788                minimum_value = item.get('minimum_value')
789            except:
790                minimum_value = None
791            try:
[3ad91de]792                maximum_displayed = (item.get('maximum_displayed') == "True")
[0b12abb5]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
[2296316]803            list.append([selected_to_fit, name, value, "+/-",
804                         [error_displayed, error_value],
[f32d144]805                         [minimum_displayed, minimum_value],
806                         [maximum_displayed, maximum_value], unit])
[2f4b430]807
[61cada5]808    def fromXML(self, file=None, node=None):
809        """
[5062bbf]810        Load fitting state from a file
[2f4b430]811
[5062bbf]812        :param file: .fitv file
813        :param node: node of a XML document to read from
[2f4b430]814
[61cada5]815        """
816        if file is not None:
[11a7e11]817            msg = "PageState no longer supports non-CanSAS"
818            msg += " format for fitting files"
819            raise RuntimeError, msg
[2f4b430]820
[11a7e11]821        if node.get('version')and node.get('version') == '1.0':
[2f4b430]822
[61cada5]823            # Get file name
824            entry = get_content('ns:filename', node)
825            if entry is not None:
826                self.file = entry.text.strip()
[2f4b430]827
[11a7e11]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)
[2f4b430]837
[61cada5]838            # Parse fitting attributes
839            entry = get_content('ns:Attributes', node)
[b1e609c]840            for item in LIST_OF_DATA_ATTRIBUTES:
[f32d144]841                node = get_content('ns:%s' % item[0], entry)
[b1e609c]842                setattr(self, item[0], parse_entry_helper(node, item))
[2296316]843
[61cada5]844            if entry is not None:
[b1e609c]845                for item in LIST_OF_STATE_ATTRIBUTES:
[2296316]846                    node = get_content('ns:%s' % item[0], entry)
[b1e609c]847                    setattr(self, item[0], parse_entry_helper(node, item))
[2f4b430]848
[b1e609c]849                for item in LIST_OF_STATE_PARAMETERS:
[2296316]850                    node = get_content("ns:%s" % item[0], entry)
[b1e609c]851                    self._fromXML_helper(node=node, list=getattr(self, item[1]))
[2f4b430]852
[f32d144]853                # Recover _disp_obj_dict from xml file
854                self._disp_obj_dict = {}
[b1e609c]855                for item in DISPERSION_LIST:
[2296316]856                    # Get node
857                    node = get_content("ns:%s" % item[0], entry)
858                    for attr in node:
[b1e609c]859                        name = str(attr.get('name'))
[f32d144]860                        val = attr.get('value')
[2296316]861                        value = val.split(" instance")[0]
862                        disp_name = value.split("<")[1]
[11a7e11]863                        try:
[2296316]864                            # Try to recover disp_model object from strings
[79492222]865                            com = "from sas.models.dispersion_models "
[2c44cf8]866                            com += "import %s as disp"
[2296316]867                            com_name = disp_name.split(".")[3]
868                            exec com % com_name
[2c44cf8]869                            disp_model = disp()
[b1e609c]870                            attribute = getattr(self, item[1])
871                            attribute[name] = com_name
[11a7e11]872                        except:
[b1e609c]873                            logging.error(sys.exc_value)
[2f4b430]874
[f32d144]875                # get self.values and self.weights dic. if exists
[b1e609c]876                for item in LIST_OF_MODEL_ATTRIBUTES:
[2296316]877                    node = get_content("ns:%s" % item[0], entry)
878                    dic = {}
[b1e609c]879                    value_list = []
[2296316]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:
[f32d144]886                                val = float(line)
[b1e609c]887                                value_list.append(val)
[2296316]888                            except:
889                                # pass if line is empty (it happens)
[b1e609c]890                                logging.error(sys.exc_value)
891                    dic[name] = numpy.array(value_list)
892                    setattr(self, item[1], dic)
[2f4b430]893
[2296316]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()
[2f4b430]907
[2296316]908        # For no figures in the list, prepare empty plot
909        if figs == None or len(figs) == 0:
910            figs = [None]
[2f4b430]911
[f32d144]912        # Loop over the list of figures
[2296316]913        # use wx.MemoryFSHandler
[f32d144]914        self.imgRAM = wx.MemoryFSHandler()
[2296316]915        for fig in figs:
916            if figs != None:
917                ind = figs.index(fig)
918                canvas = canvases[ind]
[2f4b430]919
[b1e609c]920            #store the image in wx.FileSystem Object
[2296316]921            wx.FileSystem.AddHandler(wx.MemoryFSHandler())
[2f4b430]922
[2296316]923            # index of the fig
924            ind = figs.index(fig)
[2f4b430]925
[2296316]926            #AddFile, image can be retrieved with 'memory:filename'
[f32d144]927            self.imgRAM.AddFile('img_fit%s.png' % ind,
[2296316]928                                canvas.bitmap, wx.BITMAP_TYPE_PNG)
[2f4b430]929
[2296316]930            #append figs
931            images.append(fig)
[2f4b430]932
[c0ff8cc]933        return images
[61cada5]934
[f32d144]935
[61cada5]936class Reader(CansasReader):
937    """
[5062bbf]938    Class to load a .fitv fitting file
[61cada5]939    """
940    ## File type
941    type_name = "Fitting"
[2f4b430]942
[61cada5]943    ## Wildcards
[b35d3d1]944    type = ["Fitting files (*.fitv)|*.fitv"
[b9a5f0e]945            "SASView file (*.svs)|*.svs"]
[61cada5]946    ## List of allowed extensions
[f32d144]947    ext = ['.fitv', '.FITV', '.svs', 'SVS']
[2f4b430]948
[61cada5]949    def __init__(self, call_back=None, cansas=True):
[df7ed14]950        CansasReader.__init__(self)
[61cada5]951        """
[5062bbf]952        Initialize the call-back method to be called
953        after we load a file
[2f4b430]954
[5062bbf]955        :param call_back: call-back method
956        :param cansas:  True = files will be written/read in CanSAS format
957                        False = write CanSAS format
[2f4b430]958
[61cada5]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
[75fbd17]964        self.state = None
[2f4b430]965
[75fbd17]966    def get_state(self):
967        return self.state
[2f4b430]968
[61cada5]969    def read(self, path):
[f32d144]970        """
[5062bbf]971        Load a new P(r) inversion state from file
[2f4b430]972
[5062bbf]973        :param path: file path
[2f4b430]974
[61cada5]975        """
[e9b12eaf]976        if self.cansas == True:
[61cada5]977            return self._read_cansas(path)
[2f4b430]978
[e9b12eaf]979    def _data2d_to_xml_doc(self, datainfo):
[61cada5]980        """
[5062bbf]981        Create an XML document to contain the content of a Data2D
[2f4b430]982
[5062bbf]983        :param datainfo: Data2D object
[2f4b430]984
[61cada5]985        """
986        if not issubclass(datainfo.__class__, Data2D):
987            raise RuntimeError, "The cansas writer expects a Data2D instance"
[2f4b430]988
[61cada5]989        doc = xml.dom.minidom.Document()
990        main_node = doc.createElement("SASroot")
991        main_node.setAttribute("version", self.version)
[df7ed14]992        main_node.setAttribute("xmlns", "cansas1d/%s" % self.version)
993        main_node.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance")
[b1e609c]994        main_node.setAttribute("xsi:schemaLocation",
995                               "cansas1d/%s http://svn.smallangles.net/svn/canSAS/1dwg/trunk/cansas1d.xsd" % self.version)
[2f4b430]996
[61cada5]997        doc.appendChild(main_node)
[2f4b430]998
[61cada5]999        entry_node = doc.createElement("SASentry")
1000        main_node.appendChild(entry_node)
[2f4b430]1001
[61cada5]1002        write_node(doc, entry_node, "Title", datainfo.title)
[df7ed14]1003        if datainfo is not None:
1004            write_node(doc, entry_node, "data_class", datainfo.__class__.__name__)
[61cada5]1005        for item in datainfo.run:
1006            runname = {}
[2f4b430]1007            if datainfo.run_name.has_key(item) and len(str(datainfo.run_name[item])) > 1:
[b1e609c]1008                runname = {'name': datainfo.run_name[item]}
[61cada5]1009            write_node(doc, entry_node, "Run", item, runname)
1010        # Data info
[df7ed14]1011        new_node = doc.createElement("SASdata")
1012        entry_node.appendChild(new_node)
[b1e609c]1013        for item in LIST_OF_DATA_2D_ATTR:
[e9b12eaf]1014            element = doc.createElement(item[0])
[b1e609c]1015            element.setAttribute(item[0], str(getattr(datainfo, item[1])))
[df7ed14]1016            new_node.appendChild(element)
[2f4b430]1017
[b1e609c]1018        for item in LIST_OF_DATA_2D_VALUES:
[df7ed14]1019            root_node = doc.createElement(item[0])
1020            new_node.appendChild(root_node)
[38226d26]1021            temp_list = None
[b1e609c]1022            temp_list = getattr(datainfo, item[1])
[df7ed14]1023
[2f4b430]1024            if temp_list is None or len(temp_list) == 0:
[df7ed14]1025                element = doc.createElement(item[0])
[b1e609c]1026                element.appendChild(doc.createTextNode(str(temp_list)))
[df7ed14]1027                root_node.appendChild(element)
[35b556d]1028            else:
1029                for value in temp_list:
[df7ed14]1030                    element = doc.createElement(item[0])
[b1e609c]1031                    element.setAttribute(item[0], str(value))
[df7ed14]1032                    root_node.appendChild(element)
[2f4b430]1033
[61cada5]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))
[f32d144]1040        write_node(doc, sample, "thickness", datainfo.sample.thickness,
1041                   {"unit": datainfo.sample.thickness_unit})
[61cada5]1042        write_node(doc, sample, "transmission", datainfo.sample.transmission)
[f32d144]1043        write_node(doc, sample, "temperature", datainfo.sample.temperature,
1044                   {"unit": datainfo.sample.temperature_unit})
[2f4b430]1045
[61cada5]1046        for item in datainfo.sample.details:
1047            write_node(doc, sample, "details", item)
[2f4b430]1048
[61cada5]1049        pos = doc.createElement("position")
[f32d144]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})
[61cada5]1058        if written == True:
1059            sample.appendChild(pos)
[2f4b430]1060
[61cada5]1061        ori = doc.createElement("orientation")
[f32d144]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})
[61cada5]1070        if written == True:
1071            sample.appendChild(ori)
[2f4b430]1072
[61cada5]1073        # Instrument info
1074        instr = doc.createElement("SASinstrument")
1075        entry_node.appendChild(instr)
[2f4b430]1076
[61cada5]1077        write_node(doc, instr, "name", datainfo.instrument)
[2f4b430]1078
[61cada5]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)
[2f4b430]1084
[61cada5]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))
[f32d144]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})
[61cada5]1098        if written == True:
1099            source.appendChild(size)
[2f4b430]1100
[f32d144]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})
[2f4b430]1112
[61cada5]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)
[2f4b430]1119
[f32d144]1120            write_node(doc, coll, "length", item.length,
1121                       {"unit": item.length_unit})
[2f4b430]1122
[61cada5]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)
[2f4b430]1130
[f32d144]1131                write_node(doc, ap, "distance", apert.distance,
1132                           {"unit": apert.distance_unit})
[2f4b430]1133
[61cada5]1134                size = doc.createElement("size")
1135                if apert.size_name is not None:
1136                    size.setAttribute("name", str(apert.size_name))
[f32d144]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})
[61cada5]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)
[f32d144]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})
[61cada5]1155            if written == True:
1156                instr.appendChild(det)
[2f4b430]1157
[61cada5]1158            off = doc.createElement("offset")
[f32d144]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})
[61cada5]1165            if written == True:
1166                det.appendChild(off)
[2f4b430]1167
[61cada5]1168            center = doc.createElement("beam_center")
[f32d144]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})
[61cada5]1177            if written == True:
1178                det.appendChild(center)
[2f4b430]1179
[61cada5]1180            pix = doc.createElement("pixel_size")
[f32d144]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})
[61cada5]1187            if written == True:
1188                det.appendChild(pix)
[2f4b430]1189
[61cada5]1190            ori = doc.createElement("orientation")
[f32d144]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})
[61cada5]1198            if written == True:
1199                det.appendChild(ori)
[2f4b430]1200
[61cada5]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
[2f4b430]1218
[61cada5]1219    def _parse_state(self, entry):
1220        """
[5062bbf]1221        Read a fit result from an XML node
[2f4b430]1222
[f32d144]1223        :param entry: XML node to read from
[2f4b430]1224
[5062bbf]1225        :return: PageState object
[61cada5]1226        """
1227        # Create an empty state
[2f4b430]1228        state = None
[61cada5]1229        # Locate the P(r) node
1230        try:
[f32d144]1231            nodes = entry.xpath('ns:%s' % FITTING_NODE_NAME,
1232                                namespaces={'ns': CANSAS_NS})
1233            if nodes != []:
[b35d3d1]1234                # Create an empty state
[f32d144]1235                state = PageState()
[b35d3d1]1236                state.fromXML(node=nodes[0])
[2f4b430]1237
[61cada5]1238        except:
1239            logging.info("XML document does not contain fitting information.\n %s" % sys.exc_value)
[2f4b430]1240
[61cada5]1241        return state
[2f4b430]1242
[ac5b69d]1243    def _parse_save_state_entry(self, dom):
[e9b12eaf]1244        """
[5062bbf]1245        Parse a SASentry
[2f4b430]1246
[5062bbf]1247        :param node: SASentry node
[2f4b430]1248
[df7ed14]1249        :return: Data1D/Data2D object
[2f4b430]1250
[e9b12eaf]1251        """
[df7ed14]1252        node = dom.xpath('ns:data_class', namespaces={'ns': CANSAS_NS})
1253        if not node or node[0].text.lstrip().rstrip() != "Data2D":
[ac5b69d]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)
[2f4b430]1270
[ac5b69d]1271            return return_value, _
[2f4b430]1272
[df7ed14]1273        #Parse 2D
[e9b12eaf]1274        data_info = Data2D()
[2f4b430]1275
[f32d144]1276        # Look up title
[e9b12eaf]1277        self._store_content('ns:Title', dom, 'title', data_info)
[2f4b430]1278
[f32d144]1279        # Look up run number
[e9b12eaf]1280        nodes = dom.xpath('ns:Run', namespaces={'ns': CANSAS_NS})
[f32d144]1281        for item in nodes:
[e9b12eaf]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')
[2f4b430]1288
[f32d144]1289        # Look up instrument name
1290        self._store_content('ns:SASinstrument/ns:name', dom,
1291                            'instrument', data_info)
[e9b12eaf]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)
[2f4b430]1305
[e9b12eaf]1306        # Sample info ###################
1307        entry = get_content('ns:SASsample', dom)
1308        if entry is not None:
1309            data_info.sample.name = entry.get('name')
[2f4b430]1310
[b1e609c]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)
[2f4b430]1315
[b1e609c]1316        nodes = dom.xpath('ns:SASsample/ns:details', namespaces={'ns': CANSAS_NS})
[e9b12eaf]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)
[2f4b430]1327
[e9b12eaf]1328        # Position (as a vector)
[b1e609c]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)
[2f4b430]1332
[e9b12eaf]1333        # Orientation (as a vector)
[f32d144]1334        self._store_float('ns:SASsample/ns:orientation/ns:roll',
[b1e609c]1335                          dom, 'orientation.x', data_info.sample)
[f32d144]1336        self._store_float('ns:SASsample/ns:orientation/ns:pitch',
[b1e609c]1337                          dom, 'orientation.y', data_info.sample)
[f32d144]1338        self._store_float('ns:SASsample/ns:orientation/ns:yaw',
[b1e609c]1339                          dom, 'orientation.z', data_info.sample)
[2f4b430]1340
[e9b12eaf]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')
[2f4b430]1345
[f32d144]1346        self._store_content('ns:SASinstrument/ns:SASsource/ns:radiation',
[b1e609c]1347                            dom, 'radiation', data_info.source)
[f32d144]1348        self._store_content('ns:SASinstrument/ns:SASsource/ns:beam_shape',
[b1e609c]1349                            dom, 'beam_shape', data_info.source)
[f32d144]1350        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength',
[b1e609c]1351                          dom, 'wavelength', data_info.source)
[f32d144]1352        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_min',
[b1e609c]1353                          dom, 'wavelength_min', data_info.source)
[f32d144]1354        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_max',
[b1e609c]1355                          dom, 'wavelength_max', data_info.source)
[f32d144]1356        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_spread',
[b1e609c]1357                          dom, 'wavelength_spread', data_info.source)
[2f4b430]1358
[f32d144]1359        # Beam size (as a vector)
[e9b12eaf]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')
[2f4b430]1363
[f32d144]1364        self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:x',
[b1e609c]1365                          dom, 'beam_size.x', data_info.source)
[f32d144]1366        self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:y',
[b1e609c]1367                          dom, 'beam_size.y', data_info.source)
[f32d144]1368        self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:z',
[b1e609c]1369                          dom, 'beam_size.z', data_info.source)
[2f4b430]1370
[e9b12eaf]1371        # Collimation info ###################
[f32d144]1372        nodes = dom.xpath('ns:SASinstrument/ns:SAScollimation',
1373                          namespaces={'ns': CANSAS_NS})
[e9b12eaf]1374        for item in nodes:
1375            collim = Collimation()
1376            if item.get('name') is not None:
1377                collim.name = item.get('name')
[f32d144]1378            self._store_float('ns:length', item, 'length', collim)
[2f4b430]1379
[e9b12eaf]1380            # Look for apertures
[f32d144]1381            apert_list = item.xpath('ns:aperture',
1382                                    namespaces={'ns': CANSAS_NS})
[e9b12eaf]1383            for apert in apert_list:
[f32d144]1384                aperture = Aperture()
[2f4b430]1385
[e9b12eaf]1386                # Get the name and type of the aperture
1387                aperture.name = apert.get('name')
1388                aperture.type = apert.get('type')
[2f4b430]1389
[f32d144]1390                self._store_float('ns:distance', apert, 'distance', aperture)
[2f4b430]1391
[e9b12eaf]1392                entry = get_content('ns:size', apert)
1393                if entry is not None:
1394                    aperture.size_name = entry.get('name')
[2f4b430]1395
[f32d144]1396                self._store_float('ns:size/ns:x', apert, 'size.x', aperture)
1397                self._store_float('ns:size/ns:y', apert, 'size.y', aperture)
[e9b12eaf]1398                self._store_float('ns:size/ns:z', apert, 'size.z', aperture)
[2f4b430]1399
[e9b12eaf]1400                collim.aperture.append(aperture)
[2f4b430]1401
[e9b12eaf]1402            data_info.collimation.append(collim)
[2f4b430]1403
[e9b12eaf]1404        # Detector info ######################
[f32d144]1405        nodes = dom.xpath('ns:SASinstrument/ns:SASdetector',
1406                          namespaces={'ns': CANSAS_NS})
[e9b12eaf]1407        for item in nodes:
[2f4b430]1408
[e9b12eaf]1409            detector = Detector()
[2f4b430]1410
[e9b12eaf]1411            self._store_content('ns:name', item, 'name', detector)
[f32d144]1412            self._store_float('ns:SDD', item, 'distance', detector)
[2f4b430]1413
[e9b12eaf]1414            # Detector offset (as a vector)
[f32d144]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)
[2f4b430]1418
[e9b12eaf]1419            # Detector orientation (as a vector)
[f32d144]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)
[2f4b430]1426
[e9b12eaf]1427            # Beam center (as a vector)
[f32d144]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)
[2f4b430]1434
[e9b12eaf]1435            # Pixel size (as a vector)
[f32d144]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)
[2f4b430]1442
[e9b12eaf]1443            self._store_float('ns:slit_length', item, 'slit_length', detector)
[2f4b430]1444
[f32d144]1445            data_info.detector.append(detector)
[e9b12eaf]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)
[2f4b430]1454
[e9b12eaf]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)
[2f4b430]1468
[f32d144]1469            note_list = item.xpath('ns:SASprocessnote',
1470                                   namespaces={'ns': CANSAS_NS})
[e9b12eaf]1471            for note in note_list:
1472                if note.text is not None:
1473                    process.notes.append(note.text.strip())
[2f4b430]1474
[e9b12eaf]1475            data_info.process.append(process)
[2f4b430]1476
[e9b12eaf]1477        # Data info ######################
1478        nodes = dom.xpath('ns:SASdata', namespaces={'ns': CANSAS_NS})
[f32d144]1479        if len(nodes) > 1:
[e9b12eaf]1480            raise RuntimeError, "CanSAS reader is not compatible with multiple SASdata entries"
[2f4b430]1481
[df7ed14]1482        for entry in nodes:
[b1e609c]1483            for item in LIST_OF_DATA_2D_ATTR:
[df7ed14]1484                #get node
[f32d144]1485                node = get_content('ns:%s' % item[0], entry)
[b1e609c]1486                setattr(data_info, item[1], parse_entry_helper(node, item))
[2f4b430]1487
[b1e609c]1488            for item in LIST_OF_DATA_2D_VALUES:
[f32d144]1489                field = get_content('ns:%s' % item[0], entry)
[b1e609c]1490                value_list = []
[df7ed14]1491                if field is not None:
[b1e609c]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)
[44f7c1b]1495                else:
[b1e609c]1496                    setattr(data_info, item[0], numpy.array(value_list))
[2f4b430]1497
[e9b12eaf]1498        return data_info
1499
[61cada5]1500    def _read_cansas(self, path):
[f32d144]1501        """
[5062bbf]1502        Load data and P(r) information from a CanSAS XML file.
[2f4b430]1503
[5062bbf]1504        :param path: file path
[2f4b430]1505
[f32d144]1506        :return: Data1D object if a single SASentry was found,
[5062bbf]1507                    or a list of Data1D objects if multiple entries were found,
1508                    or None of nothing was found
[2f4b430]1509
[5062bbf]1510        :raise RuntimeError: when the file can't be opened
1511        :raise ValueError: when the length of the data vectors are inconsistent
[2f4b430]1512
[61cada5]1513        """
1514        output = []
[f32d144]1515        basename = os.path.basename(path)
[b63dc6e]1516        root, extension = os.path.splitext(basename)
1517        ext = extension.lower()
[61cada5]1518        try:
1519            if os.path.isfile(path):
[2f4b430]1520
[61cada5]1521                #TODO: eventually remove the check for .xml once
1522                # the P(r) writer/reader is truly complete.
[b63dc6e]1523                if  ext in self.ext or \
1524                    ext == '.xml':
[2f4b430]1525
[61cada5]1526                    tree = etree.parse(path, parser=etree.ETCompatXMLParser())
1527                    # Check the format version number
[f32d144]1528                    # Specifying the namespace will take care of the file format version
[61cada5]1529                    root = tree.getroot()
[f32d144]1530                    entry_list = root.xpath('ns:SASentry',
1531                                            namespaces={'ns': CANSAS_NS})
1532                    for entry in entry_list:
[e9b12eaf]1533                        try:
[ac5b69d]1534                            sas_entry, _ = self._parse_save_state_entry(entry)
[e9b12eaf]1535                        except:
[df7ed14]1536                            raise
[61cada5]1537                        fitstate = self._parse_state(entry)
[2f4b430]1538
[b35d3d1]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)
[61cada5]1545            else:
[b63dc6e]1546                self.call_back(format=ext)
[61cada5]1547                raise RuntimeError, "%s is not a file" % path
[b35d3d1]1548
[61cada5]1549            # Return output consistent with the loader's api
[f32d144]1550            if len(output) == 0:
1551                self.call_back(state=None, datainfo=None, format=ext)
[61cada5]1552                return None
1553            else:
[b35d3d1]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)
[ef16f59]1563                    original_fname = state.file[0:max_char]
[f32d144]1564                    state.file = original_fname + ' [' + time_str + ']'
[2f4b430]1565
[b35d3d1]1566                    if state is not None and state.is_data is not None:
[b1e609c]1567                        output[ind].is_data = state.is_data
[2f4b430]1568
[b35d3d1]1569                    output[ind].filename = state.file
1570                    state.data = output[ind]
[f32d144]1571                    state.data.name = output[ind].filename  # state.data_name
[b35d3d1]1572                    state.data.id = state.data_id
1573                    if state.is_data is not None:
1574                        state.data.is_data = state.is_data
[f32d144]1575                    if output[ind].run_name is not None\
1576                        and len(output[ind].run_name) != 0:
[ef16f59]1577                        name = output[ind].run_name
[f32d144]1578                    else:
1579                        name = original_fname
[ef16f59]1580                    state.data.group_id = name
1581                    #store state in fitting
[f32d144]1582                    self.call_back(state=state,
1583                                   datainfo=output[ind], format=ext)
1584                    self.state = state
[ef16f59]1585                return output
[61cada5]1586        except:
[4bee68d]1587            self.call_back(format=ext)
[61cada5]1588            raise
[2f4b430]1589
[61cada5]1590    def write(self, filename, datainfo=None, fitstate=None):
1591        """
[b35d3d1]1592        Write the content of a Data1D as a CanSAS XML file only for standalone
[2f4b430]1593
[5062bbf]1594        :param filename: name of the file to write
1595        :param datainfo: Data1D object
1596        :param fitstate: PageState object
[2f4b430]1597
[61cada5]1598        """
1599        # Sanity check
1600        if self.cansas == True:
1601            # Add fitting information to the XML document
[ef16f59]1602            doc = self.write_toXML(datainfo, fitstate)
[61cada5]1603            # Write the XML document
1604        else:
[ac5b69d]1605            doc = fitstate.toXML(file=filename)
[2f4b430]1606
[ac5b69d]1607        # Save the document no matter the type
1608        fd = open(filename, 'w')
1609        fd.write(doc.toprettyxml())
1610        fd.close()
[2f4b430]1611
[b35d3d1]1612    def write_toXML(self, datainfo=None, state=None):
1613        """
[f32d144]1614        Write toXML, a helper for write(),
1615        could be used by guimanager._on_save()
[2f4b430]1616
[b35d3d1]1617        : return: xml doc
1618        """
[3c44c66]1619
[b35d3d1]1620        if state.data is None:
[79492222]1621            data = sas.dataloader.data_info.Data1D(x=[], y=[])
[a805118]1622            return None
[3b148c3]1623        elif not state.data.is_data:
1624            return None
[f32d144]1625        else:
[b35d3d1]1626            #make sure title and data run is filled up.
[f32d144]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 == {}:
[028a0e8]1630                state.data.run = [str(state.data.name)]
1631                state.data.run_name[0] = state.data.name
[2f4b430]1632
[f32d144]1633            if issubclass(state.data.__class__,
[79492222]1634                          sas.dataloader.data_info.Data1D):
[b35d3d1]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)
[2f4b430]1640
[b35d3d1]1641        if state is not None:
[ac5b69d]1642            doc = state.toXML(doc=doc, file=data.filename, entry_node=sasentry)
[2f4b430]1643
[f32d144]1644        return doc
[2f4b430]1645
[f32d144]1646# Simple html report templet
[2296316]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"
[14d2daa]1655HEADER += "<p class=MsoNormal><b><span ><center><font size='4' >"
1656HEADER += "%s</font></center></span></center></b></p>"
[2296316]1657HEADER += "<p class=MsoNormal>&nbsp;</p>"
[14d2daa]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>"
[2296316]1662FEET_1 = \
1663"""
[3a3dd9c]1664<p class=MsoNormal>&nbsp;</p>
[14d2daa]1665<br>
1666<p class=MsoNormal><b><span ><center> <font size='4' > Graph
[f32d144]1667</font></span></center></b></p>
1668<p class=MsoNormal>&nbsp;</p>
1669<center>
[14d2daa]1670<br><font size='4' >Model Computation</font>
[f32d144]1671<br><font size='4' >Data: "%s"</font><br>
[2296316]1672"""
1673FEET_2 = \
1674"""
[f32d144]1675<img src="%s" >
[2296316]1676</img>
1677"""
1678FEET_3 = \
1679"""
[f32d144]1680</center>
[2296316]1681</div>
1682</body>
1683</html>
1684"""
1685ELINE = "<p class=MsoNormal>&nbsp;</p>"
1686
[3c44c66]1687if __name__ == "__main__":
1688    state = PageState(parent=None)
1689    #state.toXML()
1690    """
[2f4b430]1691
[3c44c66]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
[f32d144]1706    db = bsddb.btopen('file_state.db', 'c')
[3c44c66]1707    val = (pickle.dumps(state), "hello", "hi")
[f32d144]1708    db['state1'] = pickle.dumps(val)
[3c44c66]1709    print pickle.loads(db['state1'])
1710    state.data_name = "hello---->22"
[f32d144]1711    db['state2'] = pickle.dumps(state)
[3c44c66]1712    state.data_name = "hello---->2"
[f32d144]1713    db['state3'] = pickle.dumps(state)
[3c44c66]1714    del db['state3']
1715    state.data_name = "hello---->3"
[f32d144]1716    db['state4'] = pickle.dumps(state)
[3c44c66]1717    new_state = pickle.loads(db['state1'])
1718    #print db.last()
1719    db.set_location('state2')
1720    state.data_name = "hello---->5"
[f32d144]1721    db['aastate5'] = pickle.dumps(state)
[3c44c66]1722    db.keys().sort()
1723    print pickle.loads(db['state2'])
[2f4b430]1724
[f32d144]1725    db.close()
Note: See TracBrowser for help on using the repository browser.