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