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

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.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 4666660 was acf8e4a5, checked in by Paul Kienzle <pkienzle@…>, 10 years ago

reference BumpsFit? directly and remove fit engine selection layer

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