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

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 a9b7a8f was a9b7a8f, checked in by lewis, 8 years ago

Tidy up

  • 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        """
65        state = "File:         {}\n".format(self.file)
66        state += "Timestamp:    {}\n".format(self.timestamp)
67        state += "Qmin:         {}\n".format(str(self.qmin))
68        state += "Qmax:         {}\n".format(str(self.qmax))
69        state += "Background:   {}\n".format(str(self.background))
70        if self.outputs is None:
71            return state
72        state += "\nOutputs:\n"
73        for output in output_list:
74            key = output[0]
75            name = output[1]
76            state += "{}: {}\n".format(name, str(self.outputs[key]))
77
78        return state
79
80    def set_saved_state(self, name, value):
81        """
82        Set a value in the current state.
83
84        :param name: The name of the parameter to set
85        :param value: The value to set the parameter to
86        """
87        self.saved_state[name] = value
88        if name == 'qmin_tcl':
89            self.qmin = value
90        elif name == 'qmax1_tcl':
91            self.qmax[0] = value
92        elif name == 'qmax2_tcl':
93            self.qmax[1] = value
94        elif name == 'background_tcl':
95            self.background = value
96
97    def toXML(self, filename='corfunc_state.cor', doc=None, entry_node=None):
98        """
99        Writes the state of the CorfuncPanel panel to file, as XML.
100
101        Compatible with standalone writing, or appending to an
102        already existing XML document. In that case, the XML document
103        is required. An optional entry node in the XML document
104        may also be given.
105
106        :param file: file to write to
107        :param doc: XML document object [optional]
108        :param entry_node: XML node within the XML document at which
109            we will append the data [optional]
110
111        :return: None if no doc is provided, modified XML document if doc!=None
112        """
113        from xml.dom.minidom import getDOMImplementation
114
115        top_element = None
116        new_doc = None
117        if doc is None:
118            # Create a new XML document
119            impl = getDOMImplementation()
120            doc_type = impl.createDocumentType(CORNODE_NAME, "1.0", "1.0")
121            new_doc = impl.createDocument(None, CORNODE_NAME, doc_type)
122            top_element = new_doc.documentElement
123        else:
124            # Create a new element in the document provided
125            top_element = doc.createElement(CORNODE_NAME)
126            if entry_node is None:
127                doc.documentElement.appendChild(top_element)
128            else:
129                entry_node.appendChild(top_element)
130            new_doc = doc
131
132        # Version
133        attr = new_doc.createAttribute("version")
134        attr.nodeValue = '1.0'
135        top_element.setAttributeNode(attr)
136
137        # Filename
138        element = new_doc.createElement("filename")
139        if self.file is not None and self.file != '':
140            element.appendChild(new_doc.createTextNode(str(self.file)))
141        else:
142            element.appendChild(new_doc.createTextNode(str(filename)))
143        top_element.appendChild(element)
144
145        # Timestamp
146        element = new_doc.createElement("timestamp")
147        # Pretty printed format
148        element.appendChild(new_doc.createTextNode(time.ctime(self.timestamp)))
149        attr = new_doc.createAttribute("epoch")
150        # Epoch value (used in self.fromXML)
151        attr.nodeValue = str(self.timestamp)
152        element.setAttributeNode(attr)
153        top_element.appendChild(element)
154
155        # Current state
156        state = new_doc.createElement("state")
157        top_element.appendChild(state)
158        for name, value in self.saved_state.iteritems():
159            element = new_doc.createElement(name)
160            element.appendChild(new_doc.createTextNode(str(value)))
161            state.appendChild(element)
162
163
164        # Save the file or return the original document with the state
165        # data appended
166        if doc is None:
167            fd = open(filename, 'w')
168            fd.write(new_doc.toprettyxml())
169            fd.close()
170            return None
171        else:
172            return new_doc
173
174
175    def fromXML(self, node):
176        """
177        Load corfunc states from a file
178
179        :param node: node of an XML document to read from (optional)
180        """
181        if node.get('version') and node.get('version') == '1.0':
182            # Parse filename
183            entry = get_content('ns:filename', node)
184            if entry is not None:
185                self.file = entry.text.strip()
186
187            # Parse timestamp
188            entry = get_content('ns:timestamp', node)
189            if entry is not None and entry.get('epoch'):
190                try:
191                    self.timestamp = (entry.get('epoch'))
192                except:
193                    msg = ("CorfuncState.fromXML: Could not read timestamp",
194                        "\n{}").format(sys.exc_value)
195                    logging.error(msg)
196
197            # Parse current state
198            entry = get_content('ns:state', node)
199            if entry is not None:
200                for item in DEFAULT_STATE.iterkeys():
201                    input_field = get_content("ns:{}".format(item), entry)
202                    if input_field is not None:
203                        try:
204                            value = float(input_field.text.strip())
205                        except:
206                            value = None
207                        self.set_saved_state(name=item, value=value)
208
209
210
211class Reader(CansasReader):
212    """
213    Reads a CanSAS file containing the state of a CorfuncPanel
214    """
215
216    type_name = "Corfunc"
217
218    def __init__(self, callback):
219        self.callback = callback
220        self.state = None
221
222    def read(self, path):
223        """
224        Load data and corfunc information frmo a CanSAS file.
225
226        :param path: The file path to read from
227        :return: Data1D object, a list of Data1D objects, or None
228        :raise IOError: When the file can't be found
229        :raise IOError: When the file is an invalid file type
230        :raise ValueError: When the length of the data vectors are inconsistent
231        """
232        output = []
233        if os.path.isfile(path):
234            # Load file
235            basename = os.path.basename(path)
236            root, ext = os.path.splitext(basename)
237            if ext.lower() != '.xml' and ext.lower() != '.svs':
238                raise IOError, "{} is not a supported file type".format(ext)
239            tree = etree.parse(path, parser=etree.ETCompatXMLParser())
240            root = tree.getroot()
241            entry_list = root.xpath('/ns:SASroot/ns:SASentry',
242                namespaces={'ns': CANSAS_NS})
243            for entry in entry_list:
244                sas_entry, _ = self._parse_entry(entry)
245                corstate = self._parse_state(entry)
246
247                if corstate != None:
248                    sas_entry.meta_data['corstate'] = corstate
249                    sas_entry.filename = corstate.file
250                    output.append(sas_entry)
251        else:
252            # File not found
253            msg = "{} is not a valid file path or doesn't exist".format(path)
254            raise IOError, msg
255
256        if len(output) == 0:
257            return None
258        elif len(output) == 1:
259            self.callback(output[0].meta_data['corstate'], datainfo=output[0])
260            return output[0]
261        else:
262            return output
263
264    def write(self, filename, datainfo=None, state=None):
265        """
266        Write the content of a Data1D as a CanSAS file.
267
268        : param filename: Name of the file to write
269        : param datainfo: Data1D object
270        : param state: CorfuncState object
271        """
272        # Prepare datainfo
273        if datainfo is None:
274            datainfo = Data1D(x=[], y=[])
275        elif not issubclass(datainfo.__class__, Data1D):
276            msg = ("The CanSAS writer expects a Data1D instance. {} was ",
277                "provided").format(datainfo.__class__.__name__)
278            raise RuntimeError, msg
279        if datainfo.title is None or datainfo.title == '':
280            datainfo.title = datainfo.name
281        if datainfo.run_name == None or datainfo.run_name == '':
282            datainfo.run = [str(datainfo.name)]
283            datainfo.run_name[0] = datainfo.name
284
285        # Create the XMl doc
286        doc, sasentry = self._to_xml_doc(datainfo)
287        if state is not None:
288            doc = state.toXML(doc=doc, entry_node=sasentry)
289
290        # Write the XML doc to a file
291        fd = open(filename, 'w')
292        fd.write(doc.toprettyxml())
293        fd.close()
294
295    def get_state(self):
296        return self.state
297
298
299    def _parse_state(self, entry):
300        """
301        Read state data from an XML node
302
303        :param entry: The XML node to read from
304        :return: CorfuncState object
305        """
306        state = None
307        try:
308            nodes = entry.xpath('ns:{}'.format(CORNODE_NAME),
309                namespaces={'ns': CANSAS_NS})
310            if nodes != []:
311                state = CorfuncState()
312                state.fromXML(nodes[0])
313        except:
314            msg = "XML document does not contain CorfuncState information\n{}"
315            msg.format(sys.exc_value)
316            logging.info(msg)
317        return state
Note: See TracBrowser for help on using the repository browser.