1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 """Module containing the classes for GUI components involving spectral data."""
26
27
28 from os import sep
29 from re import search
30 import wx
31 import wx.lib.buttons
32
33
34 from data import Relax_data_store; ds = Relax_data_store()
35 from status import Status; status = Status()
36
37
38 from gui.filedialog import RelaxFileDialog
39 from gui.fonts import font
40 from gui.message import error_message
41 from gui.misc import add_border
42 from gui.string_conv import str_to_gui
43 from gui import paths
44
45
47 """Custom GridCellEditor for the number of delays grid cells.
48
49 Changing these cells will update the relaxation delay times.
50 """
51
52 - def __init__(self, min=None, max=None, parent=None):
53 """Initialise the class.
54
55 @keyword min: The minimum value for wx.SpinCtrl.
56 @type min: None or int
57 @keyword max: The maximum value for wx.SpinCtrl.
58 @type max: None or int
59 @keyword parent: The parent wx object.
60 @type parent: wx object
61 """
62
63
64 self.min = min
65 self.max = max
66 self.parent = parent
67
68
69 super(Delay_num_cell_editor, self).__init__()
70
71
72 self.reset = False
73
74
76 """Start the editing.
77
78 @param row: The row index.
79 @type row: int
80 @param col: The column index.
81 @type col: int
82 @param grid: The grid GUI element.
83 @type grid: wx.grid.Grid instance.
84 """
85
86
87 self.prev_val = grid.GetTable().GetValue(row, col)
88
89
90 self.cell.SetValueString(str_to_gui(self.prev_val))
91
92
93 self.cell.SetFocus()
94
95
97 """Create and return a new class instance."""
98
99
100 return Delay_num_cell_editor(self.min, self.max, self.parent)
101
102
103 - def Create(self, parent, id, evtHandler):
104 """Create the control for the cell.
105
106 @param parent: The parent wx object.
107 @type parent: wx object
108 @param id: The ID number.
109 @type id: int
110 @param evtHandler: The event handler function.
111 @type evtHandler: func
112 """
113
114
115 self.cell = wx.SpinCtrl(parent, id, "", min=self.min, max=self.max)
116 self.SetControl(self.cell)
117
118
119 if evtHandler:
120 self.cell.PushEventHandler(evtHandler)
121
122
123 - def EndEdit(self, row, col, grid):
124 """End the editing.
125
126 @param row: The row index.
127 @type row: int
128 @param col: The column index.
129 @type col: int
130 @param grid: The grid GUI element.
131 @type grid: wx.grid.Grid instance.
132 """
133
134
135 if self.reset:
136
137 self.reset = False
138
139
140 if self.prev_val == '':
141 return False
142
143
144 value = self.cell.GetValue()
145
146
147 if value == self.prev_val:
148 return False
149
150
151 if value == 0:
152 text = ''
153 else:
154 text = str(value)
155 grid.GetTable().SetValue(row, col, str_to_gui(text))
156
157
158 time = self.parent.delay_time.GetValue()
159
160
161 if time == '':
162
163 return True
164
165
166 delay_time = float(time) * float(value)
167 grid.GetTable().SetValue(row, col-1, str_to_gui(delay_time))
168
169
170 return True
171
172
174 """Reset the cell to the previous value."""
175
176
177 self.cell.SetValueString(str_to_gui(self.prev_val))
178
179
180 self.reset = True
181
182
184 """Catch the starting key stroke to add the value to the cell.
185
186 @param event: The wx event.
187 @type event: wx event
188 """
189
190
191 key = event.GetKeyCode()
192
193
194 if key >= 49 and key <= 57:
195
196 num = int(chr(key))
197
198
199 self.cell.SetValue(str_to_gui(num))
200
201
202 self.cell.SetSelection(1, 1)
203
204
205 else:
206 event.Skip()
207
208
209
211 """The peak list selection class."""
212
213
214 col_label_width = 40
215 col1_width = 160
216 col2_width = 140
217
218 - def __init__(self, gui=None, parent=None, subparent=None, data=None, label=None, width=688, height=300, box=None):
219 """Build the peak list reading GUI element.
220
221 @keyword gui: The main GUI object.
222 @type gui: wx.Frame instance
223 @keyword parent: The parent GUI element that this is to be attached to (the panel object).
224 @type parent: wx object
225 @keyword subparent: The subparent GUI element that this is to be attached to (the analysis object).
226 @type subparent: wx object
227 @keyword data: The data storage container.
228 @type data: class instance
229 @keyword label: The type of analysis.
230 @type label: str
231 @keyword width: The initial width of the GUI element.
232 @type width: int
233 @keyword height: The initial height of the GUI element.
234 @type height: int
235 @keyword box: The vertical box sizer to pack this GUI component into.
236 @type box: wx.BoxSizer instance
237 """
238
239
240 self.gui = gui
241 self.parent = parent
242 self.subparent = subparent
243 self.data = data
244 self.label = label
245
246
247 self.spacing = 5
248 self.border = 5
249
250
251 self.num_rows = 50
252
253
254 stat_box = wx.StaticBox(self.parent, -1, "Peak lists")
255 stat_box.SetFont(font.subtitle)
256 sub_sizer = wx.StaticBoxSizer(stat_box, wx.VERTICAL)
257
258
259 box.Add(sub_sizer, 1, wx.ALL|wx.EXPAND, 0)
260
261
262 box_centre = add_border(sub_sizer, border=self.border)
263
264
265 box_centre.AddSpacer(self.spacing)
266 self.delay_time = self.subparent.add_text_sel_element(box_centre, self.parent, text="Single delay cycle time [s]")
267
268
269 box_centre.AddSpacer(self.spacing)
270 self.add_grid(box_centre)
271 box_centre.AddSpacer(self.spacing)
272
273
274 self.delay_time.Bind(wx.EVT_KEY_DOWN, self.change_delay_down)
275 self.delay_time.Bind(wx.EVT_KEY_UP, self.change_delay_up)
276
277
279 """Catch the resize to allow the grid to be resized.
280
281 @param event: The wx event.
282 @type event: wx event
283 """
284
285
286 x, y = event.GetSize()
287
288
289 width = x - self.col_label_width - self.col1_width - self.col2_width - 20
290
291
292 self.grid.SetRowLabelSize(self.col_label_width)
293 self.grid.SetColSize(0, width)
294 self.grid.SetColSize(1, self.col1_width)
295 self.grid.SetColSize(2, self.col2_width)
296
297
298 event.Skip()
299
300
344
345
347 """Add the grid for the peak list files and delay times.
348
349 @param sizer: The sizer element to pack the grid into.
350 @type sizer: wx.BoxSizer instance
351 """
352
353
354 self.grid = wx.grid.Grid(self.parent, -1)
355
356
357 self.grid.CreateGrid(self.num_rows, 3)
358
359
360 self.grid.SetColLabelValue(0, "%s peak list" % self.label)
361 self.grid.SetColLabelValue(1, "Relaxation delay [s]")
362 self.grid.SetColLabelValue(2, "No. of cycles")
363
364
365 self.grid.SetDefaultCellFont(font.normal)
366 self.grid.SetLabelFont(font.normal_bold)
367
368
369 height = self.delay_time.GetSize()[1]
370
371
372 for i in range(self.grid.GetNumberRows()):
373
374 self.grid.SetCellEditor(i, 2, Delay_num_cell_editor(0, 200, self))
375
376
377 self.grid.SetRowSize(i, height)
378
379
380 self.grid.EnableDragColSize(False)
381 self.grid.EnableDragRowSize(False)
382
383
384 self.grid.GetGridWindow().Bind(wx.EVT_LEFT_DCLICK, self.event_left_dclick)
385 self.grid.Bind(wx.EVT_KEY_DOWN, self.event_key_down)
386 self.grid.Bind(wx.EVT_KEY_UP, self.event_key_up)
387 self.grid.Bind(wx.EVT_SIZE, self.resize)
388
389
390 sizer.Add(self.grid, 1, wx.ALL|wx.EXPAND, 0)
391
392
394 """Handle changes to the delay time.
395
396 @param event: The wx event.
397 @type event: wx event
398 """
399
400
401 key = event.GetKeyCode()
402
403
404 text = str(self.delay_time.GetString(0, self.delay_time.GetLastPosition()))
405
406
407 allowed = []
408 allowed += [8]
409 if not search('\.', text):
410 allowed += [46]
411 allowed += [48, 49, 50, 51, 52, 53, 54, 55, 56, 57]
412 allowed += [127]
413 allowed += [wx.WXK_LEFT, wx.WXK_RIGHT, wx.WXK_HOME, wx.WXK_END]
414
415
416 if key not in allowed:
417 return
418
419
420 event.Skip()
421
422
424 """Handle updates to the delay time.
425
426 @param event: The wx event.
427 @type event: wx event
428 """
429
430
431 event.Skip()
432
433
434 self.update_grid()
435
436
438 """Handle the left mouse double click.
439
440 @param event: The wx event.
441 @type event: wx event
442 """
443
444
445 col = self.grid.GetGridCursorCol()
446 row = self.grid.GetGridCursorRow()
447
448
449 if col == 0:
450
451 dialog = RelaxFileDialog(parent=self, style=wx.FD_OPEN)
452
453
454 if status.show_gui and dialog.ShowModal() != wx.ID_OK:
455
456 return
457
458
459 filename = dialog.get_file()
460
461
462 self.grid.SetCellValue(row, col, str(filename))
463
464
465 event.Skip()
466
467
469 """Control what happens when a key is pressed.
470
471 @param event: The wx event.
472 @type event: wx event
473 """
474
475
476 if event.GetKeyCode() == wx.WXK_DELETE:
477
478 cells = self.get_selection()
479
480
481 if status.debug:
482 print(cells)
483
484
485 for cell in cells:
486
487 self.grid.SetCellValue(cell[0], cell[1], '')
488
489
490 self.update_grid()
491
492
493 return
494
495
496 event.Skip()
497
498
500 """Control what happens when a key is released.
501
502 @param event: The wx event.
503 @type event: wx event
504 """
505
506
507 self.update_grid()
508
509
510 event.Skip()
511
512
514 """Convert the cell range into a coordinate list.
515
516 @param top_left: The top left hand coordinate.
517 @type top_left: list or tuple
518 @param bottom_right: The bottom right hand coordinate.
519 @type bottom_right: list or tuple
520 @return: The list of tuples of coordinates of all cells.
521 @rtype: list of tuples
522 """
523
524
525 cells = []
526
527
528 for x in range(top_left[0], bottom_right[0]+1):
529
530 for y in range(top_left[1], bottom_right[1]+1):
531
532 cells.append((x, y))
533
534
535 return cells
536
537
539 """Determine which cells are selected.
540
541 There are three possibilities for cell selections in a wx.grid. These are:
542
543 - Single cell selection (this is not highlighted).
544 - Multiple cells are selected.
545 - Column selection.
546 - Row selection.
547
548 @return: An array of the cell selection coordinates.
549 @rtype: list of tuples of int
550 """
551
552
553 top_left = self.grid.GetSelectionBlockTopLeft()
554 bottom_right = self.grid.GetSelectionBlockBottomRight()
555
556
557 selection = self.grid.GetSelectedCells()
558 col = self.grid.GetSelectedCols()
559 row = self.grid.GetSelectedRows()
560
561
562 if status.debug:
563 print("\nTop left: %s" % top_left)
564 print("Bottom right: %s" % bottom_right)
565 print("selection: %s" % selection)
566 print("col: %s" % col)
567 print("row: %s" % row)
568
569
570 if col:
571
572 if status.debug:
573 print("Column selection")
574
575
576 return self.get_all_coordinates([0, col[0]], [self.num_rows-1, col[-1]])
577
578
579 elif row:
580
581 if status.debug:
582 print("Row selection")
583
584
585 return self.get_all_coordinates([row[0], 0], [row[-1], 1])
586
587
588 elif top_left and not selection:
589
590 if status.debug:
591 print("Multiple block selection.")
592
593
594 cells = []
595
596
597 for n in range(len(top_left)):
598
599 cells = cells + self.get_all_coordinates(top_left[n], bottom_right[n])
600
601
602 return cells
603
604
605 elif not selection and not top_left:
606
607 if status.debug:
608 print("Single cell.")
609
610
611 pos = self.grid.GetGridCursorRow(), self.grid.GetGridCursorCol()
612
613
614 return [pos]
615
616
617 elif selection:
618
619 if status.debug:
620 print("Complex selection.")
621
622
623 cells = []
624
625
626 for n in range(len(top_left)):
627
628 cells = cells + self.get_all_coordinates(top_left[n], bottom_right[n])
629
630
631 return cells + selection
632
633
634 else:
635
636 if status.debug:
637 print("Should not be here.")
638
639
641 """The variable delay list loading GUI element.
642
643 @param event: The wx event.
644 @type event: wx event
645 """
646
647
648
649
650 if vc:
651 try:
652 vc_factor = float(self.vc_time.GetValue())
653 except:
654 error_message('VC time is not a number.')
655 return
656
657
658 else:
659 vc_factor = 1
660
661
662 dialog = RelaxFileDialog(parent=self, style=wx.FD_OPEN)
663
664
665 if status.show_gui and dialog.ShowModal() != wx.ID_OK:
666
667 return
668
669
670 filename = dialog.get_file()
671
672
673 file = open(filename, 'r')
674
675
676 index = 0
677 for line in file:
678
679 try:
680 t = float(line.replace('/n', ''))
681 except:
682 continue
683
684
685 self.grid.SetCellValue(index, 1, str(t*vc_factor))
686
687
688 index = index + 1
689
690
691 if index == self.num_rows:
692 error_message('Too many entries in list.')
693 return
694
695
697 """Function to load peak lists to data grid.
698
699 @param event: The wx event.
700 @type event: wx event
701 """
702
703
704 dialog = RelaxFileDialog(parent=self, message='Select the %s peak list file'%self.label, style=wx.FD_OPEN|wx.FD_MULTIPLE)
705
706
707 if status.show_gui and dialog.ShowModal() != wx.ID_OK:
708
709 return
710
711
712 files = dialog.get_file()
713
714
715 index = 0
716 for i in range(self.num_rows):
717
718 if str(self.grid.GetCellValue(i, 0)) == '':
719
720 self.grid.SetCellValue(i, 0, str(files[index]))
721
722
723 index = index + 1
724
725
726 if index == len(files):
727 break
728
729
730 if index < (len(files)-1):
731 error_message('Not all files could be loaded.')
732
733
735 """Synchronise the rx analysis frame and the relax data store, both ways.
736
737 This method allows the frame information to be uploaded into the relax data store, or for the information in the relax data store to be downloaded by the frame.
738
739 @keyword upload: A flag which if True will cause the frame to send data to the relax data store. If False, data will be downloaded from the relax data store to update the frame.
740 @type upload: bool
741 """
742
743
744 if upload:
745
746 self.data.delay_time = str(self.delay_time.GetString(0, self.delay_time.GetLastPosition()))
747
748
749 for i in range(self.num_rows):
750
751 if not hasattr(self.data, 'file_list'):
752 self.data.file_list = []
753 if not hasattr(self.data, 'ncyc'):
754 self.data.ncyc = []
755 if not hasattr(self.data, 'relax_times'):
756 self.data.relax_times = []
757
758
759 file_name = str(self.grid.GetCellValue(i, 0))
760 relax_time = str(self.grid.GetCellValue(i, 1))
761 ncyc = str(self.grid.GetCellValue(i, 2))
762
763
764 if file_name == '' and ncyc == '':
765 break
766
767
768 if i >= len(self.data.file_list):
769 self.data.file_list.append('')
770 if i >= len(self.data.ncyc):
771 self.data.ncyc.append('')
772 if i >= len(self.data.relax_times):
773 self.data.relax_times.append('')
774
775
776 self.data.file_list[i] = file_name
777 self.data.ncyc[i] = ncyc
778 self.data.relax_times[i] = relax_time
779
780 else:
781
782 if hasattr(self.data, 'delay_time'):
783 self.delay_time.SetValue(str_to_gui(self.data.delay_time))
784
785
786 for i in range(len(self.data.file_list)):
787
788 if hasattr(self.data, 'file_list'):
789 self.grid.SetCellValue(i, 0, str_to_gui(self.data.file_list[i]))
790
791
792 if hasattr(self.data, 'relax_times'):
793 self.grid.SetCellValue(i, 1, str_to_gui(self.data.relax_times[i]))
794
795
796 if hasattr(self.data, 'ncyc'):
797 self.grid.SetCellValue(i, 2, str_to_gui(self.data.ncyc[i]))
798
799
800 self.update_grid()
801
802
804 """Update the grid, changing the relaxation delay times as needed."""
805
806
807 time = self.delay_time.GetString(0, self.delay_time.GetLastPosition())
808 try:
809 time = float(time)
810 except ValueError:
811 time = ''
812
813
814 for i in range(self.grid.GetNumberRows()):
815
816 ncyc = str(self.grid.GetCellValue(i, 2))
817
818
819 if time != '' and ncyc not in ['', '0']:
820 self.grid.SetCellValue(i, 1, str(int(ncyc) * time))
821
822
823 relax_time = str(self.grid.GetCellValue(i, 1))
824
825
826 if relax_time == '0.0':
827 self.grid.SetCellValue(i, 1, '')
828