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

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

Saving and then loading in same save state working with custom pinhole dQ as a percentage. #850

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