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

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 1e13b53 was 6d2b50b, checked in by krzywon, 7 years ago

Change the elif block to a dictionary search to minimize the number of checks.

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