source: sasview/fittingview/src/sans/perspectives/fitting/pagestate.py @ 56acb5d

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 56acb5d was f32d144, checked in by Mathieu Doucet <doucetm@…>, 13 years ago

Pep-8-ification

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