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

Last change on this file since ae2f623 was 1fa4f736, checked in by krzywon, 7 years ago

Only process data element if <perspective> element is present in <SASentry>.

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