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

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 ab06de7 was 7673ecd, checked in by Paul Kienzle <pkienzle@…>, 9 years ago

refactor support for sum model; put tracebacks in logging errors

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