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

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 b11e127 was a805118, checked in by Jae Cho <jhjcho@…>, 12 years ago

bug fix for saveproject

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