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

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 d5aff7f was d5aff7f, checked in by krzywon, 7 years ago

Small code cleanup to only fetch dx_min from save state a single time.

  • Property mode set to 100644
File size: 58.1 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
[4387385]35
[a95ae9a]36# Information to read/write state as xml
[61cada5]37FITTING_NODE_NAME = 'fitting_plug_in'
38CANSAS_NS = "cansas1d/1.0"
39
[4387385]40CUSTOM_MODEL = 'Plugin Models'
41CUSTOM_MODEL_OLD = 'Customized Models'
42
[b1e609c]43LIST_OF_DATA_ATTRIBUTES = [["is_data", "is_data", "bool"],
44                           ["group_id", "data_group_id", "string"],
45                           ["data_name", "data_name", "string"],
46                           ["data_id", "data_id", "string"],
47                           ["name", "name", "string"],
48                           ["data_name", "data_name", "string"]]
[acf8e4a5]49LIST_OF_STATE_ATTRIBUTES = [["qmin", "qmin", "float"],
[b1e609c]50                            ["qmax", "qmax", "float"],
51                            ["npts", "npts", "float"],
52                            ["categorycombobox", "categorycombobox", "string"],
[c8e1996]53                            ["formfactorcombobox", "formfactorcombobox",
54                             "string"],
55                            ["structurecombobox", "structurecombobox",
56                             "string"],
[b1e609c]57                            ["multi_factor", "multi_factor", "float"],
58                            ["magnetic_on", "magnetic_on", "bool"],
59                            ["enable_smearer", "enable_smearer", "bool"],
60                            ["disable_smearer", "disable_smearer", "bool"],
61                            ["pinhole_smearer", "pinhole_smearer", "bool"],
62                            ["slit_smearer", "slit_smearer", "bool"],
63                            ["enable_disp", "enable_disp", "bool"],
64                            ["disable_disp", "disable_disp", "bool"],
65                            ["dI_noweight", "dI_noweight", "bool"],
66                            ["dI_didata", "dI_didata", "bool"],
67                            ["dI_sqrdata", "dI_sqrdata", "bool"],
68                            ["dI_idata", "dI_idata", "bool"],
69                            ["enable2D", "enable2D", "bool"],
70                            ["cb1", "cb1", "bool"],
71                            ["tcChi", "tcChi", "float"],
72                            ["smearer", "smearer", "float"],
73                            ["smear_type", "smear_type", "string"],
[c8e1996]74                            ["dq_l", "dq_l", "float"],
75                            ["dq_r", "dq_r", "float"],
[d85f1d8a]76                            ["dx_percent", "dx_percent", "float"],
[b1e609c]77                            ["dxl", "dxl", "float"],
78                            ["dxw", "dxw", "float"]]
79
80LIST_OF_MODEL_ATTRIBUTES = [["values", "values"],
[11a7e11]81                            ["weights", "weights"]]
82
[b1e609c]83DISPERSION_LIST = [["disp_obj_dict", "_disp_obj_dict", "string"]]
[2296316]84
[b1e609c]85LIST_OF_STATE_PARAMETERS = [["parameters", "parameters"],
[f32d144]86                            ["str_parameters", "str_parameters"],
[61cada5]87                            ["orientation_parameters", "orientation_params"],
[c8e1996]88                            ["dispersity_parameters",
89                             "orientation_params_disp"],
[f32d144]90                            ["fixed_param", "fixed_param"],
91                            ["fittable_param", "fittable_param"]]
[b1e609c]92LIST_OF_DATA_2D_ATTR = [["xmin", "xmin", "float"],
[f32d144]93                        ["xmax", "xmax", "float"],
94                        ["ymin", "ymin", "float"],
95                        ["ymax", "ymax", "float"],
96                        ["_xaxis", "_xaxis", "string"],
[df7ed14]97                        ["_xunit", "_xunit", "string"],
[f32d144]98                        ["_yaxis", "_yaxis", "string"],
99                        ["_yunit", "_yunit", "string"],
100                        ["_zaxis", "_zaxis", "string"],
101                        ["_zunit", "_zunit", "string"]]
[b1e609c]102LIST_OF_DATA_2D_VALUES = [["qx_data", "qx_data", "float"],
103                          ["qy_data", "qy_data", "float"],
104                          ["dqx_data", "dqx_data", "float"],
105                          ["dqy_data", "dqy_data", "float"],
106                          ["data", "data", "float"],
107                          ["q_data", "q_data", "float"],
108                          ["err_data", "err_data", "float"],
109                          ["mask", "mask", "bool"]]
[61cada5]110
[f32d144]111
112def parse_entry_helper(node, item):
[0b12abb5]113    """
114    Create a numpy list from value extrated from the node
[2f4b430]115
[0b12abb5]116    :param node: node from each the value is stored
117    :param item: list name of three strings.the two first are name of data
[f32d144]118        attribute and the third one is the type of the value of that
[0b12abb5]119        attribute. type can be string, float, bool, etc.
[2f4b430]120
[0b12abb5]121    : return: numpy array
122    """
123    if node is not None:
124        if item[2] == "string":
125            return str(node.get(item[0]).strip())
126        elif item[2] == "bool":
127            try:
[e3d1423]128                return node.get(item[0]).strip() == "True"
[c8e1996]129            except Exception:
[0b12abb5]130                return None
131        else:
132            try:
133                return float(node.get(item[0]))
[c8e1996]134            except Exception:
[0b12abb5]135                return None
[2f4b430]136
137
[cfc0913]138class PageState(object):
[c77d859]139    """
[5062bbf]140    Contains information to reconstruct a page of the fitpanel.
[c77d859]141    """
[61cada5]142    def __init__(self, parent=None, model=None, data=None):
[f32d144]143        """
[5062bbf]144        Initialize the current state
[2f4b430]145
[5062bbf]146        :param model: a selected model within a page
[f32d144]147        :param data:
[2f4b430]148
[c77d859]149        """
[61cada5]150        self.file = None
[a95ae9a]151        # Time of state creation
[11a7e11]152        self.timestamp = time.time()
[a95ae9a]153        # Data member to store the dispersion object created
[c77d859]154        self._disp_obj_dict = {}
[a95ae9a]155        # ------------------------
156        # Data used for fitting
[c77d859]157        self.data = data
[2296316]158        # model data
159        self.theory_data = None
[a95ae9a]160        # Is 2D
[2296316]161        self.is_2D = False
162        self.images = None
[2f4b430]163
[a95ae9a]164        # save additional information on data that dataloader.reader
165        # does not read
[61cada5]166        self.is_data = None
167        self.data_name = ""
[c0ff8cc]168
[61cada5]169        if self.data is not None:
170            self.data_name = self.data.name
171        self.data_id = None
172        if self.data is not None and hasattr(self.data, "id"):
173            self.data_id = self.data.id
174        self.data_group_id = None
175        if self.data is not None and hasattr(self.data, "group_id"):
176            self.data_group_id = self.data.group_id
[2f4b430]177
[a95ae9a]178        # reset True change the state of existing button
[61cada5]179        self.reset = False
[2f4b430]180
[c77d859]181        # flag to allow data2D plot
[cfc0913]182        self.enable2D = False
[c77d859]183        # model on which the fit would be performed
184        self.model = model
[93f0a862]185        self.m_name = None
[a95ae9a]186        # list of process done to model
[dce84c0]187        self.process = []
[a95ae9a]188        # fit page manager
[c77d859]189        self.manager = None
[a95ae9a]190        # Store the parent of this panel parent
[c77d859]191        # For this application fitpanel is the parent
[f32d144]192        self.parent = parent
[c77d859]193        # Event_owner is the owner of model event
194        self.event_owner = None
[a95ae9a]195        # page name
[cfc0913]196        self.page_name = ""
[c8e1996]197        # Contains link between model, its parameters, and panel organization
[61cada5]198        self.parameters = []
[fb59ed9]199        # String parameter list that can not be fitted
200        self.str_parameters = []
[f32d144]201        # Contains list of parameters that cannot be fitted and reference to
[a95ae9a]202        # panel objects
[61cada5]203        self.fixed_param = []
[f32d144]204        # Contains list of parameters with dispersity and reference to
[a95ae9a]205        # panel objects
[61cada5]206        self.fittable_param = []
[a95ae9a]207        # orientation parameters
[61cada5]208        self.orientation_params = []
[a95ae9a]209        # orientation parameters for gaussian dispersity
[61cada5]210        self.orientation_params_disp = []
[a95ae9a]211        # smearer info
[61cada5]212        self.smearer = None
[7609f1a]213        self.smear_type = None
214        self.dq_l = None
215        self.dq_r = None
[d85f1d8a]216        self.dx_percent = None
[096181d]217        self.dx_old = False
[db8fd5b]218        self.dxl = None
219        self.dxw = None
[a95ae9a]220        # list of dispersion parameters
[f32d144]221        self.disp_list = []
[61cada5]222        if self.model is not None:
[71f0373]223            self.disp_list = self.model.getDispParamList()
[2296316]224
[61cada5]225        self.disp_cb_dict = {}
[2296316]226        self.values = {}
227        self.weights = {}
[2f4b430]228
[a95ae9a]229        # contains link between a model and selected parameters to fit
[61cada5]230        self.param_toFit = []
[a95ae9a]231        # dictionary of model type and model class
[cfc0913]232        self.model_list_box = None
[a95ae9a]233        # save the state of the context menu
[61cada5]234        self.saved_states = {}
[a95ae9a]235        # save selection of combobox
[3b9e023]236        self.formfactorcombobox = None
[ea5fa58]237        self.categorycombobox = None
[f32d144]238        self.structurecombobox = None
[c0ff8cc]239
[a95ae9a]240        # radio box to select type of model
241        # self.shape_rbutton = False
242        # self.shape_indep_rbutton = False
243        # self.struct_rbutton = False
244        # self.plugin_rbutton = False
245        # the indice of the current selection
[b787e68c]246        self.disp_box = 0
[a95ae9a]247        # Qrange
248        # Q range
[61cada5]249        self.qmin = 0.001
250        self.qmax = 0.1
[a95ae9a]251        # reset data range
[0b12abb5]252        self.qmax_x = None
253        self.qmin_x = None
[2f4b430]254
[cfc0913]255        self.npts = None
[61cada5]256        self.name = ""
[4523b68]257        self.multi_factor = None
[318b5bbb]258        self.magnetic_on = False
[a95ae9a]259        # enable smearering state
[cfc0913]260        self.enable_smearer = False
[fc6ea43]261        self.disable_smearer = True
[7609f1a]262        self.pinhole_smearer = False
[f32d144]263        self.slit_smearer = False
[55bb249c]264        # weighting options
265        self.dI_noweight = False
266        self.dI_didata = True
267        self.dI_sqrdata = False
[f32d144]268        self.dI_idata = False
[a95ae9a]269        # disperity selection
[61cada5]270        self.enable_disp = False
271        self.disable_disp = True
[2f4b430]272
[a95ae9a]273        # state of selected all check button
[cfc0913]274        self.cb1 = False
[a95ae9a]275        # store value of chisqr
[61cada5]276        self.tcChi = None
[9e0aa69a]277        self.version = (1,0,0)
[2f4b430]278
[6f023e8]279    def clone(self):
[61cada5]280        """
[5062bbf]281        Create a new copy of the current object
[61cada5]282        """
283        model = None
284        if self.model is not None:
[6f023e8]285            model = self.model.clone()
[bb70474]286            model.name = self.model.name
[61cada5]287        obj = PageState(self.parent, model=model)
288        obj.file = copy.deepcopy(self.file)
[6f023e8]289        obj.data = copy.deepcopy(self.data)
[61cada5]290        if self.data is not None:
291            self.data_name = self.data.name
292        obj.data_name = self.data_name
293        obj.is_data = self.is_data
[dcf29d7]294        obj.model_list_box = copy.deepcopy(self.model_list_box)
[2f4b430]295
[ea5fa58]296        obj.categorycombobox = self.categorycombobox
[61cada5]297        obj.formfactorcombobox = self.formfactorcombobox
[f32d144]298        obj.structurecombobox = self.structurecombobox
[2f4b430]299
[a95ae9a]300        # obj.shape_rbutton = self.shape_rbutton
301        # obj.shape_indep_rbutton = self.shape_indep_rbutton
302        # obj.struct_rbutton = self.struct_rbutton
303        # obj.plugin_rbutton = self.plugin_rbutton
[2f4b430]304
[dcf29d7]305        obj.manager = self.manager
306        obj.event_owner = self.event_owner
[71f0373]307        obj.disp_list = copy.deepcopy(self.disp_list)
[2f4b430]308
[c477b31]309        obj.enable2D = copy.deepcopy(self.enable2D)
[cfc0913]310        obj.parameters = copy.deepcopy(self.parameters)
[fb59ed9]311        obj.str_parameters = copy.deepcopy(self.str_parameters)
[cfc0913]312        obj.fixed_param = copy.deepcopy(self.fixed_param)
313        obj.fittable_param = copy.deepcopy(self.fittable_param)
[f32d144]314        obj.orientation_params = copy.deepcopy(self.orientation_params)
[c8e1996]315        obj.orientation_params_disp = \
316            copy.deepcopy(self.orientation_params_disp)
[b787e68c]317        obj.enable_disp = copy.deepcopy(self.enable_disp)
[fc6ea43]318        obj.disable_disp = copy.deepcopy(self.disable_disp)
[0aeabc6]319        obj.tcChi = self.tcChi
[2f4b430]320
[f32d144]321        if len(self._disp_obj_dict) > 0:
322            for k, v in self._disp_obj_dict.iteritems():
323                obj._disp_obj_dict[k] = v
324        if len(self.disp_cb_dict) > 0:
325            for k, v in self.disp_cb_dict.iteritems():
326                obj.disp_cb_dict[k] = v
327        if len(self.values) > 0:
328            for k, v in self.values.iteritems():
[2296316]329                obj.values[k] = v
[f32d144]330        if len(self.weights) > 0:
331            for k, v in self.weights.iteritems():
[2296316]332                obj.weights[k] = v
[b787e68c]333        obj.enable_smearer = copy.deepcopy(self.enable_smearer)
[fc6ea43]334        obj.disable_smearer = copy.deepcopy(self.disable_smearer)
[7609f1a]335        obj.pinhole_smearer = copy.deepcopy(self.pinhole_smearer)
336        obj.slit_smearer = copy.deepcopy(self.slit_smearer)
337        obj.smear_type = copy.deepcopy(self.smear_type)
[55bb249c]338        obj.dI_noweight = copy.deepcopy(self.dI_noweight)
339        obj.dI_didata = copy.deepcopy(self.dI_didata)
340        obj.dI_sqrdata = copy.deepcopy(self.dI_sqrdata)
341        obj.dI_idata = copy.deepcopy(self.dI_idata)
[7609f1a]342        obj.dq_l = copy.deepcopy(self.dq_l)
343        obj.dq_r = copy.deepcopy(self.dq_r)
[d85f1d8a]344        obj.dx_percent = copy.deepcopy(self.dx_percent)
[096181d]345        obj.dx_old = copy.deepcopy(self.dx_old)
[db8fd5b]346        obj.dxl = copy.deepcopy(self.dxl)
[2f4b430]347        obj.dxw = copy.deepcopy(self.dxw)
[b787e68c]348        obj.disp_box = copy.deepcopy(self.disp_box)
349        obj.qmin = copy.deepcopy(self.qmin)
350        obj.qmax = copy.deepcopy(self.qmax)
[c0ff8cc]351        obj.multi_factor = self.multi_factor
[318b5bbb]352        obj.magnetic_on = self.magnetic_on
[f32d144]353        obj.npts = copy.deepcopy(self.npts)
[b787e68c]354        obj.cb1 = copy.deepcopy(self.cb1)
[3370922]355        obj.smearer = copy.deepcopy(self.smearer)
[fa487d93]356        obj.version = copy.deepcopy(self.version)
[2f4b430]357
[a074145]358        for name, state in self.saved_states.iteritems():
359            copy_name = copy.deepcopy(name)
360            copy_state = state.clone()
[f32d144]361            obj.saved_states[copy_name] = copy_state
[6f023e8]362        return obj
[2f4b430]363
[a321c52]364    def _old_first_model(self):
[8898558b]365        """
[0de74af]366        Handle save states from 4.0.1 and before where the first item in the
367        selection boxes of category, formfactor and structurefactor were not
368        saved.
[8898558b]369        :return: None
370        """
[71601312]371        if self.categorycombobox == CUSTOM_MODEL_OLD:
372            self.categorycombobox = CUSTOM_MODEL
[1c6bad0]373        if self.formfactorcombobox == '':
[6d2b50b]374            FIRST_FORM = {
375                'Shapes' : 'BCCrystalModel',
376                'Uncategorized' : 'LineModel',
377                'StructureFactor' : 'HardsphereStructure',
378                'Ellipsoid' : 'core_shell_ellipsoid',
379                'Lamellae' : 'lamellar',
380                'Paracrystal' : 'bcc_paracrystal',
381                'Parallelepiped' : 'core_shell_parallelepiped',
382                'Shape Independent' : 'be_polyelectrolyte',
383                'Sphere' : 'adsorbed_layer',
384                'Structure Factor' : 'hardsphere',
[4387385]385                CUSTOM_MODEL : ''
[6d2b50b]386            }
387            if self.categorycombobox == '':
388                if len(self.parameters) == 3:
389                    self.categorycombobox = "Shape-Independent"
390                    self.formfactorcombobox = 'PowerLawAbsModel'
391                elif len(self.parameters) == 9:
392                    self.categorycombobox = 'Cylinder'
393                    self.formfactorcombobox = 'barbell'
394                else:
395                    msg = "Save state does not have enough information to load"
396                    msg += " the all of the data."
397                    logging.warning(msg=msg)
398            else:
399                self.formfactorcombobox = FIRST_FORM[self.categorycombobox]
[8898558b]400
[12361fd]401    @staticmethod
[ca224b1]402    def param_remap_to_sasmodels_convert(params, is_string=False):
[3d8c49a]403        """
[1c6bad0]404        Remaps the parameters for sasmodels conversion
[3d8c49a]405
[1c6bad0]406        :param params: list of parameters (likely self.parameters)
407        :return: remapped dictionary of parameters
[3d8c49a]408        """
409        p = dict()
[1c6bad0]410        for fittable, name, value, _, uncert, lower, upper, units in params:
[3d8c49a]411            if not value:
412                value = numpy.nan
[9eb8fae]413            if not uncert or uncert[1] == '' or uncert[1] == 'None':
[3d8c49a]414                uncert[0] = False
415                uncert[1] = numpy.nan
[9eb8fae]416            if not upper or upper[1] == '' or upper[1] == 'None':
[3d8c49a]417                upper[0] = False
418                upper[1] = numpy.nan
[9eb8fae]419            if not lower or lower[1] == '' or lower[1] == 'None':
[3d8c49a]420                lower[0] = False
421                lower[1] = numpy.nan
[0633048]422            if is_string:
423                p[name] = str(value)
424            else:
425                p[name] = float(value)
[64c7f39]426            p[name + ".fittable"] = bool(fittable)
427            p[name + ".std"] = float(uncert[1])
428            p[name + ".upper"] = float(upper[1])
429            p[name + ".lower"] = float(lower[1])
430            p[name + ".units"] = units
[1c6bad0]431        return p
432
[12361fd]433    @staticmethod
[ca224b1]434    def param_remap_from_sasmodels_convert(params):
[1c6bad0]435        """
436        Converts {name : value} map back to [] param list
437        :param params: parameter map returned from sasmodels
438        :return: None
439        """
440        p_map = []
441        for name, info in params.iteritems():
442            if ".fittable" in name or ".std" in name or ".upper" in name or \
443                            ".lower" in name or ".units" in name:
444                pass
445            else:
446                fittable = params.get(name + ".fittable", True)
447                std = params.get(name + ".std", '0.0')
448                upper = params.get(name + ".upper", 'inf')
449                lower = params.get(name + ".lower", '-inf')
450                units = params.get(name + ".units")
451                if std is not None and std is not numpy.nan:
452                    std = [True, str(std)]
453                else:
454                    std = [False, '']
455                if lower is not None and lower is not numpy.nan:
456                    lower = [True, str(lower)]
457                else:
458                    lower = [True, '-inf']
459                if upper is not None and upper is not numpy.nan:
460                    upper = [True, str(upper)]
461                else:
462                    upper = [True, 'inf']
463                param_list = [bool(fittable), str(name), str(info),
464                              "+/-", std, lower, upper, str(units)]
465                p_map.append(param_list)
466        return p_map
[377c19a2]467
[1c6bad0]468    def _convert_to_sasmodels(self):
469        """
470        Convert parameters to a form usable by sasmodels converter
471
472        :return: None
473        """
474        # Create conversion dictionary to send to sasmodels
[a321c52]475        self._old_first_model()
[1c6bad0]476        p = self.param_remap_to_sasmodels_convert(self.parameters)
[9e0aa69a]477        structurefactor, params = convert.convert_model(self.structurecombobox,
478                                                        p, False, self.version)
479        formfactor, params = convert.convert_model(self.formfactorcombobox,
480                                                   params, False, self.version)
[1c6bad0]481        if len(self.str_parameters) > 0:
[0633048]482            str_pars = self.param_remap_to_sasmodels_convert(
483                self.str_parameters, True)
484            formfactor, str_params = convert.convert_model(
[fa487d93]485                self.formfactorcombobox, str_pars, False, self.version)
[0633048]486            for key, value in str_params.iteritems():
487                params[key] = value
[3d8c49a]488
[fa487d93]489        if self.formfactorcombobox == 'SphericalSLDModel':
490            self.multi_factor += 1
491        self.formfactorcombobox = formfactor
492        self.structurecombobox = structurefactor
493        self.parameters = []
494        self.parameters = self.param_remap_from_sasmodels_convert(params)
[3d8c49a]495
[61cada5]496    def _repr_helper(self, list, rep):
497        """
[5062bbf]498        Helper method to print a state
[61cada5]499        """
500        for item in list:
[f32d144]501            rep += "parameter name: %s \n" % str(item[1])
502            rep += "value: %s\n" % str(item[2])
503            rep += "selected: %s\n" % str(item[0])
504            rep += "error displayed : %s \n" % str(item[4][0])
505            rep += "error value:%s \n" % str(item[4][1])
506            rep += "minimum displayed : %s \n" % str(item[5][0])
507            rep += "minimum value : %s \n" % str(item[5][1])
508            rep += "maximum displayed : %s \n" % str(item[6][0])
509            rep += "maximum value : %s \n" % str(item[6][1])
510            rep += "parameter unit: %s\n\n" % str(item[7])
[240b9966]511        return rep
[2f4b430]512
[61cada5]513    def __repr__(self):
[f32d144]514        """
[5062bbf]515        output string for printing
[11a7e11]516        """
[f32d144]517        rep = "\nState name: %s\n" % self.file
[11a7e11]518        t = time.localtime(self.timestamp)
[deff488]519        time_str = time.strftime("%b %d %Y %H;%M;%S ", t)
[c0ff8cc]520
[f32d144]521        rep += "State created: %s\n" % time_str
[998ca90]522        rep += "State form factor combobox selection: %s\n" % \
523               self.formfactorcombobox
524        rep += "State structure factor combobox selection: %s\n" % \
525               self.structurecombobox
[f32d144]526        rep += "is data : %s\n" % self.is_data
527        rep += "data's name : %s\n" % self.data_name
528        rep += "data's id : %s\n" % self.data_id
[a95ae9a]529        if self.model is not None:
[93f0a862]530            m_name = self.model.__class__.__name__
531            if m_name == 'Model':
532                m_name = self.m_name
[f32d144]533            rep += "model name : %s\n" % m_name
[74dc0a4]534        else:
535            rep += "model name : None\n"
[f32d144]536        rep += "multi_factor : %s\n" % str(self.multi_factor)
[2f4b430]537        rep += "magnetic_on : %s\n" % str(self.magnetic_on)
[ea5fa58]538        rep += "model type (Category) selected: %s\n" % self.categorycombobox
[f32d144]539        rep += "data : %s\n" % str(self.data)
[a95ae9a]540        rep += "Plotting Range: min: %s, max: %s, steps: %s\n" % \
[c8e1996]541               (str(self.qmin), str(self.qmax), str(self.npts))
[f32d144]542        rep += "Dispersion selection : %s\n" % str(self.disp_box)
543        rep += "Smearing enable : %s\n" % str(self.enable_smearer)
544        rep += "Smearing disable : %s\n" % str(self.disable_smearer)
545        rep += "Pinhole smearer enable : %s\n" % str(self.pinhole_smearer)
546        rep += "Slit smearer enable : %s\n" % str(self.slit_smearer)
547        rep += "Dispersity enable : %s\n" % str(self.enable_disp)
548        rep += "Dispersity disable : %s\n" % str(self.disable_disp)
549        rep += "Slit smearer enable: %s\n" % str(self.slit_smearer)
[2f4b430]550
[f32d144]551        rep += "dI_noweight : %s\n" % str(self.dI_noweight)
552        rep += "dI_didata : %s\n" % str(self.dI_didata)
553        rep += "dI_sqrdata : %s\n" % str(self.dI_sqrdata)
554        rep += "dI_idata : %s\n" % str(self.dI_idata)
[2f4b430]555
[f32d144]556        rep += "2D enable : %s\n" % str(self.enable2D)
[c8e1996]557        rep += "All parameters checkbox selected: %s\n" % self.cb1
[f32d144]558        rep += "Value of Chisqr : %s\n" % str(self.tcChi)
[c8e1996]559        rep += "Smear object : %s\n" % self.smearer
560        rep += "Smear type : %s\n" % self.smear_type
[f32d144]561        rep += "dq_l  : %s\n" % self.dq_l
562        rep += "dq_r  : %s\n" % self.dq_r
[d85f1d8a]563        rep += "dx_percent  : %s\n" % str(self.dx_percent)
[db8fd5b]564        rep += "dxl  : %s\n" % str(self.dxl)
[2f4b430]565        rep += "dxw : %s\n" % str(self.dxw)
[f32d144]566        rep += "model  : %s\n\n" % str(self.model)
[2296316]567        temp_parameters = []
568        temp_fittable_param = []
569        if self.data.__class__.__name__ == "Data2D":
570            self.is_2D = True
571        else:
572            self.is_2D = False
573        if self.data is not None:
574            if not self.is_2D:
575                for item in self.parameters:
[a95ae9a]576                    if item not in self.orientation_params:
[2296316]577                        temp_parameters.append(item)
578                for item in self.fittable_param:
[a95ae9a]579                    if item not in self.orientation_params_disp:
[2296316]580                        temp_fittable_param.append(item)
581            else:
582                temp_parameters = self.parameters
583                temp_fittable_param = self.fittable_param
[2f4b430]584
[a95ae9a]585            rep += "number parameters(self.parameters): %s\n" % \
586                   len(temp_parameters)
[f32d144]587            rep = self._repr_helper(list=temp_parameters, rep=rep)
[a95ae9a]588            rep += "number str_parameters(self.str_parameters): %s\n" % \
589                   len(self.str_parameters)
[f32d144]590            rep = self._repr_helper(list=self.str_parameters, rep=rep)
[a95ae9a]591            rep += "number fittable_param(self.fittable_param): %s\n" % \
592                   len(temp_fittable_param)
[f32d144]593            rep = self._repr_helper(list=temp_fittable_param, rep=rep)
[61cada5]594        return rep
[2296316]595
596    def set_report_string(self):
597        """
[f32d144]598        Get the values (strings) from __str__ for report
[2296316]599        """
[d06ae30]600        # Dictionary of the report strings
[2296316]601        repo_time = ""
602        model_name = ""
603        title = ""
604        title_name = ""
605        file_name = ""
606        param_string = ""
607        paramval_string = ""
608        chi2_string = ""
609        q_range = ""
610        strings = self.__repr__()
611        lines = strings.split('\n')
612
613        # get all string values from __str__()
614        for line in lines:
615            value = ""
616            content = line.split(":")
617            name = content[0]
618            try:
619                value = content[1]
[7673ecd]620            except Exception:
[c8e1996]621                msg = "Report string expected 'name: value' but got %r" % line
[6c382da]622                logging.error(msg)
[b7c6a4a]623            if name.count("State created"):
624                repo_time = "" + value
[2296316]625            if name.count("parameter name"):
626                val_name = value.split(".")
627                if len(val_name) > 1:
628                    if val_name[1].count("width"):
629                        param_string += value + ','
630                    else:
631                        continue
632                else:
633                    param_string += value + ','
634            if name == "value":
635                param_string += value + ','
[c8e1996]636            fixed_parameter = False
[b1e609c]637            if name == "selected":
638                if value == u' False':
639                    fixed_parameter = True
[2296316]640            if name == "error value":
[b1e609c]641                if fixed_parameter:
642                    param_string += '(fixed),'
643                else:
[d06ae30]644                    param_string += value + ','
[2296316]645            if name == "parameter unit":
[f32d144]646                param_string += value + ':'
[2296316]647            if name == "Value of Chisqr ":
648                chi2 = ("Chi2/Npts = " + value)
649                chi2_string = CENTRE % chi2
650            if name == "Title":
651                if len(value.strip()) == 0:
652                    continue
653                title = value + " [" + repo_time + "]"
654                title_name = HEADER % title
655            if name == "data ":
656                try:
[b1e609c]657                    file_value = ("File name:" + content[2])
658                    file_name = CENTRE % file_value
[2296316]659                    if len(title) == 0:
660                        title = content[2] + " [" + repo_time + "]"
661                        title_name = HEADER % title
[7673ecd]662                except Exception:
[6c382da]663                    msg = "While parsing 'data: ...'\n"
664                    logging.error(msg + traceback.format_exc())
[74dc0a4]665            if name == "model name ":
666                try:
667                    modelname = "Model name:" + content[1]
668                except:
669                    modelname = "Model name:" + " NAN"
[f32d144]670                model_name = CENTRE % modelname
[2f4b430]671
[2296316]672            if name == "Plotting Range":
673                try:
674                    q_range = content[1] + " = " + content[2] \
675                            + " = " + content[3].split(",")[0]
676                    q_name = ("Q Range:    " + q_range)
677                    q_range = CENTRE % q_name
[7673ecd]678                except Exception:
[6c382da]679                    msg = "While parsing 'Plotting Range: ...'\n"
680                    logging.error(msg + traceback.format_exc())
[2296316]681        paramval = ""
[f32d144]682        for lines in param_string.split(":"):
[2296316]683            line = lines.split(",")
[f32d144]684            if len(lines) > 0:
685                param = line[0]
[2296316]686                param += " = " + line[1]
687                if len(line[2].split()) > 0 and not line[2].count("None"):
688                    param += " +- " + line[2]
689                if len(line[3].split()) > 0 and not line[3].count("None"):
690                    param += " " + line[3]
691                if not paramval.count(param):
[f32d144]692                    paramval += param + "\n"
[2296316]693                    paramval_string += CENTRE % param + "\n"
[2f4b430]694
[a95ae9a]695        text_string = "\n\n\n%s\n\n%s\n%s\n%s\n\n%s" % \
696                      (title, file, q_name, chi2, paramval)
[2f4b430]697
[f5bdb4a]698        title_name = self._check_html_format(title_name)
699        file_name = self._check_html_format(file_name)
700        title = self._check_html_format(title)
[2f4b430]701
[2296316]702        html_string = title_name + "\n" + file_name + \
[f32d144]703                                   "\n" + model_name + \
[2296316]704                                   "\n" + q_range + \
705                                   "\n" + chi2_string + \
706                                   "\n" + ELINE + \
707                                   "\n" + paramval_string + \
708                                   "\n" + ELINE + \
709                                   "\n" + FEET_1 % title + \
[f32d144]710                                   "\n" + FEET_2
[2f4b430]711
[2296316]712        return html_string, text_string, title
[2f4b430]713
[f5bdb4a]714    def _check_html_format(self, name):
715        """
716        Check string '%' for html format
717        """
718        if name.count('%'):
719            name = name.replace('%', '&#37')
[2f4b430]720
[f5bdb4a]721        return name
[2f4b430]722
[2296316]723    def report(self, figs=None, canvases=None):
724        """
725        Invoke report dialog panel
[2f4b430]726
[2296316]727        : param figs: list of pylab figures [list]
728        """
[d85c194]729        from sas.sasgui.perspectives.fitting.report_dialog import ReportDialog
[2296316]730        # get the strings for report
731        html_str, text_str, title = self.set_report_string()
732        # Allow 2 figures to append
733        if len(figs) == 1:
[f32d144]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_3
[2296316]740        elif len(figs) > 2:
[f32d144]741            add_str = ELINE
742            add_str += FEET_2 % ("%s")
743            add_str += ELINE
744            add_str += FEET_2 % ("%s")
745            add_str += ELINE
746            add_str += FEET_3
[2296316]747        else:
748            add_str = ""
[c0ff8cc]749
[2296316]750        # final report html strings
751        report_str = html_str % ("%s") + add_str
752
753        # make plot image
754        images = self.set_plot_state(figs, canvases)
[f32d144]755        report_list = [report_str, text_str, images]
[6f16e25]756        dialog = ReportDialog(report_list, None, wx.ID_ANY, "")
[d838715]757        dialog.Show()
[2f4b430]758
[c8e1996]759    def _to_xml_helper(self, thelist, element, newdoc):
[61cada5]760        """
[5062bbf]761        Helper method to create xml file for saving state
[61cada5]762        """
[eddb6ec]763        for item in thelist:
[61cada5]764            sub_element = newdoc.createElement('parameter')
765            sub_element.setAttribute('name', str(item[1]))
766            sub_element.setAttribute('value', str(item[2]))
767            sub_element.setAttribute('selected_to_fit', str(item[0]))
768            sub_element.setAttribute('error_displayed', str(item[4][0]))
769            sub_element.setAttribute('error_value', str(item[4][1]))
770            sub_element.setAttribute('minimum_displayed', str(item[5][0]))
771            sub_element.setAttribute('minimum_value', str(item[5][1]))
772            sub_element.setAttribute('maximum_displayed', str(item[6][0]))
773            sub_element.setAttribute('maximum_value', str(item[6][1]))
774            sub_element.setAttribute('unit', str(item[7]))
775            element.appendChild(sub_element)
[2f4b430]776
[c8e1996]777    def to_xml(self, file="fitting_state.fitv", doc=None,
778               entry_node=None, batch_fit_state=None):
[61cada5]779        """
[a95ae9a]780        Writes the state of the fit panel to file, as XML.
[2f4b430]781
[5062bbf]782        Compatible with standalone writing, or appending to an
[998ca90]783        already existing XML document. In that case, the XML document is
784        required. An optional entry node in the XML document may also be given.
[2f4b430]785
[5062bbf]786        :param file: file to write to
787        :param doc: XML document object [optional]
[998ca90]788        :param entry_node: XML node within the XML document at which we
789                           will append the data [optional]
[c8e1996]790        :param batch_fit_state: simultaneous fit state
[61cada5]791        """
792        from xml.dom.minidom import getDOMImplementation
[71f0373]793
[61cada5]794        # Check whether we have to write a standalone XML file
795        if doc is None:
796            impl = getDOMImplementation()
[f32d144]797            doc_type = impl.createDocumentType(FITTING_NODE_NAME, "1.0", "1.0")
[61cada5]798            newdoc = impl.createDocument(None, FITTING_NODE_NAME, doc_type)
799            top_element = newdoc.documentElement
800        else:
801            # We are appending to an existing document
802            newdoc = doc
[ac5b69d]803            try:
804                top_element = newdoc.createElement(FITTING_NODE_NAME)
805            except:
806                string = etree.tostring(doc, pretty_print=True)
807                newdoc = parseString(string)
808                top_element = newdoc.createElement(FITTING_NODE_NAME)
[61cada5]809            if entry_node is None:
810                newdoc.documentElement.appendChild(top_element)
811            else:
[ac5b69d]812                try:
813                    entry_node.appendChild(top_element)
814                except:
815                    node_name = entry_node.tag
816                    node_list = newdoc.getElementsByTagName(node_name)
817                    entry_node = node_list.item(0)
818                    entry_node.appendChild(top_element)
[2f4b430]819
[61cada5]820        attr = newdoc.createAttribute("version")
[9e0aa69a]821        import sasview
822        attr.nodeValue = sasview.__version__
823        # attr.nodeValue = '1.0'
[61cada5]824        top_element.setAttributeNode(attr)
[2f4b430]825
[61cada5]826        # File name
827        element = newdoc.createElement("filename")
828        if self.file is not None:
829            element.appendChild(newdoc.createTextNode(str(self.file)))
830        else:
831            element.appendChild(newdoc.createTextNode(str(file)))
832        top_element.appendChild(element)
[2f4b430]833
[11a7e11]834        element = newdoc.createElement("timestamp")
835        element.appendChild(newdoc.createTextNode(time.ctime(self.timestamp)))
836        attr = newdoc.createAttribute("epoch")
837        attr.nodeValue = str(self.timestamp)
838        element.setAttributeNode(attr)
839        top_element.appendChild(element)
[a95ae9a]840
[61cada5]841        # Inputs
842        inputs = newdoc.createElement("Attributes")
843        top_element.appendChild(inputs)
[2f4b430]844
[61cada5]845        if self.data is not None and hasattr(self.data, "group_id"):
846            self.data_group_id = self.data.group_id
847        if self.data is not None and hasattr(self.data, "is_data"):
848            self.is_data = self.data.is_data
849        if self.data is not None:
850            self.data_name = self.data.name
851        if self.data is not None and hasattr(self.data, "id"):
852            self.data_id = self.data.id
[2f4b430]853
[b1e609c]854        for item in LIST_OF_DATA_ATTRIBUTES:
[0b12abb5]855            element = newdoc.createElement(item[0])
[b1e609c]856            element.setAttribute(item[0], str(getattr(self, item[1])))
[f32d144]857            inputs.appendChild(element)
[2f4b430]858
[b1e609c]859        for item in LIST_OF_STATE_ATTRIBUTES:
[26f3dd5]860            element = newdoc.createElement(item[0])
[b1e609c]861            element.setAttribute(item[0], str(getattr(self, item[1])))
[26f3dd5]862            inputs.appendChild(element)
[2f4b430]863
[f32d144]864        # For self.values ={ disp_param_name: [vals,...],...}
865        # and for self.weights ={ disp_param_name: [weights,...],...}
[b1e609c]866        for item in LIST_OF_MODEL_ATTRIBUTES:
[11a7e11]867            element = newdoc.createElement(item[0])
[b1e609c]868            value_list = getattr(self, item[1])
869            for key, value in value_list.iteritems():
[2296316]870                sub_element = newdoc.createElement(key)
871                sub_element.setAttribute('name', str(key))
872                for val in value:
[b1e609c]873                    sub_element.appendChild(newdoc.createTextNode(str(val)))
[2f4b430]874
[f32d144]875                element.appendChild(sub_element)
[11a7e11]876            inputs.appendChild(element)
[2f4b430]877
[2296316]878        # Create doc for the dictionary of self._disp_obj_dic
[6c382da]879        for tagname, varname, tagtype in DISPERSION_LIST:
880            element = newdoc.createElement(tagname)
881            value_list = getattr(self, varname)
882            for key, value in value_list.iteritems():
[f32d144]883                sub_element = newdoc.createElement(key)
884                sub_element.setAttribute('name', str(key))
885                sub_element.setAttribute('value', str(value))
886                element.appendChild(sub_element)
887            inputs.appendChild(element)
[2f4b430]888
[b1e609c]889        for item in LIST_OF_STATE_PARAMETERS:
[61cada5]890            element = newdoc.createElement(item[0])
[c8e1996]891            self._to_xml_helper(thelist=getattr(self, item[1]),
892                                element=element, newdoc=newdoc)
[61cada5]893            inputs.appendChild(element)
[2f4b430]894
[a95ae9a]895        # Combined and Simultaneous Fit Parameters
896        if batch_fit_state is not None:
897            batch_combo = newdoc.createElement('simultaneous_fit')
898            top_element.appendChild(batch_combo)
899
900            # Simultaneous Fit Number For Linking Later
901            element = newdoc.createElement('sim_fit_number')
902            element.setAttribute('fit_number', str(batch_fit_state.fit_page_no))
903            batch_combo.appendChild(element)
904
905            # Save constraints
906            constraints = newdoc.createElement('constraints')
907            batch_combo.appendChild(constraints)
908            for constraint in batch_fit_state.constraints_list:
[998ca90]909                if constraint.model_cbox.GetValue() != "":
[c8e1996]910                    # model_cbox, param_cbox, egal_txt, constraint,
911                    # btRemove, sizer
[998ca90]912                    doc_cons = newdoc.createElement('constraint')
913                    doc_cons.setAttribute('model_cbox',
914                                          str(constraint.model_cbox.GetValue()))
915                    doc_cons.setAttribute('param_cbox',
916                                          str(constraint.param_cbox.GetValue()))
917                    doc_cons.setAttribute('egal_txt',
918                                          str(constraint.egal_txt.GetLabel()))
919                    doc_cons.setAttribute('constraint',
920                                          str(constraint.constraint.GetValue()))
921                    constraints.appendChild(doc_cons)
[a95ae9a]922
923            # Save all models
924            models = newdoc.createElement('model_list')
925            batch_combo.appendChild(models)
926            for model in batch_fit_state.model_list:
927                doc_model = newdoc.createElement('model_list_item')
928                doc_model.setAttribute('checked', str(model[0].GetValue()))
929                keys = model[1].keys()
930                doc_model.setAttribute('name', str(keys[0]))
931                values = model[1].get(keys[0])
932                doc_model.setAttribute('fit_number', str(model[2]))
933                doc_model.setAttribute('fit_page_source', str(model[3]))
934                doc_model.setAttribute('model_name', str(values.model.id))
935                models.appendChild(doc_model)
936
937            # Select All Checkbox
938            element = newdoc.createElement('select_all')
939            if batch_fit_state.select_all:
940                element.setAttribute('checked', 'True')
941            else:
942                element.setAttribute('checked', 'False')
943            batch_combo.appendChild(element)
944
[61cada5]945        # Save the file
946        if doc is None:
947            fd = open(file, 'w')
948            fd.write(newdoc.toprettyxml())
949            fd.close()
950            return None
951        else:
[ac5b69d]952            return newdoc
[2f4b430]953
[c8e1996]954    def _from_xml_helper(self, node, list):
[61cada5]955        """
[5062bbf]956        Helper function to write state to xml
[61cada5]957        """
958        for item in node:
[0b12abb5]959            try:
960                name = item.get('name')
961            except:
962                name = None
963            try:
964                value = item.get('value')
965            except:
966                value = None
967            try:
[3ad91de]968                selected_to_fit = (item.get('selected_to_fit') == "True")
[0b12abb5]969            except:
970                selected_to_fit = None
971            try:
[3ad91de]972                error_displayed = (item.get('error_displayed') == "True")
[0b12abb5]973            except:
974                error_displayed = None
[f32d144]975            try:
[0b12abb5]976                error_value = item.get('error_value')
977            except:
978                error_value = None
979            try:
[f32d144]980                minimum_displayed = (item.get('minimum_displayed') == "True")
[0b12abb5]981            except:
982                minimum_displayed = None
983            try:
984                minimum_value = item.get('minimum_value')
985            except:
986                minimum_value = None
987            try:
[3ad91de]988                maximum_displayed = (item.get('maximum_displayed') == "True")
[0b12abb5]989            except:
990                maximum_displayed = None
991            try:
992                maximum_value = item.get('maximum_value')
993            except:
994                maximum_value = None
995            try:
996                unit = item.get('unit')
997            except:
998                unit = None
[2296316]999            list.append([selected_to_fit, name, value, "+/-",
1000                         [error_displayed, error_value],
[f32d144]1001                         [minimum_displayed, minimum_value],
1002                         [maximum_displayed, maximum_value], unit])
[2f4b430]1003
[c8e1996]1004    def from_xml(self, file=None, node=None):
[61cada5]1005        """
[5062bbf]1006        Load fitting state from a file
[2f4b430]1007
[5062bbf]1008        :param file: .fitv file
1009        :param node: node of a XML document to read from
[61cada5]1010        """
1011        if file is not None:
[11a7e11]1012            msg = "PageState no longer supports non-CanSAS"
1013            msg += " format for fitting files"
1014            raise RuntimeError, msg
[2f4b430]1015
[9e0aa69a]1016        if node.get('version'):
[fa487d93]1017            # Get the version for model conversion purposes
[9e0aa69a]1018            self.version = tuple(int(e) for e in
1019                                 str.split(node.get('version'), "."))
[fa487d93]1020            # The tuple must be at least 3 items long
1021            while len(self.version) < 3:
1022                ver_list = list(self.version)
1023                ver_list.append(0)
1024                self.version = tuple(ver_list)
[2f4b430]1025
[61cada5]1026            # Get file name
1027            entry = get_content('ns:filename', node)
1028            if entry is not None:
1029                self.file = entry.text.strip()
[2f4b430]1030
[11a7e11]1031            # Get time stamp
1032            entry = get_content('ns:timestamp', node)
1033            if entry is not None and entry.get('epoch'):
1034                try:
1035                    self.timestamp = float(entry.get('epoch'))
1036                except:
1037                    msg = "PageState.fromXML: Could not"
1038                    msg += " read timestamp\n %s" % sys.exc_value
1039                    logging.error(msg)
[2f4b430]1040
[61cada5]1041            if entry is not None:
[c8e1996]1042                # Parse fitting attributes
1043                entry = get_content('ns:Attributes', node)
1044                for item in LIST_OF_DATA_ATTRIBUTES:
1045                    node = get_content('ns:%s' % item[0], entry)
1046                    setattr(self, item[0], parse_entry_helper(node, item))
1047
[d5aff7f]1048                dx_old_node = get_content('ns:%s' % 'dx_min', entry)
[b1e609c]1049                for item in LIST_OF_STATE_ATTRIBUTES:
[d5aff7f]1050                    if item[0] == "dx_percent" and dx_old_node is not None:
1051                        dxmin = ["dx_min", "dx_min", "float"]
1052                        setattr(self, item[0], parse_entry_helper(dx_old_node,
1053                                                                  dxmin))
[096181d]1054                        self.dx_old = True
1055                    else:
[d85f1d8a]1056                        node = get_content('ns:%s' % item[0], entry)
[096181d]1057                        setattr(self, item[0], parse_entry_helper(node, item))
[2f4b430]1058
[b1e609c]1059                for item in LIST_OF_STATE_PARAMETERS:
[2296316]1060                    node = get_content("ns:%s" % item[0], entry)
[c8e1996]1061                    self._from_xml_helper(node=node,
1062                                          list=getattr(self, item[1]))
[2f4b430]1063
[f32d144]1064                # Recover _disp_obj_dict from xml file
1065                self._disp_obj_dict = {}
[6c382da]1066                for tagname, varname, tagtype in DISPERSION_LIST:
1067                    node = get_content("ns:%s" % tagname, entry)
[2296316]1068                    for attr in node:
[6c382da]1069                        parameter = str(attr.get('name'))
1070                        value = attr.get('value')
1071                        if value.startswith("<"):
1072                            try:
1073                                # <path.to.NamedDistribution object/instance...>
1074                                cls_name = value[1:].split()[0].split('.')[-1]
1075                                cls = getattr(sasmodels.weights, cls_name)
1076                                value = cls.type
1077                            except Exception:
[998ca90]1078                                base = "unable to load distribution %r for %s"
1079                                logging.error(base % (value, parameter))
[6c382da]1080                                continue
1081                        _disp_obj_dict = getattr(self, varname)
1082                        _disp_obj_dict[parameter] = value
[2f4b430]1083
[f32d144]1084                # get self.values and self.weights dic. if exists
[6c382da]1085                for tagname, varname in LIST_OF_MODEL_ATTRIBUTES:
1086                    node = get_content("ns:%s" % tagname, entry)
[2296316]1087                    dic = {}
[b1e609c]1088                    value_list = []
[2296316]1089                    for par in node:
1090                        name = par.get('name')
[6c382da]1091                        values = par.text.split()
[2296316]1092                        # Get lines only with numbers
1093                        for line in values:
1094                            try:
[f32d144]1095                                val = float(line)
[b1e609c]1096                                value_list.append(val)
[7673ecd]1097                            except Exception:
[2296316]1098                                # pass if line is empty (it happens)
[6c382da]1099                                msg = ("Error reading %r from %s %s\n"
1100                                       % (line, tagname, name))
1101                                logging.error(msg + traceback.format_exc())
[0e33a8d]1102                        dic[name] = numpy.array(value_list)
[6c382da]1103                    setattr(self, varname, dic)
[2f4b430]1104
[2296316]1105    def set_plot_state(self, figs, canvases):
1106        """
1107        Build image state that wx.html understand
1108        by plotting, putting it into wx.FileSystem image object
1109
1110        """
1111        images = []
1112
1113        # Reset memory
1114        self.imgRAM = None
1115        wx.MemoryFSHandler()
[2f4b430]1116
[2296316]1117        # For no figures in the list, prepare empty plot
[c8e1996]1118        if figs is None or len(figs) == 0:
[2296316]1119            figs = [None]
[2f4b430]1120
[f32d144]1121        # Loop over the list of figures
[2296316]1122        # use wx.MemoryFSHandler
[f32d144]1123        self.imgRAM = wx.MemoryFSHandler()
[2296316]1124        for fig in figs:
[c8e1996]1125            if fig is not None:
[2296316]1126                ind = figs.index(fig)
1127                canvas = canvases[ind]
[2f4b430]1128
[c8e1996]1129            # store the image in wx.FileSystem Object
[2296316]1130            wx.FileSystem.AddHandler(wx.MemoryFSHandler())
[2f4b430]1131
[2296316]1132            # index of the fig
1133            ind = figs.index(fig)
[2f4b430]1134
[c8e1996]1135            # AddFile, image can be retrieved with 'memory:filename'
[f32d144]1136            self.imgRAM.AddFile('img_fit%s.png' % ind,
[2296316]1137                                canvas.bitmap, wx.BITMAP_TYPE_PNG)
[2f4b430]1138
[c8e1996]1139            # append figs
[2296316]1140            images.append(fig)
[2f4b430]1141
[c0ff8cc]1142        return images
[61cada5]1143
[f32d144]1144
[61cada5]1145class Reader(CansasReader):
1146    """
[5062bbf]1147    Class to load a .fitv fitting file
[61cada5]1148    """
[c8e1996]1149    # File type
[61cada5]1150    type_name = "Fitting"
[2f4b430]1151
[c8e1996]1152    # Wildcards
[b35d3d1]1153    type = ["Fitting files (*.fitv)|*.fitv"
[b9a5f0e]1154            "SASView file (*.svs)|*.svs"]
[c8e1996]1155    # List of allowed extensions
[f32d144]1156    ext = ['.fitv', '.FITV', '.svs', 'SVS']
[2f4b430]1157
[61cada5]1158    def __init__(self, call_back=None, cansas=True):
[df7ed14]1159        CansasReader.__init__(self)
[61cada5]1160        """
[5062bbf]1161        Initialize the call-back method to be called
1162        after we load a file
[2f4b430]1163
[5062bbf]1164        :param call_back: call-back method
1165        :param cansas:  True = files will be written/read in CanSAS format
1166                        False = write CanSAS format
[2f4b430]1167
[61cada5]1168        """
[c8e1996]1169        # Call back method to be executed after a file is read
[61cada5]1170        self.call_back = call_back
[c8e1996]1171        # CanSAS format flag
[61cada5]1172        self.cansas = cansas
[75fbd17]1173        self.state = None
[a95ae9a]1174        # batch fitting params for saving
1175        self.batchfit_params = []
[2f4b430]1176
[75fbd17]1177    def get_state(self):
1178        return self.state
[2f4b430]1179
[61cada5]1180    def read(self, path):
[f32d144]1181        """
[5062bbf]1182        Load a new P(r) inversion state from file
[2f4b430]1183
[5062bbf]1184        :param path: file path
[2f4b430]1185
[61cada5]1186        """
[c8e1996]1187        if self.cansas:
[61cada5]1188            return self._read_cansas(path)
[2f4b430]1189
[61cada5]1190    def _parse_state(self, entry):
1191        """
[5062bbf]1192        Read a fit result from an XML node
[2f4b430]1193
[f32d144]1194        :param entry: XML node to read from
[5062bbf]1195        :return: PageState object
[61cada5]1196        """
1197        # Create an empty state
[2f4b430]1198        state = None
[61cada5]1199        # Locate the P(r) node
1200        try:
[f32d144]1201            nodes = entry.xpath('ns:%s' % FITTING_NODE_NAME,
1202                                namespaces={'ns': CANSAS_NS})
[e89aed5]1203            if nodes:
[b35d3d1]1204                # Create an empty state
[f32d144]1205                state = PageState()
[c8e1996]1206                state.from_xml(node=nodes[0])
[2f4b430]1207
[61cada5]1208        except:
[6c382da]1209            logging.info("XML document does not contain fitting information.\n"
1210                         + traceback.format_exc())
[2f4b430]1211
[61cada5]1212        return state
[2f4b430]1213
[e89aed5]1214    def _parse_simfit_state(self, entry):
1215        """
1216        Parses the saved data for a simultaneous fit
1217        :param entry: XML object to read from
1218        :return: XML object for a simultaneous fit or None
1219        """
1220        nodes = entry.xpath('ns:%s' % FITTING_NODE_NAME,
1221                            namespaces={'ns': CANSAS_NS})
1222        if nodes:
1223            simfitstate = nodes[0].xpath('ns:simultaneous_fit',
1224                                         namespaces={'ns': CANSAS_NS})
1225            if simfitstate:
1226                from simfitpage import SimFitPageState
1227                sim_fit_state = SimFitPageState()
1228                simfitstate_0 = simfitstate[0]
1229                all = simfitstate_0.xpath('ns:select_all',
[c8e1996]1230                                          namespaces={'ns': CANSAS_NS})
[e89aed5]1231                atts = all[0].attrib
1232                checked = atts.get('checked')
1233                sim_fit_state.select_all = bool(checked)
1234                model_list = simfitstate_0.xpath('ns:model_list',
[c8e1996]1235                                                 namespaces={'ns': CANSAS_NS})
[e89aed5]1236                model_list_items = model_list[0].xpath('ns:model_list_item',
[c8e1996]1237                                                       namespaces={'ns':
1238                                                                    CANSAS_NS})
[e89aed5]1239                for model in model_list_items:
1240                    attrs = model.attrib
1241                    sim_fit_state.model_list.append(attrs)
[998ca90]1242
[e89aed5]1243                constraints = simfitstate_0.xpath('ns:constraints',
1244                                                namespaces={'ns': CANSAS_NS})
1245                constraint_list = constraints[0].xpath('ns:constraint',
[998ca90]1246                                               namespaces={'ns': CANSAS_NS})
[e89aed5]1247                for constraint in constraint_list:
1248                    attrs = constraint.attrib
1249                    sim_fit_state.constraints_list.append(attrs)
1250
1251                return sim_fit_state
1252            else:
1253                return None
1254
[ac5b69d]1255    def _parse_save_state_entry(self, dom):
[e9b12eaf]1256        """
[5062bbf]1257        Parse a SASentry
[2f4b430]1258
[5062bbf]1259        :param node: SASentry node
[2f4b430]1260
[df7ed14]1261        :return: Data1D/Data2D object
[2f4b430]1262
[e9b12eaf]1263        """
[df7ed14]1264        node = dom.xpath('ns:data_class', namespaces={'ns': CANSAS_NS})
[af08e55]1265        return_value, _ = self._parse_entry(dom)
1266        return return_value, _
[e9b12eaf]1267
[61cada5]1268    def _read_cansas(self, path):
[f32d144]1269        """
[e89aed5]1270        Load data and fitting information from a CanSAS XML file.
[2f4b430]1271
[5062bbf]1272        :param path: file path
[f32d144]1273        :return: Data1D object if a single SASentry was found,
[5062bbf]1274                    or a list of Data1D objects if multiple entries were found,
1275                    or None of nothing was found
1276        :raise RuntimeError: when the file can't be opened
1277        :raise ValueError: when the length of the data vectors are inconsistent
[61cada5]1278        """
1279        output = []
[e89aed5]1280        simfitstate = None
[f32d144]1281        basename = os.path.basename(path)
[b63dc6e]1282        root, extension = os.path.splitext(basename)
1283        ext = extension.lower()
[61cada5]1284        try:
1285            if os.path.isfile(path):
[a95ae9a]1286                if ext in self.ext or ext == '.xml':
[61cada5]1287                    tree = etree.parse(path, parser=etree.ETCompatXMLParser())
1288                    # Check the format version number
[a95ae9a]1289                    # Specifying the namespace will take care of the file
1290                    # format version
[61cada5]1291                    root = tree.getroot()
[f32d144]1292                    entry_list = root.xpath('ns:SASentry',
1293                                            namespaces={'ns': CANSAS_NS})
1294                    for entry in entry_list:
[e9b12eaf]1295                        try:
[ac5b69d]1296                            sas_entry, _ = self._parse_save_state_entry(entry)
[e9b12eaf]1297                        except:
[df7ed14]1298                            raise
[61cada5]1299                        fitstate = self._parse_state(entry)
[2f4b430]1300
[a95ae9a]1301                        # state could be None when .svs file is loaded
1302                        # in this case, skip appending to output
1303                        if fitstate is not None:
[b35d3d1]1304                            sas_entry.meta_data['fitstate'] = fitstate
1305                            sas_entry.filename = fitstate.file
1306                            output.append(sas_entry)
[e89aed5]1307
[61cada5]1308            else:
[b63dc6e]1309                self.call_back(format=ext)
[61cada5]1310                raise RuntimeError, "%s is not a file" % path
[b35d3d1]1311
[61cada5]1312            # Return output consistent with the loader's api
[f32d144]1313            if len(output) == 0:
1314                self.call_back(state=None, datainfo=None, format=ext)
[61cada5]1315                return None
1316            else:
[b35d3d1]1317                for ind in range(len(output)):
1318                    # Call back to post the new state
1319                    state = output[ind].meta_data['fitstate']
1320                    t = time.localtime(state.timestamp)
1321                    time_str = time.strftime("%b %d %H:%M", t)
1322                    # Check that no time stamp is already appended
1323                    max_char = state.file.find("[")
1324                    if max_char < 0:
1325                        max_char = len(state.file)
[ef16f59]1326                    original_fname = state.file[0:max_char]
[f32d144]1327                    state.file = original_fname + ' [' + time_str + ']'
[2f4b430]1328
[b35d3d1]1329                    if state is not None and state.is_data is not None:
[b1e609c]1330                        output[ind].is_data = state.is_data
[2f4b430]1331
[b35d3d1]1332                    output[ind].filename = state.file
1333                    state.data = output[ind]
[f32d144]1334                    state.data.name = output[ind].filename  # state.data_name
[b35d3d1]1335                    state.data.id = state.data_id
1336                    if state.is_data is not None:
1337                        state.data.is_data = state.is_data
[f32d144]1338                    if output[ind].run_name is not None\
[c8e1996]1339                         and len(output[ind].run_name) != 0:
[654e8e0]1340                        if isinstance(output[ind].run_name, dict):
1341                            name = output[ind].run_name.keys()[0]
1342                        else:
1343                            name = output[ind].run_name
[f32d144]1344                    else:
1345                        name = original_fname
[ef16f59]1346                    state.data.group_id = name
[fa487d93]1347                    state.version = fitstate.version
[a95ae9a]1348                    # store state in fitting
[f32d144]1349                    self.call_back(state=state,
1350                                   datainfo=output[ind], format=ext)
1351                    self.state = state
[e6de6b8]1352                simfitstate = self._parse_simfit_state(entry)
[e89aed5]1353                if simfitstate is not None:
1354                    self.call_back(state=simfitstate)
1355
[ef16f59]1356                return output
[61cada5]1357        except:
[4bee68d]1358            self.call_back(format=ext)
[61cada5]1359            raise
[2f4b430]1360
[61cada5]1361    def write(self, filename, datainfo=None, fitstate=None):
1362        """
[b35d3d1]1363        Write the content of a Data1D as a CanSAS XML file only for standalone
[2f4b430]1364
[5062bbf]1365        :param filename: name of the file to write
1366        :param datainfo: Data1D object
1367        :param fitstate: PageState object
[2f4b430]1368
[61cada5]1369        """
1370        # Sanity check
[a95ae9a]1371        if self.cansas:
[61cada5]1372            # Add fitting information to the XML document
[ef16f59]1373            doc = self.write_toXML(datainfo, fitstate)
[61cada5]1374            # Write the XML document
1375        else:
[c8e1996]1376            doc = fitstate.to_xml(file=filename)
[2f4b430]1377
[ac5b69d]1378        # Save the document no matter the type
1379        fd = open(filename, 'w')
1380        fd.write(doc.toprettyxml())
1381        fd.close()
[2f4b430]1382
[a95ae9a]1383    def write_toXML(self, datainfo=None, state=None, batchfit=None):
[b35d3d1]1384        """
[f32d144]1385        Write toXML, a helper for write(),
1386        could be used by guimanager._on_save()
[2f4b430]1387
[b35d3d1]1388        : return: xml doc
1389        """
[3c44c66]1390
[a95ae9a]1391        self.batchfit_params = batchfit
1392        if state.data is None or not state.data.is_data:
[3b148c3]1393            return None
[a95ae9a]1394        # make sure title and data run are filled.
1395        if state.data.title is None or state.data.title == '':
1396            state.data.title = state.data.name
1397        if state.data.run_name is None or state.data.run_name == {}:
1398            state.data.run = [str(state.data.name)]
1399            state.data.run_name[0] = state.data.name
1400
[83c09af]1401        data = state.data
1402        doc, sasentry = self._to_xml_doc(data)
[2f4b430]1403
[b35d3d1]1404        if state is not None:
[c8e1996]1405            doc = state.to_xml(doc=doc, file=data.filename, entry_node=sasentry,
1406                               batch_fit_state=self.batchfit_params)
[2f4b430]1407
[f32d144]1408        return doc
[467202f]1409
1410# Simple html report templet
1411HEADER = "<html>\n"
1412HEADER += "<head>\n"
1413HEADER += "<meta http-equiv=Content-Type content='text/html; "
1414HEADER += "charset=windows-1252'> \n"
1415HEADER += "<meta name=Generator >\n"
1416HEADER += "</head>\n"
1417HEADER += "<body lang=EN-US>\n"
1418HEADER += "<div class=WordSection1>\n"
1419HEADER += "<p class=MsoNormal><b><span ><center><font size='4' >"
1420HEADER += "%s</font></center></span></center></b></p>"
1421HEADER += "<p class=MsoNormal>&nbsp;</p>"
1422PARA = "<p class=MsoNormal><font size='4' > %s \n"
1423PARA += "</font></p>"
1424CENTRE = "<p class=MsoNormal><center><font size='4' > %s \n"
1425CENTRE += "</font></center></p>"
1426FEET_1 = \
1427"""
1428<p class=MsoNormal>&nbsp;</p>
1429<br>
1430<p class=MsoNormal><b><span ><center> <font size='4' > Graph
1431</font></span></center></b></p>
1432<p class=MsoNormal>&nbsp;</p>
1433<center>
1434<br><font size='4' >Model Computation</font>
1435<br><font size='4' >Data: "%s"</font><br>
1436"""
1437FEET_2 = \
1438"""
1439<img src="%s" >
1440</img>
1441"""
1442FEET_3 = \
1443"""
1444</center>
1445</div>
1446</body>
1447</html>
1448"""
[c8e1996]1449ELINE = "<p class=MsoNormal>&nbsp;</p>"
Note: See TracBrowser for help on using the repository browser.