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

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

Refactor to make use of x_bins and y_bins

  • Property mode set to 100644
File size: 24.1 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        is_bsl = (self.data_type == 'bsl')
242        dlg = FrameSelectDialog(n_frames, is_bsl)
243        frames = None
244        increment = None
245        single_file = True
246        while not valid_input:
247            if dlg.ShowModal() == wx.ID_OK:
248                msg = ""
249                try:
250                    first_frame = int(dlg.first_input.GetValue())
251                    last_frame = int(dlg.last_input.GetValue())
252                    increment = int(dlg.increment_input.GetValue())
253                    if not is_bsl:
254                        single_file = dlg.single_btn.GetValue()
255
256                    if last_frame < 0 or first_frame < 0:
257                        msg = "Frame values must be positive"
258                    elif increment < 1:
259                        msg = "Increment must be greater than or equal to 1"
260                    elif first_frame > last_frame:
261                        msg = "First frame must be less than last frame"
262                    elif last_frame >= n_frames:
263                        msg = "Last frame must be less than {}".format(n_frames)
264                    else:
265                        valid_input = True
266                except:
267                    valid_input = False
268                    msg = "Please enter valid integer values"
269
270                if not valid_input:
271                    wx.PostEvent(self.parent.manager.parent,
272                        StatusEvent(status=msg))
273            else:
274                return { 'frames': [], 'inc': None, 'file': single_file }
275        frames = range(first_frame, last_frame + 1, increment)
276        return { 'frames': frames, 'inc': increment, 'file': single_file }
277
278    def on_convert(self, event):
279        """Called when the Convert button is clicked"""
280        if not self.validate_inputs():
281            return
282
283        self.sample.ID = self.title
284
285        try:
286            if self.data_type == 'ascii':
287                qdata = self.extract_ascii_data(self.q_input.GetPath())
288                iqdata = np.array([self.extract_ascii_data(self.iq_input.GetPath())])
289            elif self.data_type == 'otoko':
290                qdata, iqdata = self.extract_otoko_data(self.q_input.GetPath())
291            else: # self.data_type == 'bsl'
292
293                dataset = self.extract_bsl_data(self.iq_input.GetPath())
294                if dataset is None:
295                    # Cancelled by user
296                    return
297
298                w = NXcanSASWriter()
299                w.write(dataset, self.output.GetPath())
300
301                wx.PostEvent(self.parent.manager.parent,
302                    StatusEvent(status="Conversion completed."))
303                return
304
305        except Exception as ex:
306            msg = str(ex)
307            wx.PostEvent(self.parent.manager.parent,
308                StatusEvent(status=msg, info='error'))
309            return
310
311        frames = []
312        increment = 1
313        single_file = True
314        n_frames = iqdata.shape[0]
315        # Standard file has 3 frames: SAS, calibration and WAS
316        if n_frames > 3:
317            # File has multiple frames - ask the user which ones they want to
318            # export
319            params = self.ask_frame_range(n_frames)
320            frames = params['frames']
321            increment = params['inc']
322            single_file = params['file']
323            if frames == []: return
324        else: # Only interested in SAS data
325            frames = [0]
326
327        output_path = self.output.GetPath()
328
329        # Prepare the metadata for writing to a file
330        if self.run is None:
331            self.run = []
332        elif not isinstance(self.run, list):
333            self.run = [self.run]
334
335        if self.title is None:
336            self.title = ''
337
338        metadata = {
339            'title': self.title,
340            'run': self.run,
341            'instrument': self.instrument,
342            'detector': [self.detector],
343            'sample': self.sample,
344            'source': self.source
345        }
346
347        frame_data = {}
348        for i in frames:
349            data = Data1D(x=qdata, y=iqdata[i])
350            frame_data[i] = data
351        if single_file:
352            # Only need to set metadata on first Data1D object
353            frame_data = frame_data.values() # Don't need to know frame numbers
354            frame_data[0].filename = output_path.split('\\')[-1]
355            for key, value in metadata.iteritems():
356                setattr(frame_data[0], key, value)
357        else:
358            # Need to set metadata for all Data1D objects
359            for datainfo in frame_data.values():
360                datainfo.filename = output_path.split('\\')[-1]
361                for key, value in metadata.iteritems():
362                    setattr(datainfo, key, value)
363
364
365        self.convert_to_cansas(frame_data, output_path, single_file)
366        wx.PostEvent(self.parent.manager.parent,
367            StatusEvent(status="Conversion completed."))
368
369    def on_help(self, event):
370        """
371        Show the File Converter documentation
372        """
373        tree_location = ("user/sasgui/perspectives/file_converter/"
374            "file_converter_help.html")
375        doc_viewer = DocumentationWindow(self, -1, tree_location,
376            "", "File Converter Help")
377
378    def validate_inputs(self):
379        msg = "You must select a"
380        if self.q_input.GetPath() == '' and self.data_type != 'bsl':
381            msg += " Q Axis input file."
382        elif self.iq_input.GetPath() == '':
383            msg += "n Intensity input file."
384        elif self.output.GetPath() == '':
385            msg += " destination for the converted file."
386        if msg != "You must select a":
387            wx.PostEvent(self.parent.manager.parent,
388                StatusEvent(status=msg, info='error'))
389            return
390
391        return True
392
393    def show_detector_window(self, event):
394        """
395        Show the window for inputting Detector metadata
396        """
397        if self.meta_frames != []:
398            for frame in self.meta_frames:
399                frame.panel.on_close()
400        detector_frame = MetadataWindow(DetectorPanel,
401            parent=self.parent.manager.parent, manager=self,
402            metadata=self.detector, title='Detector Metadata')
403        self.meta_frames.append(detector_frame)
404        self.parent.manager.put_icon(detector_frame)
405        detector_frame.Show(True)
406
407    def show_sample_window(self, event):
408        """
409        Show the window for inputting Sample metadata
410        """
411        if self.meta_frames != []:
412            for frame in self.meta_frames:
413                frame.panel.on_close()
414        sample_frame = MetadataWindow(SamplePanel,
415            parent=self.parent.manager.parent, manager=self,
416            metadata=self.sample, title='Sample Metadata')
417        self.meta_frames.append(sample_frame)
418        self.parent.manager.put_icon(sample_frame)
419        sample_frame.Show(True)
420
421    def show_source_window(self, event):
422        """
423        Show the window for inputting Source metadata
424        """
425        if self.meta_frames != []:
426            for frame in self.meta_frames:
427                frame.panel.on_close()
428        source_frame = MetadataWindow(SourcePanel,
429            parent=self.parent.manager.parent, manager=self,
430            metadata=self.source, title="Source Metadata")
431        self.meta_frames.append(source_frame)
432        self.parent.manager.put_icon(source_frame)
433        source_frame.Show(True)
434
435    def on_collapsible_pane(self, event):
436        """
437        Resize the scrollable area to fit the metadata pane when it's
438        collapsed or expanded
439        """
440        self.Freeze()
441        self.SetupScrolling()
442        self.parent.Layout()
443        self.Thaw()
444
445    def datatype_changed(self, event):
446        """
447        Update the UI and self.data_type when a data type radio button is
448        pressed
449        """
450        event.Skip()
451        dtype = event.GetEventObject().GetName()
452        self.data_type = dtype
453        if dtype == 'bsl':
454            self.q_input.SetPath("")
455            self.q_input.Disable()
456            self.radiation_input.Disable()
457            self.metadata_section.Collapse()
458            self.on_collapsible_pane(None)
459            self.metadata_section.Disable()
460            self.output.SetWildcard("NXcanSAS HDF5 File (*.h5)|*.h5")
461        else:
462            self.q_input.Enable()
463            self.radiation_input.Enable()
464            self.metadata_section.Enable()
465            self.output.SetWildcard("CanSAS 1D (*.xml)|*.xml")
466
467    def radiationtype_changed(self, event):
468        event.Skip()
469        rtype = event.GetEventObject().GetValue().lower()
470        self.source.radiation = rtype
471
472    def metadata_changed(self, event):
473        event.Skip()
474        textbox = event.GetEventObject()
475        attr = textbox.GetName()
476        value = textbox.GetValue().strip()
477
478        setattr(self, attr, value)
479
480
481    def _do_layout(self):
482        vbox = wx.BoxSizer(wx.VERTICAL)
483
484        instructions = ("Select either single column ASCII files or 1D OTOKO "
485        "files containing the Q-Axis and Intensity-Axis data, or a 2D BSL file"
486        ", then chose where to save the converted file, and click Convert.\n"
487        "ASCII and OTOKO files will be converted to CanSAS XML, and OTKO files"
488        " to IGOR/DAT 2D Q_map files.\nCanSAS metadata can also be optionally "
489        "input below.")
490
491        instruction_label = wx.StaticText(self, -1, instructions,
492            size=(_STATICBOX_WIDTH+40, -1))
493        instruction_label.Wrap(_STATICBOX_WIDTH+40)
494        vbox.Add(instruction_label, flag=wx.TOP | wx.LEFT | wx.RIGHT, border=5)
495
496        section = wx.StaticBox(self, -1)
497        section_sizer = wx.StaticBoxSizer(section, wx.VERTICAL)
498        section_sizer.SetMinSize((_STATICBOX_WIDTH, -1))
499
500        input_grid = wx.GridBagSizer(5, 5)
501
502        y = 0
503
504        data_type_label = wx.StaticText(self, -1, "Input Format: ")
505        input_grid.Add(data_type_label, (y,0), (1,1),
506            wx.ALIGN_CENTER_VERTICAL, 5)
507        radio_sizer = wx.BoxSizer(wx.HORIZONTAL)
508        ascii_btn = wx.RadioButton(self, -1, "ASCII", name="ascii",
509            style=wx.RB_GROUP)
510        ascii_btn.Bind(wx.EVT_RADIOBUTTON, self.datatype_changed)
511        radio_sizer.Add(ascii_btn)
512        otoko_btn = wx.RadioButton(self, -1, "OTOKO 1D", name="otoko")
513        otoko_btn.Bind(wx.EVT_RADIOBUTTON, self.datatype_changed)
514        radio_sizer.Add(otoko_btn)
515        input_grid.Add(radio_sizer, (y,1), (1,1), wx.ALL, 5)
516        bsl_btn = wx.RadioButton(self, -1, "BSL 2D", name="bsl")
517        bsl_btn.Bind(wx.EVT_RADIOBUTTON, self.datatype_changed)
518        radio_sizer.Add(bsl_btn)
519        y += 1
520
521        q_label = wx.StaticText(self, -1, "Q-Axis Data: ")
522        input_grid.Add(q_label, (y,0), (1,1), wx.ALIGN_CENTER_VERTICAL, 5)
523
524        self.q_input = wx.FilePickerCtrl(self, -1,
525            size=(_STATICBOX_WIDTH-80, -1),
526            message="Chose the Q-Axis data file.",
527            style=wx.FLP_USE_TEXTCTRL | wx.FLP_CHANGE_DIR)
528        input_grid.Add(self.q_input, (y,1), (1,1), wx.ALL, 5)
529        y += 1
530
531        iq_label = wx.StaticText(self, -1, "Intensity-Axis Data: ")
532        input_grid.Add(iq_label, (y,0), (1,1), wx.ALIGN_CENTER_VERTICAL, 5)
533
534        self.iq_input = wx.FilePickerCtrl(self, -1,
535            size=(_STATICBOX_WIDTH-80, -1),
536            message="Chose the Intensity-Axis data file.",
537            style=wx.FLP_USE_TEXTCTRL | wx.FLP_CHANGE_DIR)
538        input_grid.Add(self.iq_input, (y,1), (1,1), wx.ALL, 5)
539        y += 1
540
541        radiation_label = wx.StaticText(self, -1, "Radiation Type: ")
542        input_grid.Add(radiation_label, (y,0), (1,1), wx.ALIGN_CENTER_VERTICAL, 5)
543        self.radiation_input = wx.ComboBox(self, -1,
544            choices=["Neutron", "X-Ray", "Muon", "Electron"],
545            name="radiation", style=wx.CB_READONLY, value="Neutron")
546        self.radiation_input.Bind(wx.EVT_COMBOBOX, self.radiationtype_changed)
547        input_grid.Add(self.radiation_input, (y,1), (1,1), wx.ALL, 5)
548        y += 1
549
550        output_label = wx.StaticText(self, -1, "Output File: ")
551        input_grid.Add(output_label, (y,0), (1,1), wx.ALIGN_CENTER_VERTICAL, 5)
552
553        self.output = FileInput(self,
554            wildcard="CanSAS 1D (*.xml)|*.xml")
555        input_grid.Add(self.output.GetCtrl(), (y,1), (1,1), wx.EXPAND | wx.ALL, 5)
556        y += 1
557
558        self.convert_btn = wx.Button(self, wx.ID_OK, "Stop Converstion")
559        self.convert_btn.SetLabel("Convert")
560        input_grid.Add(self.convert_btn, (y,0), (1,1), wx.ALL, 5)
561        self.convert_btn.Bind(wx.EVT_BUTTON, self.on_convert)
562
563        help_btn = wx.Button(self, -1, "HELP")
564        input_grid.Add(help_btn, (y,1), (1,1), wx.ALL, 5)
565        help_btn.Bind(wx.EVT_BUTTON, self.on_help)
566
567        section_sizer.Add(input_grid)
568
569        vbox.Add(section_sizer, flag=wx.ALL, border=5)
570
571        self.metadata_section = wx.CollapsiblePane(self, -1, "Metadata",
572            size=(_STATICBOX_WIDTH+40, -1), style=wx.WS_EX_VALIDATE_RECURSIVELY)
573        metadata_pane = self.metadata_section.GetPane()
574        metadata_grid = wx.GridBagSizer(5, 5)
575
576        self.metadata_section.Bind(wx.EVT_COLLAPSIBLEPANE_CHANGED,
577            self.on_collapsible_pane)
578
579        y = 0
580        for item in self.properties:
581            # Capitalise each word
582            label_txt = " ".join(
583                [s.capitalize() for s in item.replace('_', ' ').split(' ')])
584            if item == 'run':
585                label_txt = "Run Number"
586            label = wx.StaticText(metadata_pane, -1, label_txt,
587                style=wx.ALIGN_CENTER_VERTICAL)
588            input_box = wx.TextCtrl(metadata_pane, name=item,
589                size=(_STATICBOX_WIDTH-80, -1))
590            input_box.Bind(wx.EVT_TEXT, self.metadata_changed)
591            metadata_grid.Add(label, (y,0), (1,1),
592                wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
593            metadata_grid.Add(input_box, (y,1), (1,2), wx.EXPAND)
594            y += 1
595
596        detector_label = wx.StaticText(metadata_pane, -1,
597            "Detector:")
598        metadata_grid.Add(detector_label, (y, 0), (1,1), wx.ALL | wx.EXPAND, 5)
599        detector_btn = wx.Button(metadata_pane, -1, "Enter Detector Metadata")
600        metadata_grid.Add(detector_btn, (y, 1), (1,1), wx.ALL | wx.EXPAND, 5)
601        detector_btn.Bind(wx.EVT_BUTTON, self.show_detector_window)
602        y += 1
603
604        sample_label = wx.StaticText(metadata_pane, -1, "Sample: ")
605        metadata_grid.Add(sample_label, (y,0), (1,1), wx.ALL | wx.EXPAND, 5)
606        sample_btn = wx.Button(metadata_pane, -1, "Enter Sample Metadata")
607        metadata_grid.Add(sample_btn, (y,1), (1,1), wx.ALL | wx.EXPAND, 5)
608        sample_btn.Bind(wx.EVT_BUTTON, self.show_sample_window)
609        y += 1
610
611        source_label = wx.StaticText(metadata_pane, -1, "Source: ")
612        metadata_grid.Add(source_label, (y,0), (1,1), wx.ALL | wx.EXPAND, 5)
613        source_btn = wx.Button(metadata_pane, -1, "Enter Source Metadata")
614        source_btn.Bind(wx.EVT_BUTTON, self.show_source_window)
615        metadata_grid.Add(source_btn, (y,1), (1,1), wx.ALL | wx.EXPAND, 5)
616        y += 1
617
618        metadata_pane.SetSizer(metadata_grid)
619
620        vbox.Add(self.metadata_section, proportion=0, flag=wx.ALL, border=5)
621
622        vbox.Fit(self)
623        self.SetSizer(vbox)
624
625class ConverterWindow(widget.CHILD_FRAME):
626    """Displays ConverterPanel"""
627
628    def __init__(self, parent=None, title='File Converter', base=None,
629        manager=None, size=(PANEL_SIZE * 1.05, PANEL_SIZE / 1.1),
630        *args, **kwargs):
631        kwargs['title'] = title
632        kwargs['size'] = size
633        widget.CHILD_FRAME.__init__(self, parent, *args, **kwargs)
634
635        self.manager = manager
636        self.panel = ConverterPanel(self, base=None)
637        self.Bind(wx.EVT_CLOSE, self.on_close)
638        self.SetPosition((wx.LEFT, PANEL_TOP))
639        self.Show(True)
640
641    def on_close(self, event):
642        if self.manager is not None:
643            self.manager.converter_frame = None
644        self.Destroy()
Note: See TracBrowser for help on using the repository browser.