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

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

Refactor BSL extraction & conversion logic into their own methods

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