source: sasview/calculatorview/perspectives/calculator/data_editor.py @ 051e22f

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.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 051e22f was 051e22f, checked in by Gervaise Alina <gervyh@…>, 14 years ago

working on data loader

  • Property mode set to 100644
File size: 22.7 KB
Line 
1
2import wx
3import sys
4import os 
5from copy import deepcopy
6
7from DataLoader.loader import Loader
8from DataLoader.data_info import DataInfo, Detector, Collimation, Data2D
9from detector_editor import DetectorDialog
10from collimation_editor import CollimationDialog
11from console import ConsoleDialog
12
13from sans.guiframe.utils import check_float
14from sans.guicomm.events import StatusEvent
15
16_BOX_WIDTH = 76
17_QMIN_DEFAULT = 0.001
18_QMAX_DEFAULT = 0.13
19_NPTS_DEFAULT = 50
20#Control panel width
21if sys.platform.count("darwin")==0:
22    PANEL_WIDTH = 500
23    PANEL_HEIGTH = 350
24    FONT_VARIANT = 0
25    ON_MAC = False
26else:
27    PANEL_WIDTH = 550
28    PANEL_HEIGTH = 400
29    FONT_VARIANT = 1
30    ON_MAC = True
31   
32def load_error(error=None):
33    """
34        Pop up an error message.
35       
36        @param error: details error message to be displayed
37    """
38    message = "You had to try this, didn't you?\n\n"
39    message += "The data file you selected could not be loaded.\n"
40    message += "Make sure the content of your file is properly formatted.\n\n"
41   
42    if error is not None:
43        message += "When contacting the DANSE team, mention the following:\n%s" % str(error)
44   
45    dial = wx.MessageDialog(None, message, 'Error Loading File', wx.OK | wx.ICON_EXCLAMATION)
46    dial.ShowModal() 
47   
48   
49class DataEditorPanel(wx.ScrolledWindow):
50    """
51        @param data: when not empty the class can same information into a dat object
52        and post event containing the changed data object to some other frame
53    """
54    def __init__(self, parent, data=[], *args, **kwds):
55         kwds['name'] = "Data Editor"
56         kwds["size"]= (PANEL_WIDTH, PANEL_HEIGTH)
57         wx.ScrolledWindow.__init__(self, parent, *args, **kwds)
58         self.parent = parent
59         self._data = data
60         self._reset_data = deepcopy(data)
61         self.reader = None
62         self._notes = ""
63         self_description = "Edit Data"
64         self._default_save_location = os.getcwd()
65         self._do_layout()
66         self.reset_panel()
67         self.bt_apply.Disable()
68         if data:
69             self.complete_loading(data=data)
70             self.bt_apply.Enable()
71             
72    def _define_structure(self):
73        """
74            define initial sizer
75        """
76        self.main_sizer = wx.BoxSizer(wx.VERTICAL)
77        name_box = wx.StaticBox(self, -1, "Load Data")
78        self.name_sizer = wx.StaticBoxSizer(name_box, wx.HORIZONTAL)
79       
80        self.title_sizer = wx.BoxSizer(wx.HORIZONTAL)
81        self.run_sizer = wx.BoxSizer(wx.HORIZONTAL)
82        self.instrument_sizer = wx.BoxSizer(wx.HORIZONTAL)
83       
84        edit_box = wx.StaticBox(self, -1, "Edit ")
85        self.edit_sizer = wx.StaticBoxSizer(edit_box, wx.HORIZONTAL)
86       
87        self.button_sizer = wx.BoxSizer(wx.HORIZONTAL)
88     
89    def _layout_name(self):
90        """
91            Do the layout for data name related widgets
92        """
93        #data name [string]
94        data_name_txt = wx.StaticText(self, -1, 'Data : ') 
95        self.data_cbox = wx.ComboBox(self, -1, style=wx.CB_READONLY)
96        wx.EVT_COMBOBOX(self.data_cbox, -1, self.on_select_data) 
97        hint_data = "Loaded data."
98        self.data_cbox.SetToolTipString(hint_data)
99        id = wx.NewId()
100        self.browse_button = wx.Button(self, id, "Browse")
101        hint_on_browse = "Click on this button to import data in this panel."
102        self.browse_button.SetToolTipString(hint_on_browse)
103        self.Bind(wx.EVT_BUTTON, self.on_click_browse, id=id)
104        self.name_sizer.AddMany([(data_name_txt, 0, wx.LEFT, 15),
105                                       (self.data_cbox, 0, wx.LEFT, 10),
106                                       (self.browse_button, 0, wx.LEFT, 10)])
107       
108    def _layout_title(self):
109        """
110            Do the layout for data title related widgets
111        """
112        #title name [string]
113        data_title_txt = wx.StaticText(self, -1, 'Title : ') 
114        self.data_title_tcl = wx.TextCtrl(self, -1, size=(PANEL_WIDTH*3/5, -1)) 
115        self.data_title_tcl.Bind(wx.EVT_TEXT_ENTER, self.on_change_title)
116        hint_title = "Data's title."
117        self.data_title_tcl.SetToolTipString(hint_title)
118        self.title_sizer.AddMany([(data_title_txt, 0, wx.LEFT, 15),
119                                       (self.data_title_tcl, 0, wx.LEFT, 10)])
120       
121    def _layout_run(self):
122        """
123            Do the layout for data run related widgets
124        """
125        data_run_txt = wx.StaticText(self, -1, 'Run : ') 
126        data_run_txt.SetToolTipString('') 
127        self.data_run_tcl = wx.TextCtrl(self, -1, size=(PANEL_WIDTH*3/5, -1), style=wx.TE_MULTILINE)
128        hint_run = "Data's run."
129        self.data_run_tcl.SetToolTipString(hint_run)
130        self.run_sizer.AddMany([(data_run_txt, 0, wx.LEFT, 15),
131                                       (self.data_run_tcl, 0, wx.LEFT, 10)])
132       
133    def _layout_instrument(self):
134        """
135            Do the layout for instrument related widgets
136        """
137        instrument_txt = wx.StaticText(self, -1, 'Instrument : ') 
138        hint_instrument_txt = ''
139        instrument_txt.SetToolTipString(hint_instrument_txt) 
140        self.instrument_tcl = wx.TextCtrl(self, -1, size=(_BOX_WIDTH*5, 20)) 
141        hint_instrument = "Instrument."
142        self.instrument_tcl.SetToolTipString(hint_instrument)
143        self.instrument_sizer.AddMany([(instrument_txt, 0, wx.LEFT, 15),
144                                (self.instrument_tcl, 0, wx.LEFT, 10)])
145       
146    def _layout_editor(self):
147        """
148            Do the layout for sample related widgets
149        """
150        self.detector_rb = wx.RadioButton(self, -1, "Detector",
151                                           style=wx.RB_GROUP)
152        self.sample_rb = wx.RadioButton(self, -1, "Sample")
153        self.source_rb = wx.RadioButton(self, -1, "Source")
154        self.collimation_rb = wx.RadioButton(self, -1, "Collimation")
155       
156        self.bt_edit = wx.Button(self, -1, "Edit")
157        self.bt_edit.SetToolTipString("Edit data.")
158        self.bt_edit.Bind(wx.EVT_BUTTON, self.on_edit)
159        self.edit_sizer.AddMany([(self.detector_rb, 0, wx.ALL, 10),
160                        (self.sample_rb, 0, wx.RIGHT|wx.BOTTOM|wx.TOP, 10),
161                        (self.source_rb, 0, wx.RIGHT|wx.BOTTOM|wx.TOP, 10),
162                    (self.collimation_rb, 0, wx.RIGHT|wx.BOTTOM|wx.TOP, 10),
163                    (self.bt_edit, 0,
164                                  wx.RIGHT|wx.BOTTOM|wx.TOP, 10)])
165        self.reset_radiobox()
166   
167   
168    def _layout_source(self):
169        """
170            Do the layout for source related widgets
171        """
172        source_txt = wx.StaticText(self, -1, 'Source ') 
173        hint_source_txt = ''
174        source_txt.SetToolTipString(hint_source_txt) 
175        self.bt_edit_source = wx.Button(self, -1, "Edit")
176        self.bt_edit_source.SetToolTipString("Edit data's sample.")
177        self.bt_edit_source.Bind(wx.EVT_BUTTON, self.edit_source)
178        self.source_sizer.AddMany([(source_txt, 0, wx.ALL, 10),
179                                (self.bt_edit_source, 0,
180                                  wx.RIGHT|wx.BOTTOM|wx.TOP, 10)])
181
182    def _layout_summary(self):
183        """
184            Layout widgets related to data's summary
185        """
186        self.data_summary = wx.TextCtrl(self, -1, style=wx.TE_MULTILINE|wx.HSCROLL,
187                                        size=(-1, 200))
188        summary = 'No data info available...'
189        self.data_summary.SetValue(summary)
190        self.summary_sizer.Add(self.data_summary, 1, wx.EXPAND|wx.ALL, 10) 
191                   
192    def _layout_button(self): 
193        """
194            Do the layout for the button widgets
195        """ 
196        self.bt_summary = wx.Button(self, -1, "View", size=(_BOX_WIDTH*2/3,-1))
197        self.bt_summary.SetToolTipString("View final changes on data.")
198        self.bt_summary.Bind(wx.EVT_BUTTON, self.on_click_view)
199       
200        self.bt_save = wx.Button(self, -1, "Save As", size=(_BOX_WIDTH*2/3,-1))
201        self.bt_save.SetToolTipString("Save changes in a file.")
202        self.bt_save.Bind(wx.EVT_BUTTON, self.on_click_save)
203       
204        self.bt_apply = wx.Button(self, -1, "Apply", size=(_BOX_WIDTH*2/3,-1))
205        self.bt_apply.SetToolTipString("Save changes into the imported data.")
206        self.bt_apply.Bind(wx.EVT_BUTTON, self.on_click_apply)
207     
208        self.bt_reset = wx.Button(self, -1,'Reset', size=(_BOX_WIDTH*2/3,-1))
209        self.bt_reset.Bind(wx.EVT_BUTTON, self.on_click_reset)
210        self.bt_reset.SetToolTipString("Reset data to its initial state.")
211       
212        self.bt_close = wx.Button(self, -1,'Close', size=(_BOX_WIDTH*2/3,-1))
213        self.bt_close.Bind(wx.EVT_BUTTON, self.on_close)
214        self.bt_close.SetToolTipString("Close this panel.")
215       
216        self.button_sizer.AddMany([(self.bt_save, 0, wx.LEFT, 120),
217                                   (self.bt_apply, 0, wx.LEFT, 10),
218                                   (self.bt_reset, 0, wx.LEFT|wx.RIGHT, 10),
219                                   (self.bt_summary, 0, wx.RIGHT, 10),
220                                   (self.bt_close, 0, wx.RIGHT, 10)])
221       
222    def _do_layout(self, data=None):
223        """
224            Draw the current panel
225        """
226        self._define_structure()
227        self._layout_name()
228        self._layout_title()
229        self._layout_run()
230        self._layout_editor()
231        self._layout_button()
232        self.main_sizer.AddMany([(self.name_sizer, 0, wx.EXPAND|wx.ALL, 10),
233                                (self.title_sizer, 0,
234                                         wx.EXPAND|wx.TOP|wx.BOTTOM, 5),
235                                (self.run_sizer, 0,
236                                         wx.EXPAND|wx.TOP|wx.BOTTOM, 5),
237                                (self.instrument_sizer, 0,
238                                         wx.EXPAND|wx.TOP|wx.BOTTOM, 5),
239                                (self.edit_sizer, 0,
240                                        wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP,10),
241                                (self.button_sizer, 0,
242                                          wx.EXPAND|wx.TOP|wx.BOTTOM, 5)])
243        self.SetSizer(self.main_sizer)
244        self.SetScrollbars(20,20,25,65)
245        self.SetAutoLayout(True)
246       
247    def fill_data_combox(self):
248        """
249            fill the current combobox with the available data
250        """
251        if not self._data:
252            return
253        self.data_cbox.Clear()
254        for data in self._data:
255            name = data.title
256            if data.run:
257                name = data.run[0]
258            if name.lstrip().rstrip() =="":
259                name = data.filename
260            pos = self.data_cbox.Append(str(name))
261            self.data_cbox.SetClientData(pos, data)
262            self.data_cbox.SetSelection(pos)
263            self.data_cbox.SetStringSelection(str(name)) 
264       
265    def reset_panel(self):
266        """
267        """
268        self.enable_data_cbox()
269        self.data_title_tcl.SetValue("")
270        self.data_run_tcl.SetValue("")
271       
272    def on_select_data(self, event=None):
273        """
274        """
275        data, data_name, position = self.get_current_data()
276        self.reset_panel()
277        if data is None:
278            return
279        self.data_title_tcl.SetValue(str(data.title))
280        text = ""
281        if data.run:
282            for item in data.run:
283                text += item+"\n"
284        self.data_run_tcl.SetValue(str(text))
285           
286    def get_current_data(self):
287        """
288        """
289        position = self.data_cbox.GetSelection() 
290        if position == wx.NOT_FOUND:
291            return None, None, None
292        data_name = self.data_cbox.GetStringSelection() 
293        data = self.data_cbox.GetClientData(position)
294        return data, data_name, position
295   
296    def enable_data_cbox(self):
297        """
298        """
299        if self._data:
300            self.data_cbox.Enable()
301            self.bt_summary.Enable()
302            self.bt_reset.Enable()
303            self.bt_save.Enable()
304            self.bt_edit.Enable()
305        else:
306            self.data_cbox.Disable()
307            self.bt_summary.Disable()
308            self.bt_reset.Disable()
309            self.bt_save.Disable()
310            self.bt_edit.Disable()
311           
312    def reset_radiobox(self):
313        """
314        """
315        self.detector_rb.SetValue(True)
316        self.source_rb.SetValue(False)
317        self.sample_rb.SetValue(False)
318        self.collimation_rb.SetValue(False)
319   
320    def set_sample(self, sample, notes=None):
321        """
322            set sample for data
323        """
324        data, data_name, position = self.get_current_data()
325        if data is None:
326            return 
327        data.sample = sample
328        if notes is not None:
329            data.process.append(notes)
330   
331    def set_source(self, source, notes=None):
332        """
333            set source for data
334        """
335        data, data_name, position = self.get_current_data()
336        if data is None:
337            return 
338        data.source = source
339        if notes is not None:
340            data.process.append(notes)
341       
342    def set_detector(self, detector, notes=None):
343        """
344            set detector for data
345        """
346        data, data_name, position = self.get_current_data()
347        if data is None:
348            return 
349        data.detector = detector
350        if notes is not None:
351            data.process.append(notes)
352       
353    def set_collimation(self, collimation, notes=None):
354        """
355            set collimation for data
356        """
357        data, data_name, position = self.get_current_data()
358        if data is None:
359            return 
360        data.collimation = collimation
361        if notes is not None:
362            data.process.append(notes)
363               
364    def edit_collimation(self):
365        """
366            Edit the selected collimation
367        """
368        data, data_name, position = self.get_current_data()
369        if data is None:
370            return 
371        dlg = CollimationDialog(collimation=data.collimation)
372        dlg.set_manager(self)
373        dlg.ShowModal()
374           
375    def edit_detector(self):
376        """
377            Edit the selected detector
378        """
379        data, data_name, position = self.get_current_data()
380        if data is None:
381            return 
382        dlg = DetectorDialog(detector=data.detector)
383        dlg.set_manager(self)
384        dlg.ShowModal()
385
386    def edit_sample(self):
387        """
388            Open the dialog to edit the sample of the current data
389        """
390        data, data_name, position = self.get_current_data()
391        if data is None:
392            return
393        from sample_editor import SampleDialog
394        dlg = SampleDialog(parent=self, sample=data.sample)
395        dlg.set_manager(self)
396        dlg.ShowModal()
397       
398    def edit_source(self):
399        """
400            Open the dialog to edit the saource of the current data
401        """
402        data, data_name, position = self.get_current_data()
403        if data is None:
404            return
405        from source_editor import SourceDialog
406        dlg = SourceDialog(parent=self, source=data.source)
407        dlg.set_manager(self)
408        dlg.ShowModal()
409       
410    def choose_data_file(self, location=None):
411        """
412            Open a file dialog to allow loading a file
413        """
414        path = None
415        if location == None:
416            location = os.getcwd()
417       
418        l = Loader()
419        cards = l.get_wildcards()
420        wlist = '|'.join(cards)
421       
422        dlg = wx.FileDialog(self, "Choose a file", location, "", wlist, wx.OPEN)
423        if dlg.ShowModal() == wx.ID_OK:
424            path = dlg.GetPath()
425            mypath = os.path.basename(path)
426        dlg.Destroy()
427        return path
428   
429   
430    def complete_loading(self, data=None, filename=''):
431        """
432            Complete the loading and compute the slit size
433        """
434        self.done = True
435        self._data = []
436        if data is None:
437            msg = "Couldn't load data"
438            wx.PostEvent(self.parent.parent, StatusEvent(status=msg, info="warning",type='stop'))
439            return 
440        if not  data.__class__.__name__ == "list":
441            self._data.append(data)
442            self._reset_data.append(deepcopy(data))
443        else:
444            self._data = deepcopy(data)
445            self._reset_data = deepcopy(data)
446        self.set_values()
447        if self.parent.parent is None:
448            return 
449        msg = "Load Complete"
450        wx.PostEvent(self.parent.parent, StatusEvent(status=msg,info="info",type='stop'))
451 
452    def set_values(self):
453        """
454            take the aperture values of the current data and display them
455            through the panel
456        """
457        if self._data:
458            self.fill_data_combox()
459            self.on_select_data(event=None)
460       
461    def get_data(self):
462        """
463            return the current data
464        """
465        return self._data
466   
467    def get_notes(self):
468        """
469            return notes
470        """
471        return self._notes
472   
473    def on_change_run(self, event=None):
474        """
475            Change run
476        """
477        run = []
478        data, data_name, position = self.get_current_data()
479        for i in range(self.data_run_tcl.GetNumberOfLines()):
480            run.append(self.data_run_tcl.GetLineText(i).lstrip().rstrip())
481        #Change data's name
482        #run = self.data_run_tcl.GetValue().lstrip().rstrip()
483       
484        if data.run != run:
485            self._notes += "Change data 's "
486            self._notes += "run from %s to %s \n"%(data.run, str(run))
487            data.run = run
488           
489    def on_change_title(self, event=None):
490        """
491            Change title
492        """
493        data, data_name, position = self.get_current_data()
494        #Change data's name
495        title = self.data_title_tcl.GetValue().lstrip().rstrip()
496       
497        if data.title != title:
498            self._notes += "Change data 's "
499            self._notes += "title from %s to %s \n"%(data.title, str(title))
500            data.title = title
501           
502    def on_click_browse(self, event):
503        """
504            Open a file dialog to allow the user to select a given file.
505            Display the loaded data if available.
506        """
507        path = self.choose_data_file(location=self._default_save_location)
508        if path is None:
509            return 
510        if self.parent.parent is not None:
511            wx.PostEvent(self.parent.parent, StatusEvent(status="Loading...",info="info",
512                                type="progress"))
513       
514        self.done = False
515        self._default_save_location = path
516        try:
517            #Load data
518            from load_thread import DataReader
519            ## If a thread is already started, stop it
520            if self.reader is not None and self.reader.isrunning():
521                self.reader.stop()
522            self.reader = DataReader(path=path,
523                                    completefn=self.complete_loading,
524                                    updatefn=None)
525            self.reader.queue()
526        except:
527            msg = "Data Editor: %s"%(sys.exc_value)
528            load_error(msg)
529            return 
530         
531    def on_edit(self, event):
532        """
533        """
534        if self.detector_rb.GetValue():
535            self.edit_detector()
536        if self.sample_rb.GetValue():
537            self.edit_sample()
538        if self.source_rb.GetValue():
539            self.edit_source()
540        if self.collimation_rb.GetValue():
541            self.edit_collimation()
542           
543    def on_click_apply(self, event):
544        """   
545            changes are saved in data object imported to edit
546        """
547        data, data_name, position = self.get_current_data()
548        if data is None:
549            return
550        self.on_change_run(event=None)
551        self.on_change_title(event=None)
552        #must post event here
553       
554    def on_click_save(self, event):
555        """
556            Save change into a file
557        """
558        data, data_name, position = self.get_current_data()
559        if data is None:
560            return
561        self.on_change_run(event=None)
562        self.on_change_title(event=None)
563       
564        if issubclass(data.__class__, Data2D):
565            msg = "No conventional writing format for \n\n"
566            msg += "Data2D at this time.\n"
567            dlg = wx.MessageDialog(None, msg, 'Error Loading File',
568                                         wx.OK | wx.ICON_EXCLAMATION)
569            dlg.ShowModal()   
570            return 
571           
572        path = None
573        wildcard = "CanSAS 1D files(*.xml)|*.xml" 
574        dlg = wx.FileDialog(self, "Choose a file",
575                            self._default_save_location, "",wildcard , wx.SAVE)
576       
577        if dlg.ShowModal() == wx.ID_OK:
578            path = dlg.GetPath()
579            mypath = os.path.basename(path)
580            loader = Loader() 
581            format = ".xml"
582            if os.path.splitext(mypath)[1].lower() ==format:
583                loader.save( path, data, format)
584            try:
585                self._default_save_location = os.path.dirname(path)
586            except:
587                pass   
588        dlg.Destroy()
589       
590    def on_click_view(self, event):
591        """
592            Display data info
593        """
594        data, data_name, position = self.get_current_data()
595        if data is None:
596            return
597        self.on_change_run(event=None)
598        self.on_change_title(event=None)
599        dlg = ConsoleDialog(data=data)
600        dlg.ShowModal()
601   
602    def on_click_reset(self, event):
603        """
604        """
605        data, data_name, position = self.get_current_data()
606        if data is None:
607            return
608        self._data[position]= deepcopy(self._reset_data[position])
609        self.set_values()
610       
611    def on_close(self, event):
612        """
613            leave data as it is and close
614        """
615        self.parent.Close()
616       
617class DataEditorWindow(wx.Frame):
618    def __init__(self, parent, data=None, *args, **kwds):
619        kwds["size"]= (PANEL_WIDTH, PANEL_HEIGTH)
620        wx.Frame.__init__(self, parent, *args, **kwds)
621        self.parent = parent
622        self.panel = DataEditorPanel(parent=self, data=data)
623        self.Show()
624       
625    def get_data(self):
626        """
627            return the current data
628        """
629        return self.panel.get_data()
630   
631if __name__ =="__main__":
632   
633    app  = wx.App()
634    window = DataEditorWindow(parent=None, data=[], title="Data Editor")
635    app.MainLoop()
636 
Note: See TracBrowser for help on using the repository browser.