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

magnetic_scattrelease-4.2.2ticket-1009ticket-1249
Last change on this file since 5251ec6 was 5251ec6, checked in by Paul Kienzle <pkienzle@…>, 6 years ago

improved support for py37 in sasgui

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