source: sasview/src/sas/sasgui/perspectives/corfunc/corfunc_state.py @ 6970e51

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.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 6970e51 was 6970e51, checked in by lewis, 8 years ago

Add some docstrings

  • Property mode set to 100644
File size: 10.6 KB
Line 
1import time
2import sys
3import os
4import sas.sascalc.dataloader
5from lxml import etree
6from sas.sascalc.dataloader.readers.cansas_reader import Reader as CansasReader
7from sas.sascalc.dataloader.readers.cansas_reader import get_content
8from sas.sasgui.guiframe.utils import format_number
9from sas.sasgui.guiframe.gui_style import GUIFRAME_ID
10from sas.sasgui.guiframe.dataFitting import Data1D
11from sas.sascalc.dataloader.loader import Loader
12
13CORNODE_NAME = 'corfunc'
14CANSAS_NS = 'cansas1d/1.0'
15
16# The default state
17DEFAULT_STATE = {
18    'qmin_tcl': None,
19    'qmax1_tcl': None,
20    'qmax2_tcl': None,
21    'background_tcl': None
22}
23
24# List of output parameters, used by __str__
25output_list = [
26    ["long_period", "Long Period"],
27    ["hard_block_thickness", "Average Hard Block Thickness"],
28    ["interface_thickness", "Average Interface Thickness"],
29    ["core_thickness", "Average Core Thickness"],
30    ["polydispersity", "PolyDispersity"],
31    ["filling", "Filling Fraction"]
32]
33
34class CorfuncState(object):
35    """
36    Stores information about the state of CorfuncPanel
37    """
38
39    def __init__(self):
40        # Inputs
41        self.file = None
42        self.data = None
43        self.qmin = None
44        self.qmax = [0, 0]
45        self.background = None
46        self.outputs = None
47
48        self.saved_state = DEFAULT_STATE
49        # Will be filled on panel init as number of states increases
50        self.state_list = {}
51        self.timestamp = time.time()
52
53        # Raw Data
54        self.q = None
55        self.iq = None
56        # TODO: Add extrapolated data and transformed data (when implemented)
57
58    def __str__(self):
59        """
60        Pretty print the state
61
62        :return: A string representing the state
63        """
64        state = "File:         {}\n".format(self.file)
65        state += "Timestamp:    {}\n".format(self.timestamp)
66        state += "Qmin:         {}\n".format(str(self.qmin))
67        state += "Qmax:         {}\n".format(str(self.qmax))
68        state += "Background:   {}\n".format(str(self.background))
69        if self.outputs is None:
70            return state
71        state += "\nOutputs:\n"
72        for output in output_list:
73            key = output[0]
74            name = output[1]
75            state += "{}: {}\n".format(name, str(self.outputs[key]))
76
77        return state
78
79    def set_saved_state(self, name, value):
80        """
81        Set a value in the current state.
82
83        :param name: The name of the parameter to set
84        :param value: The value to set the parameter to
85        """
86        self.saved_state[name] = value
87        if name == 'qmin_tcl':
88            self.qmin = value
89        elif name == 'qmax1_tcl':
90            self.qmax[0] = value
91        elif name == 'qmax2_tcl':
92            self.qmax[1] = value
93        elif name == 'background_tcl':
94            self.background = value
95
96    def toXML(self, filename='corfunc_state.cor', doc=None, entry_node=None):
97        """
98        Writes the state of the CorfuncPanel panel to file, as XML.
99
100        Compatible with standalone writing, or appending to an
101        already existing XML document. In that case, the XML document
102        is required. An optional entry node in the XML document
103        may also be given.
104
105        :param file: file to write to
106        :param doc: XML document object [optional]
107        :param entry_node: XML node within the XML document at which
108            we will append the data [optional]
109
110        :return: None if no doc is provided, modified XML document if doc!=None
111        """
112        from xml.dom.minidom import getDOMImplementation
113
114        top_element = None
115        new_doc = None
116        if doc is None:
117            # Create a new XML document
118            impl = getDOMImplementation()
119            doc_type = impl.createDocumentType(CORNODE_NAME, "1.0", "1.0")
120            new_doc = impl.createDocument(None, CORNODE_NAME, doc_type)
121            top_element = new_doc.documentElement
122        else:
123            # Create a new element in the document provided
124            top_element = doc.createElement(CORNODE_NAME)
125            if entry_node is None:
126                doc.documentElement.appendChild(top_element)
127            else:
128                entry_node.appendChild(top_element)
129            new_doc = doc
130
131        # Version
132        attr = new_doc.createAttribute("version")
133        attr.nodeValue = '1.0'
134        top_element.setAttributeNode(attr)
135
136        # Filename
137        element = new_doc.createElement("filename")
138        if self.file is not None and self.file != '':
139            element.appendChild(new_doc.createTextNode(str(self.file)))
140        else:
141            element.appendChild(new_doc.createTextNode(str(filename)))
142        top_element.appendChild(element)
143
144        # Timestamp
145        element = new_doc.createElement("timestamp")
146        # Pretty printed format
147        element.appendChild(new_doc.createTextNode(time.ctime(self.timestamp)))
148        attr = new_doc.createAttribute("epoch")
149        # Epoch value (used in self.fromXML)
150        attr.nodeValue = str(self.timestamp)
151        element.setAttributeNode(attr)
152        top_element.appendChild(element)
153
154        # Current state
155        state = new_doc.createElement("state")
156        top_element.appendChild(state)
157        for name, value in self.saved_state.iteritems():
158            element = new_doc.createElement(name)
159            element.appendChild(new_doc.createTextNode(str(value)))
160            state.appendChild(element)
161
162
163        # Save the file or return the original document with the state
164        # data appended
165        if doc is None:
166            fd = open(filename, 'w')
167            fd.write(new_doc.toprettyxml())
168            fd.close()
169            return None
170        else:
171            return new_doc
172
173
174    def fromXML(self, node):
175        """
176        Load corfunc states from a file
177
178        :param node: node of an XML document to read from (optional)
179        """
180        if node.get('version') and node.get('version') == '1.0':
181            # Parse filename
182            entry = get_content('ns:filename', node)
183            if entry is not None:
184                self.file = entry.text.strip()
185
186            # Parse timestamp
187            entry = get_content('ns:timestamp', node)
188            if entry is not None and entry.get('epoch'):
189                try:
190                    self.timestamp = (entry.get('epoch'))
191                except:
192                    msg = ("CorfuncState.fromXML: Could not read timestamp",
193                        "\n{}").format(sys.exc_value)
194                    logging.error(msg)
195
196            # Parse current state
197            entry = get_content('ns:state', node)
198            if entry is not None:
199                for item in DEFAULT_STATE.iterkeys():
200                    input_field = get_content("ns:{}".format(item), entry)
201                    if input_field is not None:
202                        try:
203                            value = float(input_field.text.strip())
204                        except:
205                            value = None
206                        self.set_saved_state(name=item, value=value)
207
208
209
210class Reader(CansasReader):
211    """
212    Reads a CanSAS file containing the state of a CorfuncPanel
213    """
214
215    type_name = "Corfunc"
216
217    def __init__(self, callback):
218        self.callback = callback
219        self.state = None
220
221    def read(self, path):
222        """
223        Load data and corfunc information frmo a CanSAS file.
224
225        :param path: The file path to read from
226        :return: Data1D object, a list of Data1D objects, or None
227        :raise IOError: When the file can't be found
228        :raise IOError: When the file is an invalid file type
229        :raise ValueError: When the length of the data vectors are inconsistent
230        """
231        output = []
232        if os.path.isfile(path):
233            # Load file
234            basename = os.path.basename(path)
235            root, ext = os.path.splitext(basename)
236            if ext.lower() != '.xml' and ext.lower() != '.svs':
237                raise IOError, "{} is not a supported file type".format(ext)
238            tree = etree.parse(path, parser=etree.ETCompatXMLParser())
239            root = tree.getroot()
240            entry_list = root.xpath('/ns:SASroot/ns:SASentry',
241                namespaces={'ns': CANSAS_NS})
242            for entry in entry_list:
243                sas_entry, _ = self._parse_entry(entry)
244                corstate = self._parse_state(entry)
245
246                if corstate != None:
247                    sas_entry.meta_data['corstate'] = corstate
248                    sas_entry.filename = corstate.file
249                    output.append(sas_entry)
250        else:
251            # File not found
252            msg = "{} is not a valid file path or doesn't exist".format(path)
253            raise IOError, msg
254
255        if len(output) == 0:
256            return None
257        elif len(output) == 1:
258            self.callback(output[0].meta_data['corstate'], datainfo=output[0])
259            return output[0]
260        else:
261            return output
262
263    def write(self, filename, datainfo=None, state=None):
264        """
265        Write the content of a Data1D as a CanSAS file.
266
267        : param filename: Name of the file to write
268        : param datainfo: Data1D object
269        : param state: CorfuncState object
270        """
271        # Prepare datainfo
272        if datainfo is None:
273            datainfo = Data1D(x=[], y=[])
274        elif not issubclass(datainfo.__class__, Data1D):
275            msg = ("The CanSAS writer expects a Data1D instance. {} was ",
276                "provided").format(datainfo.__class__.__name__)
277            raise RuntimeError, msg
278        if datainfo.title is None or datainfo.title == '':
279            datainfo.title = datainfo.name
280        if datainfo.run_name == None or datainfo.run_name == '':
281            datainfo.run = [str(datainfo.name)]
282            datainfo.run_name[0] = datainfo.name
283
284        # Create the XMl doc
285        doc, sasentry = self._to_xml_doc(datainfo)
286        if state is not None:
287            doc = state.toXML(doc=doc, entry_node=sasentry)
288
289        # Write the XML doc to a file
290        fd = open(filename, 'w')
291        fd.write(doc.toprettyxml())
292        fd.close()
293
294    def get_state(self):
295        return self.state
296
297
298    def _parse_state(self, entry):
299        """
300        Read state data from an XML node
301
302        :param entry: The XML node to read from
303        :return: CorfuncState object
304        """
305        state = None
306        try:
307            nodes = entry.xpath('ns:{}'.format(CORNODE_NAME),
308                namespaces={'ns': CANSAS_NS})
309            if nodes != []:
310                state = CorfuncState()
311                state.fromXML(nodes[0])
312        except:
313            msg = "XML document does not contain CorfuncState information\n{}"
314            msg.format(sys.exc_value)
315            logging.info(msg)
316        return state
Note: See TracBrowser for help on using the repository browser.