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

Last change on this file since afb311e was a26f67f, checked in by Paul Kienzle <pkienzle@…>, 7 years ago

fixup api doc errors

  • Property mode set to 100644
File size: 25.7 KB
RevLine 
[77d92cd]1"""
2This module provides a GUI for the file converter
3"""
4
5import wx
6import sys
[05595c4]7import os
[a58706d]8import numpy as np
[77d92cd]9from wx.lib.scrolledpanel import ScrolledPanel
10from sas.sasgui.guiframe.panel_base import PanelBase
11from sas.sasgui.perspectives.calculator import calculator_widgets as widget
[0e11ec7]12from sas.sasgui.perspectives.file_converter.converter_widgets import FileInput
[de0df2c]13from sas.sasgui.perspectives.file_converter.meta_panels import MetadataWindow
14from sas.sasgui.perspectives.file_converter.meta_panels import DetectorPanel
[2a7722b]15from sas.sasgui.perspectives.file_converter.meta_panels import SamplePanel
[55bc56bc]16from sas.sasgui.perspectives.file_converter.meta_panels import SourcePanel
[eb8da5f]17from sas.sasgui.perspectives.file_converter.frame_select_dialog import FrameSelectDialog
[a58706d]18from sas.sasgui.guiframe.events import StatusEvent
[11794f2]19from sas.sasgui.guiframe.documentation_window import DocumentationWindow
[a58706d]20from sas.sasgui.guiframe.dataFitting import Data1D
[e9e777c]21from sas.sascalc.dataloader.data_info import Data2D
[fdbea3c]22from sas.sasgui.guiframe.utils import check_float
[0b1a677]23from sas.sascalc.file_converter.cansas_writer import CansasWriter
24from sas.sascalc.file_converter.otoko_loader import OTOKOLoader
25from sas.sascalc.file_converter.bsl_loader import BSLLoader
[eacc0b0]26from sas.sascalc.file_converter.ascii2d_loader import ASCII2DLoader
[e9e777c]27from sas.sascalc.file_converter.nxcansas_writer import NXcanSASWriter
[f2b3f28]28from sas.sascalc.dataloader.data_info import Detector
[2a7722b]29from sas.sascalc.dataloader.data_info import Sample
[55bc56bc]30from sas.sascalc.dataloader.data_info import Source
[fdbea3c]31from sas.sascalc.dataloader.data_info import Vector
[77d92cd]32
33# Panel size
34if sys.platform.count("win32") > 0:
35    PANEL_TOP = 0
36    _STATICBOX_WIDTH = 410
37    _BOX_WIDTH = 200
[0267d56]38    PANEL_SIZE = 520
[77d92cd]39    FONT_VARIANT = 0
40else:
41    PANEL_TOP = 60
42    _STATICBOX_WIDTH = 430
43    _BOX_WIDTH = 200
[0267d56]44    PANEL_SIZE = 540
[77d92cd]45    FONT_VARIANT = 1
46
47class ConverterPanel(ScrolledPanel, PanelBase):
[ba65aff]48    """
49    This class provides the File Converter GUI
50    """
[77d92cd]51
52    def __init__(self, parent, base=None, *args, **kwargs):
53        ScrolledPanel.__init__(self, parent, *args, **kwargs)
54        PanelBase.__init__(self)
[a58706d]55        self.SetupScrolling()
[77d92cd]56        self.SetWindowVariant(variant=FONT_VARIANT)
57
58        self.base = base
59        self.parent = parent
[de0df2c]60        self.meta_frames = []
[77d92cd]61
[ba65aff]62        # GUI inputs
[a58706d]63        self.q_input = None
64        self.iq_input = None
65        self.output = None
[d6bf064]66        self.radiation_input = None
[35488b2]67        self.convert_btn = None
[d6bf064]68        self.metadata_section = None
[ba65aff]69
[4b862c4]70        self.data_type = "ascii"
[a58706d]71
[ba65aff]72        # Metadata values
[94667e27]73        self.title = ''
74        self.run = ''
75        self.run_name = ''
76        self.instrument = ''
[503cc34]77        self.detector = Detector()
78        self.sample = Sample()
79        self.source = Source()
80        self.properties = ['title', 'run', 'run_name', 'instrument']
81
82        self.detector.name = ''
83        self.source.radiation = 'neutron'
[4b862c4]84
[a58706d]85        self._do_layout()
[77d92cd]86        self.SetAutoLayout(True)
87        self.Layout()
88
[9e9f848]89    def convert_to_cansas(self, frame_data, filepath, single_file):
[ba65aff]90        """
91        Saves an array of Data1D objects to a single CanSAS file with multiple
92        <SasData> elements, or to multiple CanSAS files, each with one
93        <SasData> element.
94
[a26f67f]95        :param frame_data: If single_file is true, an array of Data1D
96            objects. If single_file is false, a dictionary of the
97            form *{frame_number: Data1D}*.
[9e9f848]98        :param filepath: Where to save the CanSAS file
[a26f67f]99        :param single_file: If true, array is saved as a single file,
100            if false, each item in the array is saved to it's own file
[ba65aff]101        """
102        writer = CansasWriter()
[ea2a7348]103        entry_attrs = None
[94667e27]104        if self.run_name != '':
[ea2a7348]105            entry_attrs = { 'name': self.run_name }
[5451a78]106
[94f4518]107        if single_file:
[9e9f848]108            writer.write(filepath, frame_data,
[94f4518]109                sasentry_attrs=entry_attrs)
110        else:
[9e9f848]111            # Folder and base filename
112            [group_path, group_name] = os.path.split(filepath)
113            ext = "." + group_name.split('.')[-1] # File extension
114            for frame_number, frame_data in frame_data.iteritems():
115                # Append frame number to base filename
116                filename = group_name.replace(ext, str(frame_number)+ext)
117                destination = os.path.join(group_path, filename)
118                writer.write(destination, [frame_data],
[94f4518]119                    sasentry_attrs=entry_attrs)
[a58706d]120
[535e181]121    def extract_ascii_data(self, filename):
[ba65aff]122        """
123        Extracts data from a single-column ASCII file
124
125        :param filename: The file to load data from
126        :return: A numpy array containing the extracted data
127        """
[514a00e]128        try:
129            data = np.loadtxt(filename, dtype=str)
130        except:
131            is_bsl = False
132            # Check if file is a BSL or OTOKO header file
133            f = open(filename, 'r')
134            f.readline()
135            f.readline()
136            bsl_metadata = f.readline().strip().split()
137            f.close()
138            if len(bsl_metadata) == 10:
139                msg = ("Error parsing ASII data. {} looks like a BSL or OTOKO "
140                    "header file.")
141                raise Exception(msg.format(os.path.split(filename)[-1]))
[a58706d]142
[ff790b3]143        if len(data.shape) != 1:
144            msg = "Error reading {}: Only one column of data is allowed"
145            raise Exception(msg.format(filename.split('\\')[-1]))
146
[a58706d]147        is_float = True
148        try:
149            float(data[0])
150        except:
151            is_float = False
152
153        if not is_float:
154            end_char = data[0][-1]
155            # If lines end with comma or semi-colon, trim the last character
156            if end_char == ',' or end_char == ';':
157                data = map(lambda s: s[0:-1], data)
158            else:
159                msg = ("Error reading {}: Lines must end with a digit, comma "
160                    "or semi-colon").format(filename.split('\\')[-1])
161                raise Exception(msg)
162
163        return np.array(data, dtype=np.float32)
164
[535e181]165    def extract_otoko_data(self, filename):
[ba65aff]166        """
167        Extracts data from a 1D OTOKO file
168
169        :param filename: The OTOKO file to load the data from
170        :return: A numpy array containing the extracted data
171        """
[535e181]172        loader = OTOKOLoader(self.q_input.GetPath(),
173            self.iq_input.GetPath())
[9c500ab]174        otoko_data = loader.load_otoko_data()
175        qdata = otoko_data.q_axis.data
176        iqdata = otoko_data.data_axis.data
[535e181]177        if len(qdata) > 1:
178            msg = ("Q-Axis file has multiple frames. Only 1 frame is "
179                "allowed for the Q-Axis")
180            wx.PostEvent(self.parent.manager.parent,
181                StatusEvent(status=msg, info="error"))
182            return
183        else:
184            qdata = qdata[0]
185
186        return qdata, iqdata
187
[0b1a677]188    def extract_bsl_data(self, filename):
189        """
190        Extracts data from a 2D BSL file
191
192        :param filename: The header file to extract the data from
193        :return x_data: A 1D array containing all the x coordinates of the data
194        :return y_data: A 1D array containing all the y coordinates of the data
[a26f67f]195        :return frame_data: A dictionary of the form *{frame_number: data}*, where data is a 2D numpy array containing the intensity data
[0b1a677]196        """
197        loader = BSLLoader(filename)
198        frames = [0]
199        should_continue = True
200
201        if loader.n_frames > 1:
202            params = self.ask_frame_range(loader.n_frames)
203            frames = params['frames']
204            if len(frames) == 0:
205                should_continue = False
206        elif loader.n_rasters == 1 and loader.n_frames == 1:
207            message = ("The selected file is an OTOKO file. Please select the "
208            "'OTOKO 1D' option if you wish to convert it.")
209            dlg = wx.MessageDialog(self,
210            message,
211            'Error!',
212            wx.OK | wx.ICON_WARNING)
213            dlg.ShowModal()
214            should_continue = False
215            dlg.Destroy()
216        else:
217            message = ("The selected data file only has 1 frame, it might be"
218                " a multi-frame OTOKO file.\nContinue conversion?")
219            dlg = wx.MessageDialog(self,
220            message,
221            'Warning!',
222            wx.YES_NO | wx.ICON_WARNING)
223            should_continue = (dlg.ShowModal() == wx.ID_YES)
224            dlg.Destroy()
225
226        if not should_continue:
[fe498b83]227            return None
[0b1a677]228
[fe498b83]229        frame_data = loader.load_frames(frames)
[0b1a677]230
[fe498b83]231        return frame_data
[0b1a677]232
[eb8da5f]233    def ask_frame_range(self, n_frames):
[ba65aff]234        """
235        Display a dialog asking the user to input the range of frames they
236        would like to export
237
238        :param n_frames: How many frames the loaded data file has
239        :return: A dictionary containing the parameters input by the user
240        """
[eb8da5f]241        valid_input = False
[bec6cb8]242        _, ext = os.path.splitext(self.output.GetPath())
243        show_single_btn = (ext == '.h5')
244        dlg = FrameSelectDialog(n_frames, show_single_btn)
[eb8da5f]245        frames = None
246        increment = None
[94f4518]247        single_file = True
[eb8da5f]248        while not valid_input:
249            if dlg.ShowModal() == wx.ID_OK:
250                msg = ""
251                try:
252                    first_frame = int(dlg.first_input.GetValue())
253                    last_frame = int(dlg.last_input.GetValue())
254                    increment = int(dlg.increment_input.GetValue())
[bec6cb8]255                    if not show_single_btn:
[05595c4]256                        single_file = dlg.single_btn.GetValue()
257
[eb8da5f]258                    if last_frame < 0 or first_frame < 0:
259                        msg = "Frame values must be positive"
260                    elif increment < 1:
261                        msg = "Increment must be greater than or equal to 1"
262                    elif first_frame > last_frame:
263                        msg = "First frame must be less than last frame"
[05595c4]264                    elif last_frame >= n_frames:
[eb8da5f]265                        msg = "Last frame must be less than {}".format(n_frames)
266                    else:
267                        valid_input = True
268                except:
269                    valid_input = False
270                    msg = "Please enter valid integer values"
271
272                if not valid_input:
273                    wx.PostEvent(self.parent.manager.parent,
274                        StatusEvent(status=msg))
275            else:
[94f4518]276                return { 'frames': [], 'inc': None, 'file': single_file }
[18544f8]277        frames = range(first_frame, last_frame + 1, increment)
[94f4518]278        return { 'frames': frames, 'inc': increment, 'file': single_file }
[eb8da5f]279
[edf0e06]280    def get_metadata(self):
281        # Prepare the metadata for writing to a file
282        if self.run is None:
283            self.run = []
284        elif not isinstance(self.run, list):
285            self.run = [self.run]
[0b1a677]286
[be88076]287        run_name = None
288        if len(self.run) > 0:
289            run_number = self.run[0]
290            run_name = { run_number: self.run_name }
291
[edf0e06]292        if self.title is None:
293            self.title = ''
[e9e777c]294
[edf0e06]295        metadata = {
296            'title': self.title,
297            'run': self.run,
[be88076]298            'run_name': run_name,
[edf0e06]299            'instrument': self.instrument,
300            'detector': [self.detector],
301            'sample': self.sample,
302            'source': self.source
303        }
[535e181]304
[edf0e06]305        return metadata
[a58706d]306
[edf0e06]307    def convert_1d_data(self, qdata, iqdata):
308        """
309        Formats a 1D array of q_axis data and a 2D array of I axis data (where
310        each row of iqdata is a separate row), into an array of Data1D objects
311        """
[535e181]312        frames = []
313        increment = 1
314        single_file = True
[05595c4]315        n_frames = iqdata.shape[0]
[535e181]316        # Standard file has 3 frames: SAS, calibration and WAS
[05595c4]317        if n_frames > 3:
[ba65aff]318            # File has multiple frames - ask the user which ones they want to
319            # export
[05595c4]320            params = self.ask_frame_range(n_frames)
[535e181]321            frames = params['frames']
322            increment = params['inc']
323            single_file = params['file']
324            if frames == []: return
325        else: # Only interested in SAS data
326            frames = [0]
327
[f2b3f28]328        output_path = self.output.GetPath()
[edf0e06]329        metadata = self.get_metadata()
[f2b3f28]330
[9e9f848]331        frame_data = {}
[eb8da5f]332        for i in frames:
333            data = Data1D(x=qdata, y=iqdata[i])
[9e9f848]334            frame_data[i] = data
[94f4518]335        if single_file:
336            # Only need to set metadata on first Data1D object
[9e9f848]337            frame_data = frame_data.values() # Don't need to know frame numbers
[94f4518]338            frame_data[0].filename = output_path.split('\\')[-1]
339            for key, value in metadata.iteritems():
340                setattr(frame_data[0], key, value)
341        else:
342            # Need to set metadata for all Data1D objects
[9e9f848]343            for datainfo in frame_data.values():
[94f4518]344                datainfo.filename = output_path.split('\\')[-1]
345                for key, value in metadata.iteritems():
346                    setattr(datainfo, key, value)
347
[bec6cb8]348        _, ext = os.path.splitext(output_path)
349        if ext == '.xml':
350            self.convert_to_cansas(frame_data, output_path, single_file)
351        else: # ext == '.h5'
352            w = NXcanSASWriter()
353            w.write(frame_data, output_path)
[edf0e06]354
[eacc0b0]355    def convert_2d_data(self, dataset):
356        metadata = self.get_metadata()
357        for key, value in metadata.iteritems():
358            setattr(dataset[0], key, value)
359
360        w = NXcanSASWriter()
361        w.write(dataset, self.output.GetPath())
362
[edf0e06]363    def on_convert(self, event):
364        """Called when the Convert button is clicked"""
365        if not self.validate_inputs():
366            return
367
368        self.sample.ID = self.title
369
370        try:
371            if self.data_type == 'ascii':
372                qdata = self.extract_ascii_data(self.q_input.GetPath())
373                iqdata = np.array([self.extract_ascii_data(self.iq_input.GetPath())])
374                self.convert_1d_data(qdata, iqdata)
375            elif self.data_type == 'otoko':
376                qdata, iqdata = self.extract_otoko_data(self.q_input.GetPath())
377                self.convert_1d_data(qdata, iqdata)
[eacc0b0]378            elif self.data_type == 'ascii2d':
379                loader = ASCII2DLoader(self.iq_input.GetPath())
380                data = loader.load()
381                dataset = [data] # ASCII 2D only ever contains 1 frame
382                self.convert_2d_data(dataset)
[edf0e06]383            else: # self.data_type == 'bsl'
384                dataset = self.extract_bsl_data(self.iq_input.GetPath())
385                if dataset is None:
386                    # Cancelled by user
387                    return
[eacc0b0]388                self.convert_2d_data(dataset)
[edf0e06]389
390        except Exception as ex:
391            msg = str(ex)
392            wx.PostEvent(self.parent.manager.parent,
393                StatusEvent(status=msg, info='error'))
394            return
395
[a58706d]396        wx.PostEvent(self.parent.manager.parent,
397            StatusEvent(status="Conversion completed."))
398
[11794f2]399    def on_help(self, event):
[ba65aff]400        """
401        Show the File Converter documentation
402        """
[11794f2]403        tree_location = ("user/sasgui/perspectives/file_converter/"
404            "file_converter_help.html")
405        doc_viewer = DocumentationWindow(self, -1, tree_location,
406            "", "File Converter Help")
407
[fdbea3c]408    def validate_inputs(self):
409        msg = "You must select a"
[eacc0b0]410        if self.q_input.GetPath() == '' and self.data_type != 'bsl' \
411            and self.data_type != 'ascii2d':
[fdbea3c]412            msg += " Q Axis input file."
413        elif self.iq_input.GetPath() == '':
414            msg += "n Intensity input file."
415        elif self.output.GetPath() == '':
[35488b2]416            msg += " destination for the converted file."
[fdbea3c]417        if msg != "You must select a":
418            wx.PostEvent(self.parent.manager.parent,
419                StatusEvent(status=msg, info='error'))
420            return
421
422        return True
423
[af84162]424    def show_detector_window(self, event):
[ba65aff]425        """
[b7c21a7]426        Show the window for inputting Detector metadata
[ba65aff]427        """
[de0df2c]428        if self.meta_frames != []:
429            for frame in self.meta_frames:
430                frame.panel.on_close()
431        detector_frame = MetadataWindow(DetectorPanel,
432            parent=self.parent.manager.parent, manager=self,
[503cc34]433            metadata=self.detector, title='Detector Metadata')
[de0df2c]434        self.meta_frames.append(detector_frame)
435        self.parent.manager.put_icon(detector_frame)
436        detector_frame.Show(True)
[fdbea3c]437
[2a7722b]438    def show_sample_window(self, event):
[ba65aff]439        """
[b7c21a7]440        Show the window for inputting Sample metadata
[ba65aff]441        """
[2a7722b]442        if self.meta_frames != []:
443            for frame in self.meta_frames:
444                frame.panel.on_close()
445        sample_frame = MetadataWindow(SamplePanel,
446            parent=self.parent.manager.parent, manager=self,
[503cc34]447            metadata=self.sample, title='Sample Metadata')
[2a7722b]448        self.meta_frames.append(sample_frame)
449        self.parent.manager.put_icon(sample_frame)
450        sample_frame.Show(True)
451
[55bc56bc]452    def show_source_window(self, event):
[ba65aff]453        """
[b7c21a7]454        Show the window for inputting Source metadata
[ba65aff]455        """
[55bc56bc]456        if self.meta_frames != []:
457            for frame in self.meta_frames:
458                frame.panel.on_close()
459        source_frame = MetadataWindow(SourcePanel,
460            parent=self.parent.manager.parent, manager=self,
[503cc34]461            metadata=self.source, title="Source Metadata")
[55bc56bc]462        self.meta_frames.append(source_frame)
463        self.parent.manager.put_icon(source_frame)
464        source_frame.Show(True)
465
[a027549]466    def on_collapsible_pane(self, event):
[ba65aff]467        """
468        Resize the scrollable area to fit the metadata pane when it's
469        collapsed or expanded
470        """
[a027549]471        self.Freeze()
472        self.SetupScrolling()
473        self.parent.Layout()
474        self.Thaw()
475
[4b862c4]476    def datatype_changed(self, event):
[ba65aff]477        """
478        Update the UI and self.data_type when a data type radio button is
479        pressed
480        """
[4b862c4]481        event.Skip()
482        dtype = event.GetEventObject().GetName()
483        self.data_type = dtype
[eacc0b0]484        if dtype == 'bsl' or dtype == 'ascii2d':
[05595c4]485            self.q_input.SetPath("")
[535e181]486            self.q_input.Disable()
[e9e777c]487            self.output.SetWildcard("NXcanSAS HDF5 File (*.h5)|*.h5")
[535e181]488        else:
489            self.q_input.Enable()
[d6bf064]490            self.radiation_input.Enable()
491            self.metadata_section.Enable()
[bec6cb8]492            self.output.SetWildcard("CanSAS 1D (*.xml)|*.xml|NXcanSAS HDF5 File (*.h5)|*.h5")
[4b862c4]493
[55775e8]494    def radiationtype_changed(self, event):
495        event.Skip()
496        rtype = event.GetEventObject().GetValue().lower()
[503cc34]497        self.source.radiation = rtype
[55775e8]498
[f2b3f28]499    def metadata_changed(self, event):
500        event.Skip()
501        textbox = event.GetEventObject()
502        attr = textbox.GetName()
503        value = textbox.GetValue().strip()
[fdbea3c]504
[503cc34]505        setattr(self, attr, value)
[f2b3f28]506
507
[77d92cd]508    def _do_layout(self):
[f2b3f28]509        vbox = wx.BoxSizer(wx.VERTICAL)
[a58706d]510
[ed9f872]511        instructions = (
[0267d56]512        "If converting a 1D dataset, select linked single-column ASCII files "
513        "containing the Q-axis and intensity-axis data, or a 1D BSL/OTOKO file."
514        " If converting 2D data, select an ASCII file in the ISIS 2D file "
515        "format, or a 2D BSL/OTOKO file. Choose where to save the converted "
516        "file and click convert.\n"
517        "One dimensional ASCII and BSL/OTOKO files can be converted to CanSAS "
518        "(XML) or NXcanSAS (HDF5) formats. Two dimensional datasets can only be"
519        " converted to the NXcanSAS format.\n"
[19296dc]520        "Metadata can also be optionally added to the output file."
[ed9f872]521        )
[f5c52b0]522
[5afe77f]523        instruction_label = wx.StaticText(self, -1, instructions,
524            size=(_STATICBOX_WIDTH+40, -1))
525        instruction_label.Wrap(_STATICBOX_WIDTH+40)
[f2b3f28]526        vbox.Add(instruction_label, flag=wx.TOP | wx.LEFT | wx.RIGHT, border=5)
[77d92cd]527
[a58706d]528        section = wx.StaticBox(self, -1)
529        section_sizer = wx.StaticBoxSizer(section, wx.VERTICAL)
530        section_sizer.SetMinSize((_STATICBOX_WIDTH, -1))
[77d92cd]531
532        input_grid = wx.GridBagSizer(5, 5)
533
[4b862c4]534        y = 0
535
[489bb46]536        data_type_label = wx.StaticText(self, -1, "Input Format: ")
537        input_grid.Add(data_type_label, (y,0), (1,1),
538            wx.ALIGN_CENTER_VERTICAL, 5)
539        radio_sizer = wx.BoxSizer(wx.HORIZONTAL)
[eacc0b0]540        ascii_btn = wx.RadioButton(self, -1, "ASCII 1D", name="ascii",
[489bb46]541            style=wx.RB_GROUP)
542        ascii_btn.Bind(wx.EVT_RADIOBUTTON, self.datatype_changed)
543        radio_sizer.Add(ascii_btn)
[eacc0b0]544        ascii2d_btn = wx.RadioButton(self, -1, "ASCII 2D", name="ascii2d")
545        ascii2d_btn.Bind(wx.EVT_RADIOBUTTON, self.datatype_changed)
546        radio_sizer.Add(ascii2d_btn)
[ed9f872]547        otoko_btn = wx.RadioButton(self, -1, "BSL 1D", name="otoko")
[489bb46]548        otoko_btn.Bind(wx.EVT_RADIOBUTTON, self.datatype_changed)
549        radio_sizer.Add(otoko_btn)
550        bsl_btn = wx.RadioButton(self, -1, "BSL 2D", name="bsl")
551        bsl_btn.Bind(wx.EVT_RADIOBUTTON, self.datatype_changed)
552        radio_sizer.Add(bsl_btn)
[eacc0b0]553        input_grid.Add(radio_sizer, (y,1), (1,1), wx.ALL, 5)
[489bb46]554        y += 1
555
[13b9a63]556        q_label = wx.StaticText(self, -1, "Q-Axis Data: ")
[4b862c4]557        input_grid.Add(q_label, (y,0), (1,1), wx.ALIGN_CENTER_VERTICAL, 5)
[a58706d]558
559        self.q_input = wx.FilePickerCtrl(self, -1,
560            size=(_STATICBOX_WIDTH-80, -1),
[785fa53]561            message="Chose the Q-Axis data file.",
562            style=wx.FLP_USE_TEXTCTRL | wx.FLP_CHANGE_DIR)
[4b862c4]563        input_grid.Add(self.q_input, (y,1), (1,1), wx.ALL, 5)
564        y += 1
[a58706d]565
[eacc0b0]566        iq_label = wx.StaticText(self, -1, "Intensity Data: ")
[4b862c4]567        input_grid.Add(iq_label, (y,0), (1,1), wx.ALIGN_CENTER_VERTICAL, 5)
[a58706d]568
569        self.iq_input = wx.FilePickerCtrl(self, -1,
570            size=(_STATICBOX_WIDTH-80, -1),
[785fa53]571            message="Chose the Intensity-Axis data file.",
572            style=wx.FLP_USE_TEXTCTRL | wx.FLP_CHANGE_DIR)
[4b862c4]573        input_grid.Add(self.iq_input, (y,1), (1,1), wx.ALL, 5)
574        y += 1
575
[55775e8]576        radiation_label = wx.StaticText(self, -1, "Radiation Type: ")
[489bb46]577        input_grid.Add(radiation_label, (y,0), (1,1), wx.ALIGN_CENTER_VERTICAL, 5)
[d6bf064]578        self.radiation_input = wx.ComboBox(self, -1,
[55775e8]579            choices=["Neutron", "X-Ray", "Muon", "Electron"],
580            name="radiation", style=wx.CB_READONLY, value="Neutron")
[d6bf064]581        self.radiation_input.Bind(wx.EVT_COMBOBOX, self.radiationtype_changed)
582        input_grid.Add(self.radiation_input, (y,1), (1,1), wx.ALL, 5)
[55775e8]583        y += 1
584
[a58706d]585        output_label = wx.StaticText(self, -1, "Output File: ")
[4b862c4]586        input_grid.Add(output_label, (y,0), (1,1), wx.ALIGN_CENTER_VERTICAL, 5)
[a58706d]587
[0e11ec7]588        self.output = FileInput(self,
[bec6cb8]589            wildcard="CanSAS 1D (*.xml)|*.xml|NXcanSAS HDF5 File (*.h5)|*.h5")
[0e11ec7]590        input_grid.Add(self.output.GetCtrl(), (y,1), (1,1), wx.EXPAND | wx.ALL, 5)
[4b862c4]591        y += 1
[a58706d]592
[35488b2]593        self.convert_btn = wx.Button(self, wx.ID_OK, "Stop Converstion")
594        self.convert_btn.SetLabel("Convert")
595        input_grid.Add(self.convert_btn, (y,0), (1,1), wx.ALL, 5)
596        self.convert_btn.Bind(wx.EVT_BUTTON, self.on_convert)
[77d92cd]597
[11794f2]598        help_btn = wx.Button(self, -1, "HELP")
599        input_grid.Add(help_btn, (y,1), (1,1), wx.ALL, 5)
600        help_btn.Bind(wx.EVT_BUTTON, self.on_help)
601
[a58706d]602        section_sizer.Add(input_grid)
[77d92cd]603
[f2b3f28]604        vbox.Add(section_sizer, flag=wx.ALL, border=5)
605
[d6bf064]606        self.metadata_section = wx.CollapsiblePane(self, -1, "Metadata",
[fdbea3c]607            size=(_STATICBOX_WIDTH+40, -1), style=wx.WS_EX_VALIDATE_RECURSIVELY)
[d6bf064]608        metadata_pane = self.metadata_section.GetPane()
[f2b3f28]609        metadata_grid = wx.GridBagSizer(5, 5)
610
[d6bf064]611        self.metadata_section.Bind(wx.EVT_COLLAPSIBLEPANE_CHANGED,
[a027549]612            self.on_collapsible_pane)
613
[f2b3f28]614        y = 0
[503cc34]615        for item in self.properties:
[7c8ddb83]616            # Capitalise each word
617            label_txt = " ".join(
618                [s.capitalize() for s in item.replace('_', ' ').split(' ')])
619            if item == 'run':
620                label_txt = "Run Number"
[fdbea3c]621            label = wx.StaticText(metadata_pane, -1, label_txt,
[f2b3f28]622                style=wx.ALIGN_CENTER_VERTICAL)
[fdbea3c]623            input_box = wx.TextCtrl(metadata_pane, name=item,
[f2b3f28]624                size=(_STATICBOX_WIDTH-80, -1))
625            input_box.Bind(wx.EVT_TEXT, self.metadata_changed)
626            metadata_grid.Add(label, (y,0), (1,1),
627                wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
[fdbea3c]628            metadata_grid.Add(input_box, (y,1), (1,2), wx.EXPAND)
[f2b3f28]629            y += 1
630
[fdbea3c]631        detector_label = wx.StaticText(metadata_pane, -1,
632            "Detector:")
633        metadata_grid.Add(detector_label, (y, 0), (1,1), wx.ALL | wx.EXPAND, 5)
[af84162]634        detector_btn = wx.Button(metadata_pane, -1, "Enter Detector Metadata")
635        metadata_grid.Add(detector_btn, (y, 1), (1,1), wx.ALL | wx.EXPAND, 5)
636        detector_btn.Bind(wx.EVT_BUTTON, self.show_detector_window)
[fdbea3c]637        y += 1
638
[2a7722b]639        sample_label = wx.StaticText(metadata_pane, -1, "Sample: ")
640        metadata_grid.Add(sample_label, (y,0), (1,1), wx.ALL | wx.EXPAND, 5)
641        sample_btn = wx.Button(metadata_pane, -1, "Enter Sample Metadata")
642        metadata_grid.Add(sample_btn, (y,1), (1,1), wx.ALL | wx.EXPAND, 5)
643        sample_btn.Bind(wx.EVT_BUTTON, self.show_sample_window)
644        y += 1
645
[55bc56bc]646        source_label = wx.StaticText(metadata_pane, -1, "Source: ")
647        metadata_grid.Add(source_label, (y,0), (1,1), wx.ALL | wx.EXPAND, 5)
648        source_btn = wx.Button(metadata_pane, -1, "Enter Source Metadata")
649        source_btn.Bind(wx.EVT_BUTTON, self.show_source_window)
650        metadata_grid.Add(source_btn, (y,1), (1,1), wx.ALL | wx.EXPAND, 5)
651        y += 1
652
[fdbea3c]653        metadata_pane.SetSizer(metadata_grid)
[f2b3f28]654
[d6bf064]655        vbox.Add(self.metadata_section, proportion=0, flag=wx.ALL, border=5)
[77d92cd]656
657        vbox.Fit(self)
658        self.SetSizer(vbox)
659
660class ConverterWindow(widget.CHILD_FRAME):
[ba65aff]661    """Displays ConverterPanel"""
[77d92cd]662
663    def __init__(self, parent=None, title='File Converter', base=None,
[0267d56]664        manager=None, size=(PANEL_SIZE * 0.96, PANEL_SIZE * 0.9),
[77d92cd]665        *args, **kwargs):
666        kwargs['title'] = title
667        kwargs['size'] = size
668        widget.CHILD_FRAME.__init__(self, parent, *args, **kwargs)
669
670        self.manager = manager
671        self.panel = ConverterPanel(self, base=None)
672        self.Bind(wx.EVT_CLOSE, self.on_close)
673        self.SetPosition((wx.LEFT, PANEL_TOP))
674        self.Show(True)
675
676    def on_close(self, event):
677        if self.manager is not None:
678            self.manager.converter_frame = None
679        self.Destroy()
Note: See TracBrowser for help on using the repository browser.