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

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.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 77910cf was 1c6bad0, checked in by krzywon, 8 years ago

Added patch to handle formfactor and category names that aren't saved in v4.0.1 and earlier. Start on loading str_parameters, but not finished.

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