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

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

Add docstrings & comments, and rename old bsl_loader to otoko_loader

  • Property mode set to 100644
File size: 23.3 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
[c9a519f]12from sas.sasgui.perspectives.file_converter.converter_widgets import VectorInput
[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
[535e181]21from sas.sascalc.dataloader.data_info import Data2D
[fdbea3c]22from sas.sasgui.guiframe.utils import check_float
[8976865]23from sas.sasgui.perspectives.file_converter.cansas_writer import CansasWriter
[05595c4]24from sas.sascalc.dataloader.readers.red2d_reader import Reader as Red2DWriter
[ba65aff]25from sas.sasgui.perspectives.file_converter.otoko_loader import OTOKOLoader
[535e181]26from sas.sascalc.file_converter.bsl_loader import BSLLoader
[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
66        self.metadata_section = None
[ba65aff]67
[4b862c4]68        self.data_type = "ascii"
[a58706d]69
[ba65aff]70        # Metadata values
[503cc34]71        self.title = None
72        self.run = None
73        self.run_name = None
74        self.instrument = None
75        self.detector = Detector()
76        self.sample = Sample()
77        self.source = Source()
78        self.properties = ['title', 'run', 'run_name', 'instrument']
79
80        self.detector.name = ''
81        self.source.radiation = 'neutron'
[4b862c4]82
[a58706d]83        self._do_layout()
[77d92cd]84        self.SetAutoLayout(True)
85        self.Layout()
86
[94f4518]87    def convert_to_cansas(self, frame_data, filename, single_file):
[ba65aff]88        """
89        Saves an array of Data1D objects to a single CanSAS file with multiple
90        <SasData> elements, or to multiple CanSAS files, each with one
91        <SasData> element.
92
93        :param frame_data: The Data1D object to save
94        :param filename: Where to save the CanSAS file
95        :param single_file: If true, array is saved as a single file, if false,
96        each item in the array is saved to it's own file
97        """
98        writer = CansasWriter()
[ea2a7348]99        entry_attrs = None
100        if self.run_name is not None:
101            entry_attrs = { 'name': self.run_name }
[94f4518]102        if single_file:
[ba65aff]103            writer.write(filename, frame_data,
[94f4518]104                sasentry_attrs=entry_attrs)
105        else:
106            # strip extension from filename
107            ext = "." + filename.split('.')[-1]
108            name = filename.replace(ext, '')
109            for i in range(len(frame_data)):
[ba65aff]110                # TODO: Change i to actual frame number, not consecutive numbers
111                # (maybe use info in params from frame select dialog, ie
112                # increment. Alternatively, change frame_data to a dictionary
113                # with the frame number as key and use frame_data.iteritems()
114                # (would need to update CansasWriter to deal with this)
115                f_name = name + str(i) + ext
116                writer.write(f_name, [frame_data[i]],
[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        """
[a58706d]126        data = np.loadtxt(filename, dtype=str)
127
[ff790b3]128        if len(data.shape) != 1:
129            msg = "Error reading {}: Only one column of data is allowed"
130            raise Exception(msg.format(filename.split('\\')[-1]))
131
[a58706d]132        is_float = True
133        try:
134            float(data[0])
135        except:
136            is_float = False
137
138        if not is_float:
139            end_char = data[0][-1]
140            # If lines end with comma or semi-colon, trim the last character
141            if end_char == ',' or end_char == ';':
142                data = map(lambda s: s[0:-1], data)
143            else:
144                msg = ("Error reading {}: Lines must end with a digit, comma "
145                    "or semi-colon").format(filename.split('\\')[-1])
146                raise Exception(msg)
147
148        return np.array(data, dtype=np.float32)
149
[535e181]150    def extract_otoko_data(self, filename):
[ba65aff]151        """
152        Extracts data from a 1D OTOKO file
153
154        :param filename: The OTOKO file to load the data from
155        :return: A numpy array containing the extracted data
156        """
[535e181]157        loader = OTOKOLoader(self.q_input.GetPath(),
158            self.iq_input.GetPath())
159        bsl_data = loader.load_bsl_data()
160        qdata = bsl_data.q_axis.data
161        iqdata = bsl_data.data_axis.data
162        if len(qdata) > 1:
163            msg = ("Q-Axis file has multiple frames. Only 1 frame is "
164                "allowed for the Q-Axis")
165            wx.PostEvent(self.parent.manager.parent,
166                StatusEvent(status=msg, info="error"))
167            return
168        else:
169            qdata = qdata[0]
170
171        return qdata, iqdata
172
[eb8da5f]173    def ask_frame_range(self, n_frames):
[ba65aff]174        """
175        Display a dialog asking the user to input the range of frames they
176        would like to export
177
178        :param n_frames: How many frames the loaded data file has
179        :return: A dictionary containing the parameters input by the user
180        """
[eb8da5f]181        valid_input = False
[05595c4]182        is_bsl = (self.data_type == 'bsl')
183        dlg = FrameSelectDialog(n_frames, is_bsl)
[eb8da5f]184        frames = None
185        increment = None
[94f4518]186        single_file = True
[eb8da5f]187        while not valid_input:
188            if dlg.ShowModal() == wx.ID_OK:
189                msg = ""
190                try:
191                    first_frame = int(dlg.first_input.GetValue())
192                    last_frame = int(dlg.last_input.GetValue())
193                    increment = int(dlg.increment_input.GetValue())
[05595c4]194                    if not is_bsl:
195                        single_file = dlg.single_btn.GetValue()
196
[eb8da5f]197                    if last_frame < 0 or first_frame < 0:
198                        msg = "Frame values must be positive"
199                    elif increment < 1:
200                        msg = "Increment must be greater than or equal to 1"
201                    elif first_frame > last_frame:
202                        msg = "First frame must be less than last frame"
[05595c4]203                    elif last_frame >= n_frames:
[eb8da5f]204                        msg = "Last frame must be less than {}".format(n_frames)
205                    else:
206                        valid_input = True
207                except:
208                    valid_input = False
209                    msg = "Please enter valid integer values"
210
211                if not valid_input:
212                    wx.PostEvent(self.parent.manager.parent,
213                        StatusEvent(status=msg))
214            else:
[94f4518]215                return { 'frames': [], 'inc': None, 'file': single_file }
[eb8da5f]216        frames = range(first_frame, last_frame + increment,
217            increment)
[94f4518]218        return { 'frames': frames, 'inc': increment, 'file': single_file }
[eb8da5f]219
[a58706d]220    def on_convert(self, event):
[ba65aff]221        """Called when the Convert button is clicked"""
[fdbea3c]222        if not self.validate_inputs():
223            return
224
[503cc34]225        self.sample.ID = self.title
[3ea9371]226
[a58706d]227        try:
[4b862c4]228            if self.data_type == 'ascii':
[535e181]229                qdata = self.extract_ascii_data(self.q_input.GetPath())
230                iqdata = np.array([self.extract_ascii_data(self.iq_input.GetPath())])
231            elif self.data_type == 'otoko':
232                qdata, iqdata = self.extract_otoko_data(self.q_input.GetPath())
[4b862c4]233            else: # self.data_type == 'bsl'
[ba65aff]234                # TODO: Refactor this into an extract_bsl_data method
[535e181]235                loader = BSLLoader(self.iq_input.GetPath())
236                frames = [0]
237                if loader.n_frames > 1:
238                    params = self.ask_frame_range(loader.n_frames)
[94f4518]239                    frames = params['frames']
[05595c4]240                data = {}
241
[535e181]242                for frame in frames:
243                    loader.frame = frame
[05595c4]244                    data[frame] = loader.load_data()
245
246                # TODO: Tidy this up
247                # Prepare axes values (arbitrary scale)
[535e181]248                data_x = []
249                data_y = range(loader.n_pixels) * loader.n_rasters
250                for i in range(loader.n_rasters):
251                    data_x += [i] * loader.n_pixels
[05595c4]252
253                file_path = self.output.GetPath()
254                filename = os.path.split(file_path)[-1]
255                file_path = os.path.split(file_path)[0]
256                for i, frame in data.iteritems():
257                    # If more than 1 frame is being exported, append the frame
258                    # number to the filename
259                    if len(data) > 1:
260                        frame_filename = filename.split('.')
261                        frame_filename[0] += str(i+1)
262                        frame_filename = '.'.join(frame_filename)
263                    else:
264                        frame_filename = filename
265
266                    data_i = frame.reshape((loader.n_pixels*loader.n_rasters,1))
267                    data_info = Data2D(data=data_i, qx_data=data_x, qy_data=data_y)
268                    writer = Red2DWriter()
269                    writer.write(os.path.join(file_path, frame_filename), data_info)
270
[535e181]271                wx.PostEvent(self.parent.manager.parent,
272                    StatusEvent(status="Conversion completed."))
273                return
274
[a58706d]275        except Exception as ex:
276            msg = str(ex)
277            wx.PostEvent(self.parent.manager.parent,
278                StatusEvent(status=msg, info='error'))
279            return
280
[535e181]281        frames = []
282        increment = 1
283        single_file = True
[05595c4]284        n_frames = iqdata.shape[0]
[535e181]285        # Standard file has 3 frames: SAS, calibration and WAS
[05595c4]286        if n_frames > 3:
[ba65aff]287            # File has multiple frames - ask the user which ones they want to
288            # export
[05595c4]289            params = self.ask_frame_range(n_frames)
[535e181]290            frames = params['frames']
291            increment = params['inc']
292            single_file = params['file']
293            if frames == []: return
294        else: # Only interested in SAS data
295            frames = [0]
296
[f2b3f28]297        output_path = self.output.GetPath()
298
[ba65aff]299        # Prepare the metadata for writing to a file
[d112c30]300        if self.run is None:
[503cc34]301            self.run = []
[d112c30]302        elif not isinstance(self.run, list):
303            self.run = [self.run]
[503cc34]304
[ea2a7348]305        if self.title is None:
306            self.title = ''
307
[503cc34]308        metadata = {
309            'title': self.title,
310            'run': self.run,
311            'intrument': self.instrument,
312            'detector': [self.detector],
313            'sample': self.sample,
314            'source': self.source
315        }
[f2b3f28]316
[eb8da5f]317        frame_data = []
318        for i in frames:
319            data = Data1D(x=qdata, y=iqdata[i])
320            frame_data.append(data)
[94f4518]321        if single_file:
322            # Only need to set metadata on first Data1D object
323            frame_data[0].filename = output_path.split('\\')[-1]
324            for key, value in metadata.iteritems():
325                setattr(frame_data[0], key, value)
326        else:
327            # Need to set metadata for all Data1D objects
328            for datainfo in frame_data:
329                datainfo.filename = output_path.split('\\')[-1]
330                for key, value in metadata.iteritems():
331                    setattr(datainfo, key, value)
332
333
334        self.convert_to_cansas(frame_data, output_path, single_file)
[a58706d]335        wx.PostEvent(self.parent.manager.parent,
336            StatusEvent(status="Conversion completed."))
337
[11794f2]338    def on_help(self, event):
[ba65aff]339        """
340        Show the File Converter documentation
341        """
[11794f2]342        tree_location = ("user/sasgui/perspectives/file_converter/"
343            "file_converter_help.html")
344        doc_viewer = DocumentationWindow(self, -1, tree_location,
345            "", "File Converter Help")
346
[fdbea3c]347    def validate_inputs(self):
348        msg = "You must select a"
[535e181]349        if self.q_input.GetPath() == '' and self.data_type != 'bsl':
[fdbea3c]350            msg += " Q Axis input file."
351        elif self.iq_input.GetPath() == '':
352            msg += "n Intensity input file."
353        elif self.output.GetPath() == '':
354            msg += "destination for the converted file."
355        if msg != "You must select a":
356            wx.PostEvent(self.parent.manager.parent,
357                StatusEvent(status=msg, info='error'))
358            return
359
360        return True
361
[af84162]362    def show_detector_window(self, event):
[ba65aff]363        """
364        Show the window for inputting :class:`~sas.sascalc.dataloader.data_info.Detector~` metadata
365        """
[de0df2c]366        if self.meta_frames != []:
367            for frame in self.meta_frames:
368                frame.panel.on_close()
369        detector_frame = MetadataWindow(DetectorPanel,
370            parent=self.parent.manager.parent, manager=self,
[503cc34]371            metadata=self.detector, title='Detector Metadata')
[de0df2c]372        self.meta_frames.append(detector_frame)
373        self.parent.manager.put_icon(detector_frame)
374        detector_frame.Show(True)
[fdbea3c]375
[2a7722b]376    def show_sample_window(self, event):
[ba65aff]377        """
378        Show the window for inputting :class:`~sas.sascalc.dataloader.data_info.Sample~` metadata
379        """
[2a7722b]380        if self.meta_frames != []:
381            for frame in self.meta_frames:
382                frame.panel.on_close()
383        sample_frame = MetadataWindow(SamplePanel,
384            parent=self.parent.manager.parent, manager=self,
[503cc34]385            metadata=self.sample, title='Sample Metadata')
[2a7722b]386        self.meta_frames.append(sample_frame)
387        self.parent.manager.put_icon(sample_frame)
388        sample_frame.Show(True)
389
[55bc56bc]390    def show_source_window(self, event):
[ba65aff]391        """
392        Show the window for inputting :class:`~sas.sascalc.dataloader.data_info.Source~` metadata
393        """
[55bc56bc]394        if self.meta_frames != []:
395            for frame in self.meta_frames:
396                frame.panel.on_close()
397        source_frame = MetadataWindow(SourcePanel,
398            parent=self.parent.manager.parent, manager=self,
[503cc34]399            metadata=self.source, title="Source Metadata")
[55bc56bc]400        self.meta_frames.append(source_frame)
401        self.parent.manager.put_icon(source_frame)
402        source_frame.Show(True)
403
[a027549]404    def on_collapsible_pane(self, event):
[ba65aff]405        """
406        Resize the scrollable area to fit the metadata pane when it's
407        collapsed or expanded
408        """
[a027549]409        self.Freeze()
410        self.SetupScrolling()
411        self.parent.Layout()
412        self.Thaw()
413
[4b862c4]414    def datatype_changed(self, event):
[ba65aff]415        """
416        Update the UI and self.data_type when a data type radio button is
417        pressed
418        """
[4b862c4]419        event.Skip()
420        dtype = event.GetEventObject().GetName()
421        self.data_type = dtype
[535e181]422        if dtype == 'bsl':
[05595c4]423            self.q_input.SetPath("")
[535e181]424            self.q_input.Disable()
[d6bf064]425            self.radiation_input.Disable()
426            self.metadata_section.Collapse()
427            self.on_collapsible_pane(None)
428            self.metadata_section.Disable()
[535e181]429        else:
430            self.q_input.Enable()
[d6bf064]431            self.radiation_input.Enable()
432            self.metadata_section.Enable()
[4b862c4]433
[55775e8]434    def radiationtype_changed(self, event):
435        event.Skip()
436        rtype = event.GetEventObject().GetValue().lower()
[503cc34]437        self.source.radiation = rtype
[55775e8]438
[f2b3f28]439    def metadata_changed(self, event):
440        event.Skip()
441        textbox = event.GetEventObject()
442        attr = textbox.GetName()
443        value = textbox.GetValue().strip()
[fdbea3c]444
[503cc34]445        if value == '': value = None
446
447        setattr(self, attr, value)
[f2b3f28]448
449
[77d92cd]450    def _do_layout(self):
[f2b3f28]451        vbox = wx.BoxSizer(wx.VERTICAL)
[a58706d]452
[13b9a63]453        instructions = ("Select either single column ASCII files or BSL/OTOKO"
454            " files containing the Q-Axis and Intensity-axis data, chose where"
455            " to save the converted file, then click Convert to convert them "
456            "to CanSAS XML format. If required, metadata can also be input "
457            "below.")
[5afe77f]458        instruction_label = wx.StaticText(self, -1, instructions,
459            size=(_STATICBOX_WIDTH+40, -1))
460        instruction_label.Wrap(_STATICBOX_WIDTH+40)
[f2b3f28]461        vbox.Add(instruction_label, flag=wx.TOP | wx.LEFT | wx.RIGHT, border=5)
[77d92cd]462
[a58706d]463        section = wx.StaticBox(self, -1)
464        section_sizer = wx.StaticBoxSizer(section, wx.VERTICAL)
465        section_sizer.SetMinSize((_STATICBOX_WIDTH, -1))
[77d92cd]466
467        input_grid = wx.GridBagSizer(5, 5)
468
[4b862c4]469        y = 0
470
[489bb46]471        data_type_label = wx.StaticText(self, -1, "Input Format: ")
472        input_grid.Add(data_type_label, (y,0), (1,1),
473            wx.ALIGN_CENTER_VERTICAL, 5)
474        radio_sizer = wx.BoxSizer(wx.HORIZONTAL)
475        ascii_btn = wx.RadioButton(self, -1, "ASCII", name="ascii",
476            style=wx.RB_GROUP)
477        ascii_btn.Bind(wx.EVT_RADIOBUTTON, self.datatype_changed)
478        radio_sizer.Add(ascii_btn)
479        otoko_btn = wx.RadioButton(self, -1, "OTOKO 1D", name="otoko")
480        otoko_btn.Bind(wx.EVT_RADIOBUTTON, self.datatype_changed)
481        radio_sizer.Add(otoko_btn)
482        input_grid.Add(radio_sizer, (y,1), (1,1), wx.ALL, 5)
483        bsl_btn = wx.RadioButton(self, -1, "BSL 2D", name="bsl")
484        bsl_btn.Bind(wx.EVT_RADIOBUTTON, self.datatype_changed)
485        radio_sizer.Add(bsl_btn)
486        y += 1
487
[13b9a63]488        q_label = wx.StaticText(self, -1, "Q-Axis Data: ")
[4b862c4]489        input_grid.Add(q_label, (y,0), (1,1), wx.ALIGN_CENTER_VERTICAL, 5)
[a58706d]490
491        self.q_input = wx.FilePickerCtrl(self, -1,
492            size=(_STATICBOX_WIDTH-80, -1),
[13b9a63]493            message="Chose the Q-Axis data file.")
[4b862c4]494        input_grid.Add(self.q_input, (y,1), (1,1), wx.ALL, 5)
495        y += 1
[a58706d]496
[13b9a63]497        iq_label = wx.StaticText(self, -1, "Intensity-Axis Data: ")
[4b862c4]498        input_grid.Add(iq_label, (y,0), (1,1), wx.ALIGN_CENTER_VERTICAL, 5)
[a58706d]499
500        self.iq_input = wx.FilePickerCtrl(self, -1,
501            size=(_STATICBOX_WIDTH-80, -1),
[13b9a63]502            message="Chose the Intensity-Axis data file.")
[4b862c4]503        input_grid.Add(self.iq_input, (y,1), (1,1), wx.ALL, 5)
504        y += 1
505
[55775e8]506        radiation_label = wx.StaticText(self, -1, "Radiation Type: ")
[489bb46]507        input_grid.Add(radiation_label, (y,0), (1,1), wx.ALIGN_CENTER_VERTICAL, 5)
[d6bf064]508        self.radiation_input = wx.ComboBox(self, -1,
[55775e8]509            choices=["Neutron", "X-Ray", "Muon", "Electron"],
510            name="radiation", style=wx.CB_READONLY, value="Neutron")
[d6bf064]511        self.radiation_input.Bind(wx.EVT_COMBOBOX, self.radiationtype_changed)
512        input_grid.Add(self.radiation_input, (y,1), (1,1), wx.ALL, 5)
[55775e8]513        y += 1
514
[a58706d]515        output_label = wx.StaticText(self, -1, "Output File: ")
[4b862c4]516        input_grid.Add(output_label, (y,0), (1,1), wx.ALIGN_CENTER_VERTICAL, 5)
[a58706d]517
518        self.output = wx.FilePickerCtrl(self, -1,
519            size=(_STATICBOX_WIDTH-80, -1),
[55775e8]520            message="Chose where to save the output file.",
[a58706d]521            style=wx.FLP_SAVE | wx.FLP_OVERWRITE_PROMPT | wx.FLP_USE_TEXTCTRL,
[05595c4]522            wildcard="CanSAS 1D (*.xml)|*.xml|Red2D (*.dat)|*.dat")
[4b862c4]523        input_grid.Add(self.output, (y,1), (1,1), wx.ALL, 5)
524        y += 1
[a58706d]525
[fdbea3c]526        convert_btn = wx.Button(self, wx.ID_OK, "Convert")
[4b862c4]527        input_grid.Add(convert_btn, (y,0), (1,1), wx.ALL, 5)
[a58706d]528        convert_btn.Bind(wx.EVT_BUTTON, self.on_convert)
[77d92cd]529
[11794f2]530        help_btn = wx.Button(self, -1, "HELP")
531        input_grid.Add(help_btn, (y,1), (1,1), wx.ALL, 5)
532        help_btn.Bind(wx.EVT_BUTTON, self.on_help)
533
[a58706d]534        section_sizer.Add(input_grid)
[77d92cd]535
[f2b3f28]536        vbox.Add(section_sizer, flag=wx.ALL, border=5)
537
[d6bf064]538        self.metadata_section = wx.CollapsiblePane(self, -1, "Metadata",
[fdbea3c]539            size=(_STATICBOX_WIDTH+40, -1), style=wx.WS_EX_VALIDATE_RECURSIVELY)
[d6bf064]540        metadata_pane = self.metadata_section.GetPane()
[f2b3f28]541        metadata_grid = wx.GridBagSizer(5, 5)
542
[d6bf064]543        self.metadata_section.Bind(wx.EVT_COLLAPSIBLEPANE_CHANGED,
[a027549]544            self.on_collapsible_pane)
545
[f2b3f28]546        y = 0
[503cc34]547        for item in self.properties:
[7c8ddb83]548            # Capitalise each word
549            label_txt = " ".join(
550                [s.capitalize() for s in item.replace('_', ' ').split(' ')])
551            if item == 'run':
552                label_txt = "Run Number"
[fdbea3c]553            label = wx.StaticText(metadata_pane, -1, label_txt,
[f2b3f28]554                style=wx.ALIGN_CENTER_VERTICAL)
[fdbea3c]555            input_box = wx.TextCtrl(metadata_pane, name=item,
[f2b3f28]556                size=(_STATICBOX_WIDTH-80, -1))
557            input_box.Bind(wx.EVT_TEXT, self.metadata_changed)
558            metadata_grid.Add(label, (y,0), (1,1),
559                wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
[fdbea3c]560            metadata_grid.Add(input_box, (y,1), (1,2), wx.EXPAND)
[f2b3f28]561            y += 1
562
[fdbea3c]563        detector_label = wx.StaticText(metadata_pane, -1,
564            "Detector:")
565        metadata_grid.Add(detector_label, (y, 0), (1,1), wx.ALL | wx.EXPAND, 5)
[af84162]566        detector_btn = wx.Button(metadata_pane, -1, "Enter Detector Metadata")
567        metadata_grid.Add(detector_btn, (y, 1), (1,1), wx.ALL | wx.EXPAND, 5)
568        detector_btn.Bind(wx.EVT_BUTTON, self.show_detector_window)
[fdbea3c]569        y += 1
570
[2a7722b]571        sample_label = wx.StaticText(metadata_pane, -1, "Sample: ")
572        metadata_grid.Add(sample_label, (y,0), (1,1), wx.ALL | wx.EXPAND, 5)
573        sample_btn = wx.Button(metadata_pane, -1, "Enter Sample Metadata")
574        metadata_grid.Add(sample_btn, (y,1), (1,1), wx.ALL | wx.EXPAND, 5)
575        sample_btn.Bind(wx.EVT_BUTTON, self.show_sample_window)
576        y += 1
577
[55bc56bc]578        source_label = wx.StaticText(metadata_pane, -1, "Source: ")
579        metadata_grid.Add(source_label, (y,0), (1,1), wx.ALL | wx.EXPAND, 5)
580        source_btn = wx.Button(metadata_pane, -1, "Enter Source Metadata")
581        source_btn.Bind(wx.EVT_BUTTON, self.show_source_window)
582        metadata_grid.Add(source_btn, (y,1), (1,1), wx.ALL | wx.EXPAND, 5)
583        y += 1
584
[fdbea3c]585        metadata_pane.SetSizer(metadata_grid)
[f2b3f28]586
[d6bf064]587        vbox.Add(self.metadata_section, proportion=0, flag=wx.ALL, border=5)
[77d92cd]588
589        vbox.Fit(self)
590        self.SetSizer(vbox)
591
592class ConverterWindow(widget.CHILD_FRAME):
[ba65aff]593    """Displays ConverterPanel"""
[77d92cd]594
595    def __init__(self, parent=None, title='File Converter', base=None,
[a027549]596        manager=None, size=(PANEL_SIZE * 1.05, PANEL_SIZE / 1.25),
[77d92cd]597        *args, **kwargs):
598        kwargs['title'] = title
599        kwargs['size'] = size
600        widget.CHILD_FRAME.__init__(self, parent, *args, **kwargs)
601
602        self.manager = manager
603        self.panel = ConverterPanel(self, base=None)
604        self.Bind(wx.EVT_CLOSE, self.on_close)
605        self.SetPosition((wx.LEFT, PANEL_TOP))
606        self.Show(True)
607
608    def on_close(self, event):
609        if self.manager is not None:
610            self.manager.converter_frame = None
611        self.Destroy()
Note: See TracBrowser for help on using the repository browser.