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

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

Fix instrument not being set

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