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

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 3a22ce7 was 6c382da, checked in by Paul Kienzle <pkienzle@…>, 8 years ago

support alternate distributions in save/load and copy/paste. Closes #669

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