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

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

Allow converting 1D data to NXcanSAS as well as CanSAS xml

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