source: sasview/src/sas/sasgui/perspectives/invariant/invariant_state.py @ cf1be88

Last change on this file since cf1be88 was e9920cd, checked in by krzywon, 6 years ago

Add SasView? version to report files. refs #1179

  • Property mode set to 100644
File size: 30.8 KB
RevLine 
[51f14603]1"""
[78a205a]2    State class for the invariant UI
[51f14603]3"""
4
[78a205a]5# import time
[51f14603]6import os
7import sys
8import logging
9import copy
[b699768]10import sas.sascalc.dataloader
[78a205a]11# from xml.dom.minidom import parse
[51f14603]12from lxml import etree
[b699768]13from sas.sascalc.dataloader.readers.cansas_reader import Reader as CansasReader
[ec4b19c]14from sas.sasgui.guiframe.report_image_handler import ReportImageHandler
[b699768]15from sas.sascalc.dataloader.readers.cansas_reader import get_content
[d85c194]16from sas.sasgui.guiframe.utils import format_number
17from sas.sasgui.guiframe.gui_style import GUIFRAME_ID
18from sas.sasgui.guiframe.dataFitting import Data1D
[51f14603]19
[463e7ffc]20logger = logging.getLogger(__name__)
[c155a16]21
[51f14603]22INVNODE_NAME = 'invariant'
23CANSAS_NS = "cansas1d/1.0"
24
25# default state
[78a205a]26DEFAULT_STATE = {'file': 'None',
27                 'compute_num':0,
28                 'state_num':0,
29                 'is_time_machine':False,
30                 'background_tcl':0.0,
31                 'scale_tcl':1.0,
32                 'contrast_tcl':1.0,
33                 'porod_constant_tcl':'',
34                 'npts_low_tcl':10,
35                 'npts_high_tcl':10,
36                 'power_high_tcl':4.0,
37                 'power_low_tcl': 4.0,
38                 'enable_high_cbox':False,
39                 'enable_low_cbox':False,
40                 'guinier': True,
41                 'power_law_high': False,
42                 'power_law_low': False,
43                 'fit_enable_high': False,
44                 'fit_enable_low': False,
45                 'fix_enable_high':True,
46                 'fix_enable_low':True,
47                 'volume_tcl':'',
48                 'volume_err_tcl':'',
49                 'surface_tcl':'',
50                 'surface_err_tcl':''}
51# list of states: This list will be filled as panel
[51f14603]52# init and the number of states increases
53state_list = {}
54bookmark_list = {}
[78a205a]55# list of input parameters (will be filled up on panel init) used by __str__
[51f14603]56input_list = {'background_tcl':0,
[78a205a]57              'scale_tcl':0,
58              'contrast_tcl':0,
59              'porod_constant_tcl':'',
60              'npts_low_tcl':0,
61              'npts_high_tcl':0,
62              'power_high_tcl':0,
63              'power_low_tcl': 0}
64# list of output parameters (order sensitive) used by __str__
65output_list = [["qstar_low", "Q* from low Q extrapolation [1/(cm*A)]"],
66               ["qstar_low_err", "dQ* from low Q extrapolation"],
67               ["qstar_low_percent", "Q* percent from low Q extrapolation"],
68               ["qstar", "Q* from data [1/(cm*A)]"],
69               ["qstar_err", "dQ* from data"],
70               ["qstar_percent", "Q* percent from data"],
71               ["qstar_high", "Q* from high Q extrapolation [1/(cm*A)]"],
72               ["qstar_high_err", "dQ* from high Q extrapolation"],
[51f14603]73               ["qstar_high_percent", "Q* percent from low Q extrapolation"],
[78a205a]74               ["qstar_total", "total Q* [1/(cm*A)]"],
75               ["qstar_total_err", "total dQ*"],
76               ["volume", "volume fraction"],
77               ["volume_err", "volume fraction error"],
78               ["surface", "specific surface"],
79               ["surface_err", "specific surface error"]]
80
[51f14603]81
82
83class InvariantState(object):
84    """
85    Class to hold the state information of the InversionControl panel.
86    """
87    def __init__(self):
88        """
89        Default values
90        """
[78a205a]91        # Input
92        self.file = None
[51f14603]93        self.data = Data1D(x=[], y=[], dx=None, dy=None)
[78a205a]94        self.theory_lowQ = Data1D(x=[], y=[], dy=None)
[51f14603]95        self.theory_lowQ.symbol = GUIFRAME_ID.CURVE_SYMBOL_NUM
96        self.theory_highQ = Data1D(x=[], y=[], dy=None)
97        self.theory_highQ.symbol = GUIFRAME_ID.CURVE_SYMBOL_NUM
[78a205a]98        # self.is_time_machine = False
99        self.saved_state = DEFAULT_STATE
[51f14603]100        self.state_list = state_list
101        self.bookmark_list = bookmark_list
102        self.input_list = input_list
103        self.output_list = output_list
[78a205a]104
[51f14603]105        self.compute_num = 0
106        self.state_num = 0
107        self.timestamp = ('00:00:00', '00/00/0000')
108        self.container = None
[78a205a]109        # plot image
[51f14603]110        self.wximbmp = None
111        # report_html strings
[d85c194]112        import sas.sasgui.perspectives.invariant as invariant
[51f14603]113        path = invariant.get_data_path(media='media')
[78a205a]114        path_report_html = os.path.join(path, "report_template.html")
115        html_template = open(path_report_html, "r")
[51f14603]116        self.template_str = html_template.read()
117        self.report_str = self.template_str
[78a205a]118        # self.report_str_save = None
[51f14603]119        html_template.close()
[78a205a]120
[51f14603]121    def __str__(self):
122        """
123        Pretty print
[78a205a]124
[51f14603]125        : return: string representing the state
126        """
127        # Input string
128        compute_num = self.saved_state['compute_num']
129        compute_state = self.state_list[str(compute_num)]
130        my_time, date = self.timestamp
131        file_name = self.file
[e9920cd]132        from sas.sasview.__init__ import __version__ as sasview_version
[51f14603]133
134        state_num = int(self.saved_state['state_num'])
135        state = "\n[Invariant computation for %s: " % file_name
[e9920cd]136        state += "performed at %s on %s] " % (my_time, date)
137        state += "[SasView v%s]\n" % (sasview_version)
[51f14603]138        state += "State No.: %d \n" % state_num
139        state += "\n=== Inputs ===\n"
[78a205a]140
[51f14603]141        # text ctl general inputs ( excluding extrapolation text ctl)
[78a205a]142        for key, value in self.input_list.iteritems():
[51f14603]143            if value == '':
144                continue
[78a205a]145            key_split = key.split('_')
146            max_ind = len(key_split) - 1
147            if key_split[max_ind] == 'tcl':
[51f14603]148                name = ""
149                if key_split[1] == 'low' or key_split[1] == 'high':
150                    continue
151                for ind in range(0, max_ind):
152                    name += " %s" % key_split[ind]
153                state += "%s:   %s\n" % (name.lstrip(" "), value)
[78a205a]154
155        # other input parameters
[51f14603]156        extra_lo = compute_state['enable_low_cbox']
157        if compute_state['enable_low_cbox']:
158            if compute_state['guinier']:
159                extra_lo = 'Guinier'
160            else:
161                extra_lo = 'Power law'
162        extra_hi = compute_state['enable_high_cbox']
163        if compute_state['enable_high_cbox']:
164            extra_hi = 'Power law'
[78a205a]165        state += "\nExtrapolation:  High=%s; Low=%s\n" % (extra_hi, extra_lo)
[51f14603]166        low_off = False
167        high_off = False
[78a205a]168        for key, value in self.input_list.iteritems():
169            key_split = key.split('_')
170            max_ind = len(key_split) - 1
171            if key_split[max_ind] == 'tcl':
172                name = ""
[51f14603]173                # check each buttons whether or not ON or OFF
174                if key_split[1] == 'low' or key_split[1] == 'high':
175                    if not compute_state['enable_low_cbox'] and \
[78a205a]176                        key_split[max_ind - 1] == 'low':
[51f14603]177                        low_off = True
[78a205a]178                        continue
[51f14603]179                    elif not compute_state['enable_high_cbox'] and \
[78a205a]180                        key_split[max_ind - 1] == 'high':
[51f14603]181                        high_off = True
182                        continue
183                    elif extra_lo == 'Guinier' and key_split[0] == 'power' and \
[78a205a]184                        key_split[max_ind - 1] == 'low':
[51f14603]185                        continue
186                    for ind in range(0, max_ind):
187                        name += " %s" % key_split[ind]
188                    name = name.lstrip(" ")
[78a205a]189                    if name == "power low":
[51f14603]190                        if compute_state['fix_enable_low']:
191                            name += ' (Fixed)'
192                        else:
193                            name += ' (Fitted)'
[78a205a]194                    if name == "power high":
[51f14603]195                        if compute_state['fix_enable_high']:
196                            name += ' (Fixed)'
197                        else:
198                            name += ' (Fitted)'
199                    state += "%s:   %s\n" % (name, value)
200        # Outputs
201        state += "\n=== Outputs ==="
202        for item in output_list:
[78a205a]203            item_split = item[0].split('_')
[51f14603]204            # Exclude the extrapolation that turned off
205            if len(item_split) > 1:
206                if low_off and item_split[1] == 'low':
207                    continue
208                if high_off and item_split[1] == 'high':
209                    continue
210            max_ind = len(item_split) - 1
211            value = None
[78a205a]212            if hasattr(self.container, item[0]):
[51f14603]213                # Q* outputs
[78a205a]214                value = getattr(self.container, item[0])
215            else:
[51f14603]216                # other outputs than Q*
217                name = item[0] + "_tcl"
218                if name in self.saved_state.keys():
[78a205a]219                    value = self.saved_state[name]
220
221            # Exclude the outputs w/''
[51f14603]222            if value == '':
[78a205a]223                continue
[51f14603]224            # Error outputs
225            if item_split[max_ind] == 'err':
226                state += "+- %s " % format_number(value)
227            # Percentage outputs
228            elif item_split[max_ind] == 'percent':
[78a205a]229                value = float(value) * 100
[51f14603]230                state += "(%s %s)" % (format_number(value), '%')
231            # Outputs
232            else:
[78a205a]233                state += "\n%s:   %s " % (item[1],
234                                          format_number(value, high=True))
[51f14603]235        # Include warning msg
236        if self.container is not None:
237            state += "\n\nNote:\n" + self.container.warning_msg
238        return state
239
240    def clone_state(self):
241        """
242        deepcopy the state
243        """
244        return copy.deepcopy(self.saved_state)
245
246    def toXML(self, file="inv_state.inv", doc=None, entry_node=None):
247        """
248        Writes the state of the InversionControl panel to file, as XML.
[78a205a]249
[51f14603]250        Compatible with standalone writing, or appending to an
251        already existing XML document. In that case, the XML document
[78a205a]252        is required. An optional entry node in the XML document
[51f14603]253        may also be given.
[78a205a]254
[51f14603]255        : param file: file to write to
256        : param doc: XML document object [optional]
[78a205a]257        : param entry_node: XML node within the document at which we will append the data [optional]
[51f14603]258        """
[92a2ecd]259        # TODO: Get this to work
[51f14603]260        from xml.dom.minidom import getDOMImplementation
261        import time
262        timestamp = time.time()
263        # Check whether we have to write a standalone XML file
264        if doc is None:
265            impl = getDOMImplementation()
[78a205a]266
267            doc_type = impl.createDocumentType(INVNODE_NAME, "1.0", "1.0")
268
[51f14603]269            newdoc = impl.createDocument(None, INVNODE_NAME, doc_type)
270            top_element = newdoc.documentElement
271        else:
272            # We are appending to an existing document
273            newdoc = doc
274            top_element = newdoc.createElement(INVNODE_NAME)
275            if entry_node is None:
276                newdoc.documentElement.appendChild(top_element)
277            else:
278                entry_node.appendChild(top_element)
[78a205a]279
[51f14603]280        attr = newdoc.createAttribute("version")
281        attr.nodeValue = '1.0'
282        top_element.setAttributeNode(attr)
[78a205a]283
[51f14603]284        # File name
285        element = newdoc.createElement("filename")
[7432acb]286        if self.file is not None and self.file != '':
[51f14603]287            element.appendChild(newdoc.createTextNode(str(self.file)))
288        else:
289            element.appendChild(newdoc.createTextNode(str(file)))
290        top_element.appendChild(element)
[78a205a]291
[51f14603]292        element = newdoc.createElement("timestamp")
293        element.appendChild(newdoc.createTextNode(time.ctime(timestamp)))
294        attr = newdoc.createAttribute("epoch")
295        attr.nodeValue = str(timestamp)
296        element.setAttributeNode(attr)
297        top_element.appendChild(element)
[78a205a]298
[51f14603]299        # Current state
300        state = newdoc.createElement("state")
301        top_element.appendChild(state)
[78a205a]302
303        for name, value in self.saved_state.iteritems():
[51f14603]304            element = newdoc.createElement(str(name))
305            element.appendChild(newdoc.createTextNode(str(value)))
306            state.appendChild(element)
[78a205a]307
[51f14603]308        # State history list
309        history = newdoc.createElement("history")
310        top_element.appendChild(history)
[78a205a]311
[51f14603]312        for name, value in self.state_list.iteritems():
313            history_element = newdoc.createElement('state_' + str(name))
[78a205a]314            for state_name, state_value in value.iteritems():
[51f14603]315                state_element = newdoc.createElement(str(state_name))
316                child = newdoc.createTextNode(str(state_value))
317                state_element.appendChild(child)
318                history_element.appendChild(state_element)
[78a205a]319            # history_element.appendChild(state_list_element)
[51f14603]320            history.appendChild(history_element)
321
322        # Bookmarks  bookmark_list[self.bookmark_num] = [\
[78a205a]323        # my_time,date,state,comp_state]
[51f14603]324        bookmark = newdoc.createElement("bookmark")
325        top_element.appendChild(bookmark)
[78a205a]326        item_list = ['time', 'date', 'state', 'comp_state']
[51f14603]327        for name, value_list in self.bookmark_list.iteritems():
[78a205a]328            element = newdoc.createElement('mark_' + str(name))
329            _, date, state, comp_state = value_list
[51f14603]330            time_element = newdoc.createElement('time')
331            time_element.appendChild(newdoc.createTextNode(str(value_list[0])))
332            date_element = newdoc.createElement('date')
333            date_element.appendChild(newdoc.createTextNode(str(value_list[1])))
334            state_list_element = newdoc.createElement('state')
335            comp_state_list_element = newdoc.createElement('comp_state')
336            for state_name, state_value in value_list[2].iteritems():
337                state_element = newdoc.createElement(str(state_name))
338                child = newdoc.createTextNode(str(state_value))
339                state_element.appendChild(child)
340                state_list_element.appendChild(state_element)
341            for comp_name, comp_value in value_list[3].iteritems():
342                comp_element = newdoc.createElement(str(comp_name))
343                comp_element.appendChild(newdoc.createTextNode(str(comp_value)))
344                comp_state_list_element.appendChild(comp_element)
345            element.appendChild(time_element)
346            element.appendChild(date_element)
347            element.appendChild(state_list_element)
348            element.appendChild(comp_state_list_element)
349            bookmark.appendChild(element)
350
351        # Save the file
352        if doc is None:
353            fd = open('test000', 'w')
354            fd.write(newdoc.toprettyxml())
355            fd.close()
356            return None
357        else:
[92a2ecd]358            return newdoc
[78a205a]359
[51f14603]360    def fromXML(self, file=None, node=None):
361        """
362        Load invariant states from a file
[78a205a]363
[51f14603]364        : param file: .inv file
[78a205a]365        : param node: node of a XML document to read from
[51f14603]366        """
367        if file is not None:
368            msg = "InvariantSate no longer supports non-CanSAS"
369            msg += " format for invariant files"
370            raise RuntimeError, msg
[78a205a]371
[51f14603]372        if node.get('version')\
373            and node.get('version') == '1.0':
374
375            # Get file name
376            entry = get_content('ns:filename', node)
377            if entry is not None:
378                file_name = entry.text.strip()
379
380            # Get time stamp
381            entry = get_content('ns:timestamp', node)
382            if entry is not None and entry.get('epoch'):
383                try:
384                    timestamp = (entry.get('epoch'))
385                except:
386                    msg = "InvariantSate.fromXML: Could not read"
387                    msg += " timestamp\n %s" % sys.exc_value
[c155a16]388                    logger.error(msg)
[78a205a]389
[51f14603]390            # Parse bookmarks
391            entry_bookmark = get_content('ns:bookmark', node)
392
393            for ind in range(1, len(entry_bookmark) + 1):
394                temp_state = {}
395                temp_bookmark = {}
[78a205a]396                entry = get_content('ns:mark_%s' % ind, entry_bookmark)
397
[51f14603]398                if entry is not None:
399                    my_time = get_content('ns:time', entry)
400                    val_time = str(my_time.text.strip())
401                    date = get_content('ns:date', entry)
402                    val_date = str(date.text.strip())
403                    state_entry = get_content('ns:state', entry)
[78a205a]404                    for item in DEFAULT_STATE:
[51f14603]405                        input_field = get_content('ns:%s' % item, state_entry)
406                        val = str(input_field.text.strip())
407                        if input_field is not None:
[78a205a]408                            temp_state[item] = val
[51f14603]409                    comp_entry = get_content('ns:comp_state', entry)
[78a205a]410
411                    for item in DEFAULT_STATE:
[51f14603]412                        input_field = get_content('ns:%s' % item, comp_entry)
413                        val = str(input_field.text.strip())
414                        if input_field is not None:
[78a205a]415                            temp_bookmark[item] = val
[51f14603]416                    try:
[78a205a]417                        self.bookmark_list[ind] = [val_time, val_date, temp_state, temp_bookmark]
[51f14603]418                    except:
419                        raise "missing components of bookmarks..."
420            # Parse histories
421            entry_history = get_content('ns:history', node)
422
423            for ind in range(0, len(entry_history)):
424                temp_state = {}
[78a205a]425                entry = get_content('ns:state_%s' % ind, entry_history)
[51f14603]426
427                if entry is not None:
[78a205a]428                    for item in DEFAULT_STATE:
429                        input_field = get_content('ns:%s' % item, entry)
[db5294e]430                        if input_field.text is not None:
431                            val = str(input_field.text.strip())
432                        else:
433                            val = ''
[51f14603]434                        if input_field is not None:
[78a205a]435                            temp_state[item] = val
[cb93b40]436                            self.state_list[str(ind)] = temp_state
[78a205a]437
[51f14603]438            # Parse current state (ie, saved_state)
[78a205a]439            entry = get_content('ns:state', node)
[51f14603]440            if entry is not None:
[78a205a]441                for item in DEFAULT_STATE:
[51f14603]442                    input_field = get_content('ns:%s' % item, entry)
[db5294e]443                    if input_field.text is not None:
444                        val = str(input_field.text.strip())
445                    else:
446                        val = ''
[51f14603]447                    if input_field is not None:
448                        self.set_saved_state(name=item, value=val)
449            self.file = file_name
[78a205a]450
[51f14603]451    def set_report_string(self):
452        """
[78a205a]453        Get the values (strings) from __str__ for report
[51f14603]454        """
455        strings = self.__str__()
[78a205a]456
[51f14603]457        # default string values
[78a205a]458        for num in range(1, 19):
[51f14603]459            exec "s_%s = 'NA'" % str(num)
460        lines = strings.split('\n')
461        # get all string values from __str__()
462        for line in range(0, len(lines)):
463            if line == 1:
464                s_1 = lines[1]
465            elif line == 2:
466                s_2 = lines[2]
467            else:
468                item = lines[line].split(':')
469                item[0] = item[0].strip()
470                if item[0] == "scale":
471                    s_3 = item[1]
472                elif item[0] == "porod constant":
473                    s_4 = item[1]
474                elif item[0] == "background":
475                    s_5 = item[1]
476                elif item[0] == "contrast":
477                    s_6 = item[1]
478                elif item[0] == "Extrapolation":
479                    extra = item[1].split(";")
480                    bool_0 = extra[0].split("=")
481                    bool_1 = extra[1].split("=")
482                    s_8 = " " + bool_0[0] + "Q region = " + bool_0[1]
483                    s_7 = " " + bool_1[0] + "Q region = " + bool_1[1]
484                elif item[0] == "npts low":
485                    s_9 = item[1]
486                elif item[0] == "npts high":
487                    s_10 = item[1]
488                elif item[0] == "volume fraction":
[78a205a]489                    val = item[1].split("+-")[0].strip()
[51f14603]490                    error = item[1].split("+-")[1].strip()
491                    s_17 = val + " ± " + error
492                elif item[0] == "specific surface":
493                    val = item[1].split("+-")[0].strip()
494                    error = item[1].split("+-")[1].strip()
495                    s_18 = val + " ± " + error
496                elif item[0].split("(")[0].strip() == "power low":
[78a205a]497                    s_11 = item[0] + " =" + item[1]
[51f14603]498                elif item[0].split("(")[0].strip() == "power high":
[78a205a]499                    s_12 = item[0] + " =" + item[1]
[51f14603]500                elif item[0].split("[")[0].strip() == "Q* from low Q extrapolation":
[78a205a]501                    # looks messy but this way the symbols +_ and % work on html
[51f14603]502                    val = item[1].split("+-")[0].strip()
503                    error = item[1].split("+-")[1].strip()
504                    err = error.split("%")[0].strip()
505                    percent = error.split("%")[1].strip()
506                    s_13 = val + " ± " + err + "&#37" + percent
507                elif item[0].split("[")[0].strip() == "Q* from data":
508                    val = item[1].split("+-")[0].strip()
509                    error = item[1].split("+-")[1].strip()
510                    err = error.split("%")[0].strip()
511                    percent = error.split("%")[1].strip()
512                    s_14 = val + " ± " + err + "&#37" + percent
513                elif item[0].split("[")[0].strip() == "Q* from high Q extrapolation":
514                    val = item[1].split("+-")[0].strip()
515                    error = item[1].split("+-")[1].strip()
516                    err = error.split("%")[0].strip()
517                    percent = error.split("%")[1].strip()
518                    s_15 = val + " ± " + err + "&#37" + percent
519                elif item[0].split("[")[0].strip() == "total Q*":
520                    val = item[1].split("+-")[0].strip()
521                    error = item[1].split("+-")[1].strip()
522                    s_16 = val + " ± " + error
523                else:
524                    continue
525
526        s_1 = self._check_html_format(s_1)
527        file_name = self._check_html_format(self.file)
528
529        # make plot image
[78a205a]530        self.set_plot_state(extra_high=bool_0[1], extra_low=bool_1[1])
[51f14603]531        # get ready for report with setting all the html strings
532        self.report_str = str(self.template_str) % (s_1, s_2,
[78a205a]533                                                    s_3, s_4, s_5, s_6, s_7, s_8,
534                                                    s_9, s_10, s_11, s_12, s_13, s_14, s_15,
535                                                    s_16, s_17, s_18, file_name, "%s")
[51f14603]536
537    def _check_html_format(self, name):
538        """
539        Check string '%' for html format
540        """
541        if name.count('%'):
542            name = name.replace('%', '&#37')
[78a205a]543
[51f14603]544        return name
[78a205a]545
[51f14603]546    def set_saved_state(self, name, value):
547        """
[78a205a]548        Set the state list
549
[51f14603]550        : param name: name of the state component
551        : param value: value of the state component
552        """
[78a205a]553        rb_list = [['power_law_low', 'guinier'],
554                   ['fit_enable_low', 'fix_enable_low'],
555                   ['fit_enable_high', 'fix_enable_high']]
[51f14603]556
[78a205a]557        self.name = value
558        self.saved_state[name] = value
559        # set the count part of radio button clicked
560        # False for the saved_state
561        for title, content in rb_list:
562            if name == title:
563                name = content
564                value = False
565            elif name == content:
566                name = title
567                value = False
568        self.saved_state[name] = value
569        self.state_num = self.saved_state['state_num']
[51f14603]570
571    def set_plot_state(self, extra_high=False, extra_low=False):
572        """
573        Build image state that wx.html understand
574        by plotting, putting it into wx.FileSystem image object
[78a205a]575
[51f14603]576        : extrap_high,extra_low: low/high extrapolations
577        are possible extra-plots
578        """
579        # some imports
580        import wx
581        import matplotlib.pyplot as plt
582        from matplotlib.backends.backend_agg import FigureCanvasAgg
583
[78a205a]584        # we use simple plot, not plotpanel
585        # make matlab figure
[51f14603]586        fig = plt.figure()
587        fig.set_facecolor('w')
588        graph = fig.add_subplot(111)
589
[78a205a]590        # data plot
[51f14603]591        graph.errorbar(self.data.x, self.data.y, yerr=self.data.dy, fmt='o')
[78a205a]592        # low Q extrapolation fit plot
[51f14603]593        if not extra_low == 'False':
[78a205a]594            graph.plot(self.theory_lowQ.x, self.theory_lowQ.y)
595        # high Q extrapolation fit plot
[51f14603]596        if not extra_high == 'False':
[78a205a]597            graph.plot(self.theory_highQ.x, self.theory_highQ.y)
[51f14603]598        graph.set_xscale("log", nonposx='clip')
599        graph.set_yscale("log", nonposy='clip')
[78a205a]600        graph.set_xlabel('$\\rm{Q}(\\AA^{-1})$', fontsize=12)
601        graph.set_ylabel('$\\rm{Intensity}(cm^{-1})$', fontsize=12)
[51f14603]602        canvas = FigureCanvasAgg(fig)
[78a205a]603        # actually make image
[51f14603]604        canvas.draw()
[78a205a]605
606        # make python.Image object
607        # size
[51f14603]608        w, h = canvas.get_width_height()
[78a205a]609        # convert to wx.Image
610        wximg = wx.EmptyImage(w, h)
611        # wxim.SetData(img.convert('RGB').tostring() )
612        wximg.SetData(canvas.tostring_rgb())
613        # get the dynamic image for the htmlwindow
[51f14603]614        wximgbmp = wx.BitmapFromImage(wximg)
[78a205a]615        # store the image in wx.FileSystem Object
[fa412df]616        imgs, refs = ReportImageHandler.set_figs([fig], [wximgbmp], 'inv')
[ec4b19c]617
618        self.wximgbmp = refs[0]
619        self.image = imgs[0]
[51f14603]620
621class Reader(CansasReader):
622    """
623    Class to load a .inv invariant file
624    """
[78a205a]625    # # File type
[51f14603]626    type_name = "Invariant"
[78a205a]627
628    # # Wildcards
[51f14603]629    type = ["Invariant file (*.inv)|*.inv",
[b9a5f0e]630            "SASView file (*.svs)|*.svs"]
[78a205a]631    # # List of allowed extensions
632    ext = ['.inv', '.INV', '.svs', 'SVS']
633
[51f14603]634    def __init__(self, call_back, cansas=True):
635        """
636        Initialize the call-back method to be called
637        after we load a file
[78a205a]638
[51f14603]639        : param call_back: call-back method
640        : param cansas:  True = files will be written/read in CanSAS format
[78a205a]641                        False = write CanSAS format
[51f14603]642        """
[78a205a]643        # # Call back method to be executed after a file is read
[51f14603]644        self.call_back = call_back
[78a205a]645        # # CanSAS format flag
[51f14603]646        self.cansas = cansas
647        self.state = None
648
649    def read(self, path):
[78a205a]650        """
[51f14603]651        Load a new invariant state from file
[78a205a]652
[51f14603]653        : param path: file path
654        : return: None
655        """
[2469df7]656        if self.cansas:
[51f14603]657            return self._read_cansas(path)
658        else:
659            return self._read_standalone(path)
[78a205a]660
[51f14603]661    def _read_standalone(self, path):
[78a205a]662        """
[51f14603]663        Load a new invariant state from file.
664        The invariant node is assumed to be the top element.
[78a205a]665
[51f14603]666        : param path: file path
667        : return: None
668        """
669        # Read the new state from file
670        state = InvariantState()
671
672        state.fromXML(file=path)
[78a205a]673
[51f14603]674        # Call back to post the new state
675        self.call_back(state)
676        return None
[78a205a]677
[51f14603]678    def _parse_state(self, entry):
679        """
680        Read an invariant result from an XML node
[78a205a]681
682        : param entry: XML node to read from
[51f14603]683        : return: InvariantState object
684        """
685        state = None
686        # Locate the invariant node
687        try:
688            nodes = entry.xpath('ns:%s' % INVNODE_NAME,
689                                namespaces={'ns': CANSAS_NS})
690            # Create an empty state
691            if nodes != []:
692                state = InvariantState()
693                state.fromXML(node=nodes[0])
694        except:
695            msg = "XML document does not contain invariant"
696            msg += " information.\n %s" % sys.exc_value
[c155a16]697            logger.info(msg)
[51f14603]698        return state
[78a205a]699
[51f14603]700    def _read_cansas(self, path):
[78a205a]701        """
[51f14603]702        Load data and invariant information from a CanSAS XML file.
[78a205a]703
[51f14603]704        : param path: file path
[78a205a]705        : return: Data1D object if a single SASentry was found,
[51f14603]706                    or a list of Data1D objects if multiple entries were found,
707                    or None of nothing was found
708        : raise RuntimeError: when the file can't be opened
709        : raise ValueError: when the length of the data vectors are inconsistent
710        """
711        output = []
712        if os.path.isfile(path):
[78a205a]713            basename = os.path.basename(path)
[51f14603]714            root, extension = os.path.splitext(basename)
[78a205a]715
[51f14603]716            if  extension.lower() in self.ext or \
717                extension.lower() == '.xml':
718                tree = etree.parse(path, parser=etree.ETCompatXMLParser())
[78a205a]719
[51f14603]720                # Check the format version number
[78a205a]721                # Specifying the namespace will take care of
722                # the file format version
[51f14603]723                root = tree.getroot()
[78a205a]724
[51f14603]725                entry_list = root.xpath('/ns:SASroot/ns:SASentry',
726                                        namespaces={'ns': CANSAS_NS})
[78a205a]727
[51f14603]728                for entry in entry_list:
729                    invstate = self._parse_state(entry)
[78a205a]730                    # invstate could be None when .svs file is loaded
731                    # in this case, skip appending to output
[7432acb]732                    if invstate is not None:
[1fa4f736]733                        sas_entry, _ = self._parse_entry(entry)
[51f14603]734                        sas_entry.meta_data['invstate'] = invstate
735                        sas_entry.filename = invstate.file
736                        output.append(sas_entry)
737        else:
738            raise RuntimeError, "%s is not a file" % path
739
740        # Return output consistent with the loader's api
741        if len(output) == 0:
742            return None
743        elif len(output) == 1:
744            # Call back to post the new state
745            self.state = output[0].meta_data['invstate']
746            self.call_back(state=output[0].meta_data['invstate'],
[78a205a]747                           datainfo=output[0])
[51f14603]748            return output[0]
749        else:
[78a205a]750            return output
751
[51f14603]752    def get_state(self):
753        return self.state
[78a205a]754
[51f14603]755    def write(self, filename, datainfo=None, invstate=None):
756        """
757        Write the content of a Data1D as a CanSAS XML file
[78a205a]758
[51f14603]759        : param filename: name of the file to write
760        : param datainfo: Data1D object
761        : param invstate: InvariantState object
762        """
763        # Sanity check
[2469df7]764        if self.cansas:
[78a205a]765            doc = self.write_toXML(datainfo, invstate)
[51f14603]766            # Write the XML document
767            fd = open(filename, 'w')
768            fd.write(doc.toprettyxml())
769            fd.close()
770        else:
771            invstate.toXML(file=filename)
[78a205a]772
[51f14603]773    def write_toXML(self, datainfo=None, state=None):
774        """
775        Write toXML, a helper for write()
[78a205a]776
[51f14603]777        : return: xml doc
778        """
779        if datainfo is None:
[c10d9d6c]780            datainfo = sas.sascalc.dataloader.data_info.Data1D(x=[], y=[])
781        elif not issubclass(datainfo.__class__, sas.sascalc.dataloader.data_info.Data1D):
[51f14603]782            msg = "The cansas writer expects a Data1D"
783            msg += " instance: %s" % str(datainfo.__class__.__name__)
784            raise RuntimeError, msg
[78a205a]785        # make sure title and data run is filled up.
[235f514]786        if datainfo.title is None or datainfo.title == '':
[51f14603]787            datainfo.title = datainfo.name
[235f514]788        if datainfo.run_name is None or datainfo.run_name == {}:
[51f14603]789            datainfo.run = [str(datainfo.name)]
790            datainfo.run_name[0] = datainfo.name
791        # Create basic XML document
792        doc, sasentry = self._to_xml_doc(datainfo)
793        # Add the invariant information to the XML document
794        if state is not None:
[78a205a]795            doc = state.toXML(datainfo.name, doc=doc, entry_node=sasentry)
[51f14603]796        return doc
Note: See TracBrowser for help on using the repository browser.