source: sasview/src/sas/sasgui/perspectives/file_converter/converter_panel.py @ 11794f2

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

Add file convertor docs

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