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, 6 years ago

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

  • Property mode set to 100644
File size: 24.8 KB
Line 
1"""
2This module provides a GUI for the file converter
3"""
4
5import wx
6import sys
7import os
8import numpy as np
9from wx.lib.scrolledpanel import ScrolledPanel
10from sas.sasgui.guiframe.panel_base import PanelBase
11from sas.sasgui.perspectives.calculator import calculator_widgets as widget
12from sas.sasgui.perspectives.file_converter.converter_widgets import FileInput
13from sas.sasgui.perspectives.file_converter.meta_panels import MetadataWindow
14from sas.sasgui.perspectives.file_converter.meta_panels import DetectorPanel
15from sas.sasgui.perspectives.file_converter.meta_panels import SamplePanel
16from sas.sasgui.perspectives.file_converter.meta_panels import SourcePanel
17from sas.sasgui.perspectives.file_converter.frame_select_dialog import FrameSelectDialog
18from sas.sasgui.guiframe.events import StatusEvent
19from sas.sasgui.guiframe.documentation_window import DocumentationWindow
20from sas.sasgui.guiframe.dataFitting import Data1D
21from sas.sascalc.dataloader.data_info import Data2D
22from sas.sasgui.guiframe.utils import check_float
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
26from sas.sascalc.file_converter.nxcansas_writer import NXcanSASWriter
27from sas.sascalc.dataloader.data_info import Detector
28from sas.sascalc.dataloader.data_info import Sample
29from sas.sascalc.dataloader.data_info import Source
30from sas.sascalc.dataloader.data_info import Vector
31
32# Panel size
33if sys.platform.count("win32") > 0:
34    PANEL_TOP = 0
35    _STATICBOX_WIDTH = 410
36    _BOX_WIDTH = 200
37    PANEL_SIZE = 480
38    FONT_VARIANT = 0
39else:
40    PANEL_TOP = 60
41    _STATICBOX_WIDTH = 430
42    _BOX_WIDTH = 200
43    PANEL_SIZE = 500
44    FONT_VARIANT = 1
45
46class ConverterPanel(ScrolledPanel, PanelBase):
47    """
48    This class provides the File Converter GUI
49    """
50
51    def __init__(self, parent, base=None, *args, **kwargs):
52        ScrolledPanel.__init__(self, parent, *args, **kwargs)
53        PanelBase.__init__(self)
54        self.SetupScrolling()
55        self.SetWindowVariant(variant=FONT_VARIANT)
56
57        self.base = base
58        self.parent = parent
59        self.meta_frames = []
60
61        # GUI inputs
62        self.q_input = None
63        self.iq_input = None
64        self.output = None
65        self.radiation_input = None
66        self.convert_btn = None
67        self.metadata_section = None
68
69        self.data_type = "ascii"
70
71        # Metadata values
72        self.title = ''
73        self.run = ''
74        self.run_name = ''
75        self.instrument = ''
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'
83
84        self._do_layout()
85        self.SetAutoLayout(True)
86        self.Layout()
87
88    def convert_to_cansas(self, frame_data, filepath, single_file):
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
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
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()
101        entry_attrs = None
102        if self.run_name != '':
103            entry_attrs = { 'name': self.run_name }
104
105        if single_file:
106            writer.write(filepath, frame_data,
107                sasentry_attrs=entry_attrs)
108        else:
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],
117                    sasentry_attrs=entry_attrs)
118
119    def extract_ascii_data(self, filename):
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        """
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]))
140
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
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
163    def extract_otoko_data(self, filename):
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        """
170        loader = OTOKOLoader(self.q_input.GetPath(),
171            self.iq_input.GetPath())
172        otoko_data = loader.load_otoko_data()
173        qdata = otoko_data.q_axis.data
174        iqdata = otoko_data.data_axis.data
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
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:
226            return None
227
228        frame_data = loader.load_frames(frames)
229
230        return frame_data
231
232    def ask_frame_range(self, n_frames):
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        """
240        valid_input = False
241        _, ext = os.path.splitext(self.output.GetPath())
242        show_single_btn = (ext == '.h5')
243        dlg = FrameSelectDialog(n_frames, show_single_btn)
244        frames = None
245        increment = None
246        single_file = True
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())
254                    if not show_single_btn:
255                        single_file = dlg.single_btn.GetValue()
256
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"
263                    elif last_frame >= n_frames:
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:
275                return { 'frames': [], 'inc': None, 'file': single_file }
276        frames = range(first_frame, last_frame + 1, increment)
277        return { 'frames': frames, 'inc': increment, 'file': single_file }
278
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]
285
286        if self.title is None:
287            self.title = ''
288
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        }
297
298        return metadata
299
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        """
305        frames = []
306        increment = 1
307        single_file = True
308        n_frames = iqdata.shape[0]
309        # Standard file has 3 frames: SAS, calibration and WAS
310        if n_frames > 3:
311            # File has multiple frames - ask the user which ones they want to
312            # export
313            params = self.ask_frame_range(n_frames)
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
321        output_path = self.output.GetPath()
322        metadata = self.get_metadata()
323
324        frame_data = {}
325        for i in frames:
326            data = Data1D(x=qdata, y=iqdata[i])
327            frame_data[i] = data
328        if single_file:
329            # Only need to set metadata on first Data1D object
330            frame_data = frame_data.values() # Don't need to know frame numbers
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
336            for datainfo in frame_data.values():
337                datainfo.filename = output_path.split('\\')[-1]
338                for key, value in metadata.iteritems():
339                    setattr(datainfo, key, value)
340
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)
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)
372                if self.run != []:
373                    run_number = self.run[0]
374                    dataset[0].run_name[run_number] = self.run_name
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
384        wx.PostEvent(self.parent.manager.parent,
385            StatusEvent(status="Conversion completed."))
386
387    def on_help(self, event):
388        """
389        Show the File Converter documentation
390        """
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
396    def validate_inputs(self):
397        msg = "You must select a"
398        if self.q_input.GetPath() == '' and self.data_type != 'bsl':
399            msg += " Q Axis input file."
400        elif self.iq_input.GetPath() == '':
401            msg += "n Intensity input file."
402        elif self.output.GetPath() == '':
403            msg += " destination for the converted file."
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
411    def show_detector_window(self, event):
412        """
413        Show the window for inputting Detector metadata
414        """
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,
420            metadata=self.detector, title='Detector Metadata')
421        self.meta_frames.append(detector_frame)
422        self.parent.manager.put_icon(detector_frame)
423        detector_frame.Show(True)
424
425    def show_sample_window(self, event):
426        """
427        Show the window for inputting Sample metadata
428        """
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,
434            metadata=self.sample, title='Sample Metadata')
435        self.meta_frames.append(sample_frame)
436        self.parent.manager.put_icon(sample_frame)
437        sample_frame.Show(True)
438
439    def show_source_window(self, event):
440        """
441        Show the window for inputting Source metadata
442        """
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,
448            metadata=self.source, title="Source Metadata")
449        self.meta_frames.append(source_frame)
450        self.parent.manager.put_icon(source_frame)
451        source_frame.Show(True)
452
453    def on_collapsible_pane(self, event):
454        """
455        Resize the scrollable area to fit the metadata pane when it's
456        collapsed or expanded
457        """
458        self.Freeze()
459        self.SetupScrolling()
460        self.parent.Layout()
461        self.Thaw()
462
463    def datatype_changed(self, event):
464        """
465        Update the UI and self.data_type when a data type radio button is
466        pressed
467        """
468        event.Skip()
469        dtype = event.GetEventObject().GetName()
470        self.data_type = dtype
471        if dtype == 'bsl':
472            self.q_input.SetPath("")
473            self.q_input.Disable()
474            self.output.SetWildcard("NXcanSAS HDF5 File (*.h5)|*.h5")
475        else:
476            self.q_input.Enable()
477            self.radiation_input.Enable()
478            self.metadata_section.Enable()
479            self.output.SetWildcard("CanSAS 1D (*.xml)|*.xml|NXcanSAS HDF5 File (*.h5)|*.h5")
480
481    def radiationtype_changed(self, event):
482        event.Skip()
483        rtype = event.GetEventObject().GetValue().lower()
484        self.source.radiation = rtype
485
486    def metadata_changed(self, event):
487        event.Skip()
488        textbox = event.GetEventObject()
489        attr = textbox.GetName()
490        value = textbox.GetValue().strip()
491
492        setattr(self, attr, value)
493
494
495    def _do_layout(self):
496        vbox = wx.BoxSizer(wx.VERTICAL)
497
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
505        instruction_label = wx.StaticText(self, -1, instructions,
506            size=(_STATICBOX_WIDTH+40, -1))
507        instruction_label.Wrap(_STATICBOX_WIDTH+40)
508        vbox.Add(instruction_label, flag=wx.TOP | wx.LEFT | wx.RIGHT, border=5)
509
510        section = wx.StaticBox(self, -1)
511        section_sizer = wx.StaticBoxSizer(section, wx.VERTICAL)
512        section_sizer.SetMinSize((_STATICBOX_WIDTH, -1))
513
514        input_grid = wx.GridBagSizer(5, 5)
515
516        y = 0
517
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
535        q_label = wx.StaticText(self, -1, "Q-Axis Data: ")
536        input_grid.Add(q_label, (y,0), (1,1), wx.ALIGN_CENTER_VERTICAL, 5)
537
538        self.q_input = wx.FilePickerCtrl(self, -1,
539            size=(_STATICBOX_WIDTH-80, -1),
540            message="Chose the Q-Axis data file.",
541            style=wx.FLP_USE_TEXTCTRL | wx.FLP_CHANGE_DIR)
542        input_grid.Add(self.q_input, (y,1), (1,1), wx.ALL, 5)
543        y += 1
544
545        iq_label = wx.StaticText(self, -1, "Intensity-Axis Data: ")
546        input_grid.Add(iq_label, (y,0), (1,1), wx.ALIGN_CENTER_VERTICAL, 5)
547
548        self.iq_input = wx.FilePickerCtrl(self, -1,
549            size=(_STATICBOX_WIDTH-80, -1),
550            message="Chose the Intensity-Axis data file.",
551            style=wx.FLP_USE_TEXTCTRL | wx.FLP_CHANGE_DIR)
552        input_grid.Add(self.iq_input, (y,1), (1,1), wx.ALL, 5)
553        y += 1
554
555        radiation_label = wx.StaticText(self, -1, "Radiation Type: ")
556        input_grid.Add(radiation_label, (y,0), (1,1), wx.ALIGN_CENTER_VERTICAL, 5)
557        self.radiation_input = wx.ComboBox(self, -1,
558            choices=["Neutron", "X-Ray", "Muon", "Electron"],
559            name="radiation", style=wx.CB_READONLY, value="Neutron")
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)
562        y += 1
563
564        output_label = wx.StaticText(self, -1, "Output File: ")
565        input_grid.Add(output_label, (y,0), (1,1), wx.ALIGN_CENTER_VERTICAL, 5)
566
567        self.output = FileInput(self,
568            wildcard="CanSAS 1D (*.xml)|*.xml|NXcanSAS HDF5 File (*.h5)|*.h5")
569        input_grid.Add(self.output.GetCtrl(), (y,1), (1,1), wx.EXPAND | wx.ALL, 5)
570        y += 1
571
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)
576
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
581        section_sizer.Add(input_grid)
582
583        vbox.Add(section_sizer, flag=wx.ALL, border=5)
584
585        self.metadata_section = wx.CollapsiblePane(self, -1, "Metadata",
586            size=(_STATICBOX_WIDTH+40, -1), style=wx.WS_EX_VALIDATE_RECURSIVELY)
587        metadata_pane = self.metadata_section.GetPane()
588        metadata_grid = wx.GridBagSizer(5, 5)
589
590        self.metadata_section.Bind(wx.EVT_COLLAPSIBLEPANE_CHANGED,
591            self.on_collapsible_pane)
592
593        y = 0
594        for item in self.properties:
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"
600            label = wx.StaticText(metadata_pane, -1, label_txt,
601                style=wx.ALIGN_CENTER_VERTICAL)
602            input_box = wx.TextCtrl(metadata_pane, name=item,
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)
607            metadata_grid.Add(input_box, (y,1), (1,2), wx.EXPAND)
608            y += 1
609
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)
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)
616        y += 1
617
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
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
632        metadata_pane.SetSizer(metadata_grid)
633
634        vbox.Add(self.metadata_section, proportion=0, flag=wx.ALL, border=5)
635
636        vbox.Fit(self)
637        self.SetSizer(vbox)
638
639class ConverterWindow(widget.CHILD_FRAME):
640    """Displays ConverterPanel"""
641
642    def __init__(self, parent=None, title='File Converter', base=None,
643        manager=None, size=(PANEL_SIZE * 1.05, PANEL_SIZE / 1.1),
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.