source: sasview/src/sas/sasgui/perspectives/file_converter/converter_panel.py @ 9318c4e

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

Remove redundant logic from converter_panel

  • Property mode set to 100644
File size: 10.8 KB
Line 
1"""
2This module provides a GUI for the file converter
3"""
4
5import wx
6import sys
7import numpy as np
8from wx.lib.scrolledpanel import ScrolledPanel
9from sas.sasgui.guiframe.panel_base import PanelBase
10from sas.sasgui.perspectives.calculator import calculator_widgets as widget
11from sas.sasgui.perspectives.file_converter.converter_widgets import VectorInput
12from sas.sasgui.perspectives.file_converter.meta_panels import MetadataWindow
13from sas.sasgui.perspectives.file_converter.meta_panels import DetectorPanel
14from sas.sasgui.perspectives.file_converter.meta_panels import SamplePanel
15from sas.sasgui.guiframe.events import StatusEvent
16from sas.sasgui.guiframe.dataFitting import Data1D
17from sas.sasgui.guiframe.utils import check_float
18from sas.sascalc.dataloader.readers.cansas_reader import Reader as CansasReader
19from sas.sascalc.dataloader.data_info import Detector
20from sas.sascalc.dataloader.data_info import Sample
21from sas.sascalc.dataloader.data_info import Vector
22
23# Panel size
24if sys.platform.count("win32") > 0:
25    PANEL_TOP = 0
26    _STATICBOX_WIDTH = 410
27    _BOX_WIDTH = 200
28    PANEL_SIZE = 480
29    FONT_VARIANT = 0
30else:
31    PANEL_TOP = 60
32    _STATICBOX_WIDTH = 430
33    _BOX_WIDTH = 200
34    PANEL_SIZE = 500
35    FONT_VARIANT = 1
36
37class ConverterPanel(ScrolledPanel, PanelBase):
38
39    def __init__(self, parent, base=None, *args, **kwargs):
40        ScrolledPanel.__init__(self, parent, *args, **kwargs)
41        PanelBase.__init__(self)
42        self.SetupScrolling()
43        self.SetWindowVariant(variant=FONT_VARIANT)
44
45        self.base = base
46        self.parent = parent
47        self.meta_frames = []
48
49        self.q_input = None
50        self.iq_input = None
51        self.output = None
52
53        self.metadata = {
54            'title': None,
55            'run': None,
56            'run_name': None,
57            'instrument': None,
58            'detector': [Detector()],
59            'sample': Sample()
60        }
61
62        self._do_layout()
63        self.SetAutoLayout(True)
64        self.Layout()
65
66    def convert_to_cansas(self, data, filename):
67        reader = CansasReader()
68        reader.write(filename, data)
69
70    def extract_data(self, filename):
71        data = np.loadtxt(filename, dtype=str)
72
73        if len(data.shape) != 1:
74            msg = "Error reading {}: Only one column of data is allowed"
75            raise Exception(msg.format(filename.split('\\')[-1]))
76
77        is_float = True
78        try:
79            float(data[0])
80        except:
81            is_float = False
82
83        if not is_float:
84            end_char = data[0][-1]
85            # If lines end with comma or semi-colon, trim the last character
86            if end_char == ',' or end_char == ';':
87                data = map(lambda s: s[0:-1], data)
88            else:
89                msg = ("Error reading {}: Lines must end with a digit, comma "
90                    "or semi-colon").format(filename.split('\\')[-1])
91                raise Exception(msg)
92
93        return np.array(data, dtype=np.float32)
94
95    def on_convert(self, event):
96        if not self.validate_inputs():
97            return
98
99        try:
100            qdata = self.extract_data(self.q_input.GetPath())
101            iqdata = self.extract_data(self.iq_input.GetPath())
102        except Exception as ex:
103            msg = str(ex)
104            wx.PostEvent(self.parent.manager.parent,
105                StatusEvent(status=msg, info='error'))
106            return
107
108        output_path = self.output.GetPath()
109        data = Data1D(x=qdata, y=iqdata)
110        data.filename = output_path.split('\\')[-1]
111
112        if self.metadata['run'] is not None:
113            run = self.metadata['run']
114            run_name = self.metadata['run_name']
115
116            if not isinstance(run, list) and run is not None:
117                self.metadata['run'] = [run]
118            else:
119                run = run[0]
120
121            if not isinstance(run_name, dict):
122                if run_name is not None:
123                    self.metadata['run_name'] = { run: run_name }
124                else:
125                    self.metadata['run_name'] = {}
126            elif run_name != {}:
127                self.metadata['run_name'][run] = run_name.values()[0]
128        else:
129            self.metadata['run'] = []
130            self.metadata['run_name'] = {}
131
132        for attr, value in self.metadata.iteritems():
133            if value is not None:
134                setattr(data, attr, value)
135
136        self.convert_to_cansas(data, output_path)
137        wx.PostEvent(self.parent.manager.parent,
138            StatusEvent(status="Conversion completed."))
139
140    def validate_inputs(self):
141        msg = "You must select a"
142        if self.q_input.GetPath() == '':
143            msg += " Q Axis input file."
144        elif self.iq_input.GetPath() == '':
145            msg += "n Intensity input file."
146        elif self.output.GetPath() == '':
147            msg += "destination for the converted file."
148        if msg != "You must select a":
149            wx.PostEvent(self.parent.manager.parent,
150                StatusEvent(status=msg, info='error'))
151            return
152
153        return True
154
155    def show_detector_window(self, event):
156        if self.meta_frames != []:
157            for frame in self.meta_frames:
158                frame.panel.on_close()
159        detector_frame = MetadataWindow(DetectorPanel,
160            parent=self.parent.manager.parent, manager=self,
161            metadata=self.metadata['detector'][0], title='Detector Metadata')
162        self.meta_frames.append(detector_frame)
163        self.parent.manager.put_icon(detector_frame)
164        detector_frame.Show(True)
165
166    def show_sample_window(self, event):
167        if self.meta_frames != []:
168            for frame in self.meta_frames:
169                frame.panel.on_close()
170        sample_frame = MetadataWindow(SamplePanel,
171            parent=self.parent.manager.parent, manager=self,
172            metadata=self.metadata['sample'], title='Sample Metadata')
173        self.meta_frames.append(sample_frame)
174        self.parent.manager.put_icon(sample_frame)
175        sample_frame.Show(True)
176
177    def metadata_changed(self, event):
178        event.Skip()
179        textbox = event.GetEventObject()
180        attr = textbox.GetName()
181        value = textbox.GetValue().strip()
182
183        if value == '':
184            self.metadata[attr] = None
185        else:
186            self.metadata[attr] = value
187
188
189    def _do_layout(self):
190        vbox = wx.BoxSizer(wx.VERTICAL)
191
192        instructions = ("Select the 1 column ASCII files containing the Q Axis"
193            " and Intensity data, chose where to save the converted file, then"
194            " click Convert to convert them to CanSAS XML format. If required,"
195            " metadata can also be input below.")
196        instruction_label = wx.StaticText(self, -1, instructions,
197            size=(_STATICBOX_WIDTH+40, -1))
198        instruction_label.Wrap(_STATICBOX_WIDTH+40)
199        vbox.Add(instruction_label, flag=wx.TOP | wx.LEFT | wx.RIGHT, border=5)
200
201        section = wx.StaticBox(self, -1)
202        section_sizer = wx.StaticBoxSizer(section, wx.VERTICAL)
203        section_sizer.SetMinSize((_STATICBOX_WIDTH, -1))
204
205        input_grid = wx.GridBagSizer(5, 5)
206
207        q_label = wx.StaticText(self, -1, "Q Axis: ")
208        input_grid.Add(q_label, (0,0), (1,1), wx.ALIGN_CENTER_VERTICAL, 5)
209
210        self.q_input = wx.FilePickerCtrl(self, -1,
211            size=(_STATICBOX_WIDTH-80, -1),
212            message="Chose the Q Axis data file.")
213        input_grid.Add(self.q_input, (0,1), (1,1), wx.ALL, 5)
214
215        iq_label = wx.StaticText(self, -1, "Intensity Data: ")
216        input_grid.Add(iq_label, (1,0), (1,1), wx.ALIGN_CENTER_VERTICAL, 5)
217
218        self.iq_input = wx.FilePickerCtrl(self, -1,
219            size=(_STATICBOX_WIDTH-80, -1),
220            message="Chose the Intensity data file.")
221        input_grid.Add(self.iq_input, (1,1), (1,1), wx.ALL, 5)
222
223        output_label = wx.StaticText(self, -1, "Output File: ")
224        input_grid.Add(output_label, (2,0), (1,1), wx.ALIGN_CENTER_VERTICAL, 5)
225
226        self.output = wx.FilePickerCtrl(self, -1,
227            size=(_STATICBOX_WIDTH-80, -1),
228            message="Chose the Intensity data file.",
229            style=wx.FLP_SAVE | wx.FLP_OVERWRITE_PROMPT | wx.FLP_USE_TEXTCTRL,
230            wildcard="*.xml")
231        input_grid.Add(self.output, (2,1), (1,1), wx.ALL, 5)
232
233        convert_btn = wx.Button(self, wx.ID_OK, "Convert")
234        input_grid.Add(convert_btn, (3,0), (1,1), wx.ALL, 5)
235        convert_btn.Bind(wx.EVT_BUTTON, self.on_convert)
236
237        section_sizer.Add(input_grid)
238
239        vbox.Add(section_sizer, flag=wx.ALL, border=5)
240
241        metadata_section = wx.CollapsiblePane(self, -1, "Metadata",
242            size=(_STATICBOX_WIDTH+40, -1), style=wx.WS_EX_VALIDATE_RECURSIVELY)
243        metadata_pane = metadata_section.GetPane()
244        metadata_grid = wx.GridBagSizer(5, 5)
245
246        y = 0
247        for item in self.metadata.keys():
248            if item == 'detector' or item == 'sample': continue
249            label_txt = item.replace('_', ' ').capitalize()
250            label = wx.StaticText(metadata_pane, -1, label_txt,
251                style=wx.ALIGN_CENTER_VERTICAL)
252            input_box = wx.TextCtrl(metadata_pane, name=item,
253                size=(_STATICBOX_WIDTH-80, -1))
254            input_box.Bind(wx.EVT_TEXT, self.metadata_changed)
255            metadata_grid.Add(label, (y,0), (1,1),
256                wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
257            metadata_grid.Add(input_box, (y,1), (1,2), wx.EXPAND)
258            y += 1
259
260        detector_label = wx.StaticText(metadata_pane, -1,
261            "Detector:")
262        metadata_grid.Add(detector_label, (y, 0), (1,1), wx.ALL | wx.EXPAND, 5)
263        detector_btn = wx.Button(metadata_pane, -1, "Enter Detector Metadata")
264        metadata_grid.Add(detector_btn, (y, 1), (1,1), wx.ALL | wx.EXPAND, 5)
265        detector_btn.Bind(wx.EVT_BUTTON, self.show_detector_window)
266        y += 1
267
268        sample_label = wx.StaticText(metadata_pane, -1, "Sample: ")
269        metadata_grid.Add(sample_label, (y,0), (1,1), wx.ALL | wx.EXPAND, 5)
270        sample_btn = wx.Button(metadata_pane, -1, "Enter Sample Metadata")
271        metadata_grid.Add(sample_btn, (y,1), (1,1), wx.ALL | wx.EXPAND, 5)
272        sample_btn.Bind(wx.EVT_BUTTON, self.show_sample_window)
273        y += 1
274
275        metadata_pane.SetSizer(metadata_grid)
276
277        vbox.Add(metadata_section, proportion=0, flag=wx.ALL, border=5)
278
279        vbox.Fit(self)
280        self.SetSizer(vbox)
281
282class ConverterWindow(widget.CHILD_FRAME):
283
284    def __init__(self, parent=None, title='File Converter', base=None,
285        manager=None, size=(PANEL_SIZE * 1.05, PANEL_SIZE / 1.55),
286        *args, **kwargs):
287        kwargs['title'] = title
288        kwargs['size'] = size
289        widget.CHILD_FRAME.__init__(self, parent, *args, **kwargs)
290
291        self.manager = manager
292        self.panel = ConverterPanel(self, base=None)
293        self.Bind(wx.EVT_CLOSE, self.on_close)
294        self.SetPosition((wx.LEFT, PANEL_TOP))
295        self.Show(True)
296
297    def on_close(self, event):
298        if self.manager is not None:
299            self.manager.converter_frame = None
300        self.Destroy()
Note: See TracBrowser for help on using the repository browser.