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

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 c23f303 was 7673ecd, checked in by Paul Kienzle <pkienzle@…>, 8 years ago

refactor support for sum model; put tracebacks in logging errors

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