source: sasview/src/sas/sasgui/perspectives/calculator/collimation_editor.py @ a5e1b6ca

ticket-1249
Last change on this file since a5e1b6ca was 5251ec6, checked in by Paul Kienzle <pkienzle@…>, 6 years ago

improved support for py37 in sasgui

  • Property mode set to 100644
File size: 22.2 KB
Line 
1"""
2"""
3import sys
4from copy import deepcopy
5
6import wx
7
8from sas.sascalc.dataloader.loader import Loader
9from sas.sascalc.dataloader.data_info import Aperture, Collimation
10from sas.sasgui.guiframe.utils import check_float
11
12from .aperture_editor import ApertureDialog
13
14_BOX_WIDTH = 60
15
16if sys.platform.count("win32") > 0:
17    _STATICBOX_WIDTH = 500
18    PANEL_WIDTH = 530
19    PANEL_HEIGHT = 430
20    FONT_VARIANT = 0
21else:
22    _STATICBOX_WIDTH = 550
23    PANEL_WIDTH = 600
24    PANEL_HEIGHT = 480
25    FONT_VARIANT = 1
26
27class CollimationDialog(wx.Dialog):
28    """
29    """
30    def __init__(self, parent=None, manager=None,
31                 collimation=[], *args, **kwds):
32        """
33        """
34        kwds['size'] = (PANEL_WIDTH, PANEL_HEIGHT)
35        kwds['title'] = "Collimation Editor"
36        wx.Dialog.__init__(self, parent=parent, *args, **kwds)
37        self.parent = parent
38        self.manager = manager
39        self._collimation = collimation
40        self._reset_collimation = deepcopy(collimation)
41        self._notes = ""
42        self._description = "Edit collimation"
43        #layout attributes
44        self.main_sizer = None
45        self.box_collimation = None
46        self.boxsizer_collimation = None
47
48
49        self._do_layout()
50        self.set_values()
51
52    def _define_structure(self):
53        """
54        define initial sizer
55        """
56        self.main_sizer = wx.BoxSizer(wx.VERTICAL)
57        self.box_collimation = wx.StaticBox(self, -1,
58                                            str("Edit Selected Collimation"))
59        self.boxsizer_collimation = wx.StaticBoxSizer(self.box_collimation,
60                                                       wx.VERTICAL)
61
62        collimation_box = wx.StaticBox(self, -1, "Edit Number of Collimations")
63        self.collimation_sizer = wx.StaticBoxSizer(collimation_box, wx.VERTICAL)
64        self.collimation_sizer.SetMinSize((_STATICBOX_WIDTH, -1))
65        self.collimation_button_sizer = wx.BoxSizer(wx.HORIZONTAL)
66        self.collimation_hint_sizer = wx.BoxSizer(wx.HORIZONTAL)
67
68        self.name_sizer = wx.BoxSizer(wx.HORIZONTAL)
69        self.length_sizer = wx.BoxSizer(wx.HORIZONTAL)
70        self.button_sizer = wx.BoxSizer(wx.HORIZONTAL)
71
72        aperture_box = wx.StaticBox(self, -1, "Edit Aperture")
73        self.aperture_sizer = wx.StaticBoxSizer(aperture_box, wx.VERTICAL)
74        self.aperture_button_sizer = wx.BoxSizer(wx.HORIZONTAL)
75        self.aperture_hint_sizer = wx.BoxSizer(wx.HORIZONTAL)
76
77    def _layout_collimation(self):
78        """
79        Do the layout for collimation related widgets
80        """
81        collimation_name_txt = wx.StaticText(self, -1, "Collimation:")
82        hint_collimation_txt = 'Current available collimation.'
83        collimation_name_txt.SetToolTipString(hint_collimation_txt)
84        self.collimation_cbox = wx.ComboBox(self, -1, style=wx.CB_READONLY)
85        wx.EVT_COMBOBOX(self.collimation_cbox, -1, self.on_select_collimation)
86        hint_collimation_name_txt = 'Name of collimations.'
87        self.collimation_cbox.SetToolTipString(hint_collimation_name_txt)
88
89        self.bt_add_collimation = wx.Button(self, -1, "Add")
90        self.bt_add_collimation.SetToolTipString("Edit data's collimation.")
91        self.bt_add_collimation.Bind(wx.EVT_BUTTON, self.add_collimation)
92
93        self.bt_remove_collimation = wx.Button(self, -1, "Remove")
94        hint = "Remove data's collimation."
95        self.bt_remove_collimation.SetToolTipString(hint)
96        self.bt_remove_collimation.Bind(wx.EVT_BUTTON, self.remove_collimation)
97
98        self.collimation_button_sizer.AddMany([(collimation_name_txt, 0,
99                                                 wx.LEFT, 15),
100                                     (self.collimation_cbox, 0, wx.LEFT, 5),
101                                     (self.bt_add_collimation, 0, wx.LEFT, 10),
102                                     (self.bt_remove_collimation,
103                                       0, wx.LEFT, 5)])
104        collimation_hint_txt = 'No collimation is available for this data.'
105        self.collimation_txt = wx.StaticText(self, -1, collimation_hint_txt)
106        self.collimation_hint_sizer.Add(self.collimation_txt, 0, wx.LEFT, 10)
107        self.collimation_sizer.AddMany([(self.collimation_button_sizer,
108                                          0, wx.ALL, 10),
109                                     (self.collimation_hint_sizer,
110                                       0, wx.ALL, 10)])
111
112        self.fill_collimation_combox()
113        self.enable_collimation()
114
115
116    def _layout_name(self):
117        """
118        Do the layout for collimation name related widgets
119        """
120        #Collimation name [string]
121        name_txt = wx.StaticText(self, -1, 'Name : ')
122        self.name_tcl = wx.TextCtrl(self, -1, size=(_BOX_WIDTH * 5, 20), style=0)
123        self.name_sizer.AddMany([(name_txt, 0, wx.LEFT | wx.RIGHT, 10),
124                                       (self.name_tcl, 0, wx.EXPAND)])
125
126    def _layout_length(self):
127        """
128        Do the  layout for length related widgets
129        """
130        #Collimation length
131        length_txt = wx.StaticText(self, -1, 'Length:')
132        self.length_tcl = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, 20), style=0)
133        length_unit_txt = wx.StaticText(self, -1, 'Unit: ')
134        self.length_unit_tcl = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, 20),
135                                           style=0)
136        self.length_sizer.AddMany([(length_txt, 0, wx.LEFT | wx.RIGHT, 10),
137                                     (self.length_tcl, 0, wx.LEFT, 12),
138                                     (length_unit_txt, 0, wx.LEFT | wx.RIGHT, 10),
139                                     (self.length_unit_tcl, 0, wx.EXPAND)])
140
141    def _layout_button(self):
142        """
143        Do the layout for the button widgets
144        """
145        self.bt_apply = wx.Button(self, -1, 'Apply')
146        self.bt_apply.Bind(wx.EVT_BUTTON, self.on_click_apply)
147        self.bt_apply.SetToolTipString("Apply current changes to collimation.")
148        self.bt_cancel = wx.Button(self, -1, 'Cancel')
149        self.bt_cancel.SetToolTipString("Cancel current changes.")
150        self.bt_cancel.Bind(wx.EVT_BUTTON, self.on_click_cancel)
151        self.bt_close = wx.Button(self, wx.ID_CANCEL, 'Close')
152        self.bt_close.SetToolTipString("Close window.")
153        self.button_sizer.AddMany([(self.bt_apply, 0, wx.LEFT, 200),
154                                   (self.bt_cancel, 0, wx.LEFT, 10),
155                                   (self.bt_close, 0, wx.LEFT, 10)])
156    def _layout_aperture(self):
157        """
158        Do the layout for aperture related widgets
159        """
160        aperture_name_txt = wx.StaticText(self, -1, "Aperture:")
161        hint_aperture_txt = 'Current available aperture.'
162        aperture_name_txt.SetToolTipString(hint_aperture_txt)
163        self.aperture_cbox = wx.ComboBox(self, -1, style=wx.CB_READONLY)
164        hint_aperture_name_txt = 'Name of apertures.'
165        self.aperture_cbox.SetToolTipString(hint_aperture_name_txt)
166
167        self.bt_add_aperture = wx.Button(self, -1, "Add")
168        self.bt_add_aperture.SetToolTipString("Edit data's aperture.")
169        self.bt_add_aperture.Bind(wx.EVT_BUTTON, self.add_aperture)
170        self.bt_edit_aperture = wx.Button(self, -1, "Edit")
171        self.bt_edit_aperture.SetToolTipString("Edit data's aperture.")
172        self.bt_edit_aperture.Bind(wx.EVT_BUTTON, self.edit_aperture)
173        self.bt_remove_aperture = wx.Button(self, -1, "Remove")
174        self.bt_remove_aperture.SetToolTipString("Remove data's aperture.")
175        self.bt_remove_aperture.Bind(wx.EVT_BUTTON, self.remove_aperture)
176
177        self.aperture_button_sizer.AddMany([(aperture_name_txt, 0, wx.LEFT, 15),
178                                     (self.aperture_cbox, 0, wx.LEFT, 5),
179                                     (self.bt_add_aperture, 0, wx.LEFT, 10),
180                                     (self.bt_edit_aperture, 0, wx.LEFT, 5),
181                                     (self.bt_remove_aperture, 0, wx.LEFT, 5)])
182        aperture_hint_txt = 'No aperture is available for this data.'
183        self.aperture_txt = wx.StaticText(self, -1, aperture_hint_txt)
184        self.aperture_hint_sizer.Add(self.aperture_txt, 0, wx.LEFT, 10)
185        self.aperture_sizer.AddMany([(self.aperture_button_sizer,
186                                       0, wx.ALL, 10),
187                                     (self.aperture_hint_sizer, 0, wx.ALL, 10)])
188        self.fill_aperture_combox()
189        self.enable_aperture()
190
191    def _do_layout(self):
192        """
193        Draw the current panel
194        """
195        self._define_structure()
196        self._layout_collimation()
197        self._layout_name()
198        self._layout_length()
199        self._layout_aperture()
200        self._layout_button()
201
202        self.boxsizer_collimation.AddMany([(self.name_sizer, 0,
203                                          wx.EXPAND | wx.TOP | wx.BOTTOM, 5),
204                                          (self.length_sizer, 0,
205                                     wx.EXPAND | wx.TOP | wx.BOTTOM, 5),
206                                     (self.aperture_sizer, 0,
207                                     wx.EXPAND | wx.ALL, 10)])
208        self.main_sizer.AddMany([(self.collimation_sizer, 0, wx.ALL, 10),
209                                  (self.boxsizer_collimation, 0, wx.ALL, 10),
210                                  (self.button_sizer, 0,
211                                    wx.EXPAND | wx.TOP | wx.BOTTOM, 5)])
212        self.SetSizer(self.main_sizer)
213        self.SetAutoLayout(True)
214
215    def get_current_collimation(self):
216        """
217        """
218        if not self.collimation_cbox.IsEnabled():
219            return None, None, None
220        position = self.collimation_cbox.GetSelection()
221        if position == wx.NOT_FOUND:
222            return None, None, None
223        collimation_name = self.collimation_cbox.GetStringSelection()
224        collimation = self.collimation_cbox.GetClientData(position)
225        return collimation, collimation_name, position
226
227    def fill_collimation_combox(self):
228        """
229        fill the current combobox with the available collimation
230        """
231        if self._collimation is None or self._collimation == []:
232            return
233        for collimation in self._collimation:
234            pos = self.collimation_cbox.Append(str(collimation.name))
235            self.collimation_cbox.SetClientData(pos, collimation)
236            self.collimation_cbox.SetSelection(pos)
237            self.collimation_cbox.SetStringSelection(str(collimation.name))
238
239    def add_collimation(self, event):
240        """
241        Append empty collimation to data's list of collimation
242        """
243
244        if not self.collimation_cbox.IsEnabled():
245            self.collimation_cbox.Enable()
246        collimation = Collimation()
247        self._collimation.append(collimation)
248        position = self.collimation_cbox.Append(str(collimation.name))
249        self.collimation_cbox.SetClientData(position, collimation)
250        self.collimation_cbox.SetSelection(position)
251        self.enable_collimation()
252        self.bt_add_aperture.Enable()
253        self.fill_aperture_combox()
254        self.enable_aperture()
255        self.set_values()
256
257    def remove_collimation(self, event):
258        """
259        Remove collimation to data's list of collimation
260        """
261        if self.collimation_cbox.IsEnabled():
262            if self.collimation_cbox.GetCount() > 0:
263                position = self.collimation_cbox.GetCurrentSelection()
264                collimation = self.collimation_cbox.GetClientData(position)
265                if collimation in self._collimation:
266                    self._collimation.remove(collimation)
267                    self.collimation_cbox.Delete(position)
268                    #set the combo box box the next available item
269                    position = self.collimation_cbox.GetCount()
270                    if position > 0:
271                        position -= 1
272                    self.collimation_cbox.SetSelection(position)
273        if not self._collimation and self.collimation_cbox.GetCount() == 0:
274            self.bt_add_aperture.Disable()
275            self.name_tcl.Clear()
276            self.length_tcl.Clear()
277            self.length_unit_tcl.Clear()
278            self.aperture_cbox.Clear()
279            self.aperture_cbox.Disable()
280        #disable or enable the combo box when necessary
281        self.enable_collimation()
282        self.enable_aperture()
283
284    def on_select_collimation(self, event):
285        """
286        fill the control on the panel according to
287        the current  selected collimation
288        """
289        self.set_values()
290        self.fill_aperture_combox()
291        self.enable_aperture()
292
293    def enable_collimation(self):
294        """
295        Enable /disable widgets related to collimation
296        """
297        if self._collimation is not None and \
298            self.collimation_cbox.GetCount() > 0:
299            self.collimation_cbox.Enable()
300            self.bt_remove_collimation.Enable()
301            n_collimation = self.collimation_cbox.GetCount()
302            collimation_hint_txt = "collimations"
303            collimation_hint_txt += " available: %s " % str(n_collimation)
304            if len(self._collimation) > 0:
305                self.bt_remove_collimation.Enable()
306            else:
307                self.bt_remove_collimation.Disable()
308        else:
309            self.collimation_cbox.Disable()
310            self.bt_remove_collimation.Disable()
311            collimation_hint_txt = 'No collimation is available for this data.'
312        self.collimation_txt.SetLabel(collimation_hint_txt)
313
314
315    def reset_collimation_combobox(self, edited_collimation):
316        """
317        take all edited editor and reset clientdata of collimation combo box
318        """
319        for position in range(self.collimation_cbox.GetCount()):
320            collimation = self.collimation_cbox.GetClientData(position)
321            if collimation == edited_collimation:
322                collimation = edited_collimation
323                self.collimation_cbox.SetString(position, str(collimation.name))
324                self.collimation_cbox.SetClientData(position, collimation)
325                self.collimation_cbox.SetStringSelection(str(collimation.name))
326
327    def add_aperture(self, event):
328        """
329        Append empty aperture to data's list of aperture
330        """
331        collimation, _, _ = self.get_current_collimation()
332        if not self.aperture_cbox.IsEnabled():
333            self.aperture_cbox.Enable()
334        aperture = Aperture()
335        collimation.aperture.append(aperture)
336        position = self.aperture_cbox.Append(str(aperture.name))
337        self.aperture_cbox.SetClientData(position, aperture)
338        self.aperture_cbox.SetSelection(position)
339        self.enable_aperture()
340
341    def edit_aperture(self, event):
342        """
343        Edit the selected aperture
344        """
345        if self._collimation is None or not self.aperture_cbox.IsEnabled():
346            return
347        position = self.aperture_cbox.GetSelection()
348        if position != wx.NOT_FOUND:
349            name = self.aperture_cbox.GetStringSelection()
350            aperture = self.aperture_cbox.GetClientData(position)
351            dlg = ApertureDialog(parent=self, aperture=aperture)
352            dlg.set_manager(self)
353            dlg.ShowModal()
354
355    def remove_aperture(self, event):
356        """
357        Remove aperture to data's list of aperture
358        """
359        if self._collimation is None or not self._collimation:
360            return
361        collimation, _, _ = self.get_current_collimation()
362        if self.aperture_cbox.IsEnabled():
363            if self.aperture_cbox.GetCount() > 1:
364                position = self.aperture_cbox.GetCurrentSelection()
365                aperture = self.aperture_cbox.GetClientData(position)
366                if aperture in collimation.aperture:
367                    collimation.aperture.remove(aperture)
368                    self.aperture_cbox.Delete(position)
369                    #set the combo box box the next available item
370                    position = self.aperture_cbox.GetCount()
371                    if position > 0:
372                        position -= 1
373                    self.aperture_cbox.SetSelection(position)
374
375        #disable or enable the combo box when necessary
376        self.enable_aperture()
377
378    def set_aperture(self, aperture):
379        """
380        set aperture for data
381        """
382        if self._collimation is None or not self._collimation:
383            return
384        collimation, _, _ = self.get_current_collimation()
385        if collimation.aperture:
386            for item in collimation.aperture:
387                if item == aperture:
388                    item = aperture
389                    self.reset_aperture_combobox(edited_aperture=aperture)
390                    return
391
392    def enable_aperture(self):
393        """
394        Enable /disable widgets crelated to aperture
395        """
396        collimation, _, _ = self.get_current_collimation()
397        if  self.aperture_cbox.GetCount() > 0:
398            self.aperture_cbox.Enable()
399            self.bt_edit_aperture.Enable()
400            self.bt_remove_aperture.Enable()
401            n_aperture = self.aperture_cbox.GetCount()
402            aperture_hint_txt = 'apertures available: %s ' % str(n_aperture)
403            if len(collimation.aperture) > 0:
404                self.bt_remove_aperture.Enable()
405            else:
406                self.bt_remove_aperture.Disable()
407        else:
408            self.aperture_cbox.Disable()
409            self.bt_edit_aperture.Disable()
410            self.bt_remove_aperture.Disable()
411            aperture_hint_txt = 'No aperture is available for this data.'
412        self.aperture_txt.SetLabel(aperture_hint_txt)
413
414    def reset_aperture_combobox(self, edited_aperture):
415        """
416        take all edited editor and reset clientdata of aperture combo box
417        """
418        for position in range(self.aperture_cbox.GetCount()):
419            aperture = self.aperture_cbox.GetClientData(position)
420            if aperture == edited_aperture:
421                aperture = edited_aperture
422                self.aperture_cbox.SetString(position, str(aperture.name))
423                self.aperture_cbox.SetClientData(position, aperture)
424                self.aperture_cbox.SetStringSelection(str(aperture.name))
425
426    def fill_aperture_combox(self):
427        """
428        fill the current combobox with the available aperture
429        """
430        self.aperture_cbox.Clear()
431        collimation, _, _ = self.get_current_collimation()
432        if collimation is None or not collimation.aperture:
433            return
434        for aperture in collimation.aperture:
435            pos = self.aperture_cbox.Append(str(aperture.name))
436            self.aperture_cbox.SetClientData(pos, aperture)
437            self.aperture_cbox.SetSelection(pos)
438            self.aperture_cbox.SetStringSelection(str(aperture.name))
439
440    def set_manager(self, manager):
441        """
442        Set manager of this window
443        """
444        self.manager = manager
445
446    def set_values(self):
447        """
448        take the collimation values of the current data and display them
449        through the panel
450        """
451        collimation, _, _ = self.get_current_collimation()
452        if collimation is None:
453            self.bt_add_aperture.Disable()
454            self.length_tcl.SetValue("")
455            self.name_tcl.SetValue("")
456            self.length_unit_tcl.SetValue("")
457            return
458        #Name
459        self.name_tcl.SetValue(str(collimation.name))
460        #length
461        self.length_tcl.SetValue(str(collimation.length))
462        #Length unit
463        self.length_unit_tcl.SetValue(str(collimation.length_unit))
464
465    def get_collimation(self):
466        """
467        return the current collimation
468        """
469        return self._collimation
470
471    def get_notes(self):
472        """
473        return notes
474        """
475        return self._notes
476
477    def on_change_name(self):
478        """
479        Change name
480        """
481        collimation, collimation_name, position = self.get_current_collimation()
482        if collimation is None:
483            return
484        #Change the name of the collimation
485        name = self.name_tcl.GetValue().lstrip().rstrip()
486        if name == "" or name == str(None):
487            name = None
488        if collimation.name != name:
489            self._notes += "Change collimation 's "
490            self._notes += "name from %s to %s \n" % (collimation.name, name)
491            collimation.name = name
492            self.collimation_cbox.SetString(position, str(collimation.name))
493            self.collimation_cbox.SetClientData(position, collimation)
494            self.collimation_cbox.SetStringSelection(str(collimation.name))
495
496    def on_change_length(self):
497        """
498        Change the length
499        """
500        collimation, collimation_name, position = self.get_current_collimation()
501        if collimation is None:
502            return
503        #Change length 
504        length = self.length_tcl.GetValue().lstrip().rstrip()
505        if length == "" or length == str(None):
506            length = None
507            collimation.length = length
508        else:
509            if check_float(self.length_tcl):
510                if collimation.length != float(length):
511                    self._notes += "Change Collimation length from "
512                    self._notes += "%s to %s \n" % (collimation.length, length)
513                    collimation.length = float(length)
514            else:
515                self._notes += "Error: Expected a float for collimation length"
516                self._notes += " won't changes length from "
517                self._notes += "%s to %s" % (collimation.length, length)
518        #change length  unit
519        unit = self.length_unit_tcl.GetValue().lstrip().rstrip()
520        if collimation.length_unit != unit:
521            self._notes += " Change length's unit from "
522            self._notes += "%s to %s" % (collimation.length_unit, unit)
523            collimation.length_unit = unit
524
525    def on_click_apply(self, event):
526        """
527        Apply user values to the collimation
528        """
529        self.on_change_name()
530        self.on_change_length()
531        self.set_values()
532        if self.manager is not None:
533            self.manager.set_collimation(self._collimation, self._notes)
534
535    def on_click_cancel(self, event):
536        """
537        leave the collimation as it is and close
538        """
539        self._collimation = deepcopy(self._reset_collimation)
540        self.set_values()
541        if self.manager is not None:
542            self.manager.set_collimation(self._collimation)
543
544
545if __name__ == "__main__":
546
547    app = wx.App()
548    dlg = CollimationDialog(collimation=[Collimation()])
549    dlg.ShowModal()
550    app.MainLoop()
Note: See TracBrowser for help on using the repository browser.