1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 """The pipe editor GUI element."""
24
25
26 import wx
27 import wx.grid
28
29
30 from data_store import Relax_data_store; ds = Relax_data_store()
31 from graphics import WIZARD_IMAGE_PATH, fetch_icon
32 from gui.components.menu import build_menu_item
33 from gui.fonts import font
34 from gui.icons import Relax_icons
35 from gui.message import Question
36 from gui.misc import add_border, bitmap_setup
37 from gui.string_conv import gui_to_str, str_to_gui
38 from gui.uf_objects import Uf_storage; uf_store = Uf_storage()
39 from lib.errors import RelaxError
40 from pipe_control.pipes import cdp_name, delete, get_bundle, get_type, pipe_names, switch
41 from status import Status; status = Status()
42
43
44
45 MENU_BUNDLE = wx.NewId()
46 MENU_DELETE = wx.NewId()
47 MENU_SWITCH = wx.NewId()
48 MENU_NEW_AUTO_ANALYSIS = wx.NewId()
49
50
52 """The pipe editor window object."""
53
54 - def __init__(self, gui=None, size_x=1000, size_y=600, border=10):
55 """Set up the relax controller frame.
56
57 @keyword gui: The main GUI object.
58 @type gui: wx.Frame instance
59 @keyword size_x: The initial and minimum width of the window.
60 @type size_x: int
61 @keyword size_y: The initial and minimum height of the window.
62 @type size_y: int
63 @keyword border: The size of the internal border of the window.
64 @type border: int
65 """
66
67
68 self.gui = gui
69 self.border = border
70
71
72 wx.Frame.__init__(self, None, id=-1, title="Data pipe editor")
73
74
75 self.SetIcons(Relax_icons())
76
77
78 self.width_col_label = 40
79
80
81 self.SetMinSize((size_x, size_y))
82 self.SetSize((size_x, size_y))
83
84
85 self.main_panel = wx.Panel(self, -1)
86
87
88 main_sizer = wx.BoxSizer(wx.VERTICAL)
89 self.main_panel.SetSizer(main_sizer)
90
91
92 sizer = add_border(main_sizer, border=border, packing=wx.VERTICAL)
93
94
95 sizer.AddSpacer(10)
96 self.add_logo(sizer)
97 sizer.AddSpacer(20)
98 self.add_buttons(sizer)
99 sizer.AddSpacer(10)
100 self.add_table(sizer)
101
102
103 self.grid.Bind(wx.EVT_SIZE, self.resize)
104 self.Bind(wx.EVT_CLOSE, self.handler_close)
105 self.grid.Bind(wx.grid.EVT_GRID_CELL_RIGHT_CLICK, self.menu)
106
107
108 self.name = 'pipe editor'
109
110
111 self.update_grid()
112
113
115 """Activate or deactivate certain elements in response to the execution lock."""
116
117
118 if status.exec_lock.locked():
119 wx.CallAfter(self.button_bundle.Enable, False)
120 wx.CallAfter(self.button_create.Enable, False)
121 wx.CallAfter(self.button_copy.Enable, False)
122 wx.CallAfter(self.button_delete.Enable, False)
123 wx.CallAfter(self.button_hybrid.Enable, False)
124 wx.CallAfter(self.button_switch.Enable, False)
125
126
127 else:
128 wx.CallAfter(self.button_bundle.Enable, True)
129 wx.CallAfter(self.button_create.Enable, True)
130 wx.CallAfter(self.button_copy.Enable, True)
131 wx.CallAfter(self.button_delete.Enable, True)
132 wx.CallAfter(self.button_hybrid.Enable, True)
133 wx.CallAfter(self.button_switch.Enable, True)
134
135
137 """The pop up menu.
138
139 @param event: The wx event.
140 @type event: wx event
141 """
142
143
144 row = event.GetRow()
145
146
147 self.selected_pipe = gui_to_str(self.grid.GetCellValue(row, 0))
148
149
150 if not self.selected_pipe:
151 return
152
153
154 pipe_type = get_type(self.selected_pipe)
155 pipe_bundle = get_bundle(self.selected_pipe)
156
157
158 popup_menus = []
159
160
161 if not pipe_bundle:
162 popup_menus.append({
163 'id': MENU_BUNDLE,
164 'text': "&Add the pipe to a bundle",
165 'icon': fetch_icon("relax.pipe_bundle"),
166 'method': self.pipe_bundle
167 })
168
169
170 popup_menus.append({
171 'id': MENU_DELETE,
172 'text': "&Delete the pipe",
173 'icon': fetch_icon('oxygen.actions.list-remove', "16x16"),
174 'method': self.pipe_delete
175 })
176
177
178 popup_menus.append({
179 'id': MENU_SWITCH,
180 'text': "&Switch to this pipe",
181 'icon': fetch_icon('oxygen.actions.system-switch-user', "16x16"),
182 'method': self.pipe_switch
183 })
184
185
186 if pipe_bundle and self.gui.analysis.page_index_from_bundle(pipe_bundle) == None and pipe_type in ['noe', 'r1', 'r2', 'mf', 'relax_disp']:
187 popup_menus.append({
188 'id': MENU_NEW_AUTO_ANALYSIS,
189 'text': "&Associate with a new auto-analysis",
190 'icon': fetch_icon('oxygen.actions.document-new', "16x16"),
191 'method': self.associate_auto
192 })
193
194
195 if status.exec_lock.locked():
196 return
197
198
199 menu = wx.Menu()
200
201
202 for i in range(len(popup_menus)):
203
204 info = popup_menus[i]
205
206
207 build_menu_item(menu, id=info['id'], text=info['text'], icon=info['icon'])
208
209
210 self.Bind(wx.EVT_MENU, info['method'], id=info['id'])
211
212
213 if status.show_gui:
214 self.PopupMenu(menu)
215
216
217 menu.Destroy()
218
219
278
279
281 """Launch the user function GUI wizards.
282
283 @param event: The wx event.
284 @type event: wx event
285 """
286
287
288 if event.GetEventObject() == self.button_bundle:
289 uf_store['pipe.bundle'](event, wx_parent=self, wx_wizard_sync=True, wx_wizard_modal=True)
290 elif event.GetEventObject() == self.button_create:
291 uf_store['pipe.create'](event, wx_parent=self, wx_wizard_sync=True, wx_wizard_modal=True)
292 elif event.GetEventObject() == self.button_copy:
293 uf_store['pipe.copy'](event, wx_parent=self, wx_wizard_sync=True, wx_wizard_modal=True)
294 elif event.GetEventObject() == self.button_delete:
295 uf_store['pipe.delete'](event, wx_parent=self, wx_wizard_sync=True, wx_wizard_modal=True)
296 elif event.GetEventObject() == self.button_hybrid:
297 uf_store['pipe.hybridise'](event, wx_parent=self, wx_wizard_sync=True, wx_wizard_modal=True)
298 elif event.GetEventObject() == self.button_switch:
299 uf_store['pipe.switch'](event, wx_parent=self, wx_wizard_sync=True, wx_wizard_modal=True)
300
301
303 """Add the logo to the sizer.
304
305 @param box: The sizer element to pack the logo into.
306 @type box: wx.Sizer instance
307 """
308
309
310 logo = wx.StaticBitmap(self.main_panel, -1, bitmap_setup(WIZARD_IMAGE_PATH+'pipe_200x90.png'))
311
312
313 box.Add(logo, 0, wx.ALIGN_CENTER_HORIZONTAL, 0)
314
315
317 """Add the table to the sizer.
318
319 @param sizer: The sizer element to pack the table into.
320 @type sizer: wx.Sizer instance
321 """
322
323
324 self.grid = wx.grid.Grid(self.main_panel, -1)
325
326
327 self.grid.CreateGrid(1, 5)
328
329
330 self.grid.SetColLabelValue(0, "Data pipe")
331 self.grid.SetColLabelValue(1, "Type")
332 self.grid.SetColLabelValue(2, "Bundle")
333 self.grid.SetColLabelValue(3, "Current")
334 self.grid.SetColLabelValue(4, "Analysis tab")
335
336
337 self.grid.SetDefaultCellFont(font.normal)
338 self.grid.SetLabelFont(font.normal_bold)
339
340
341 self.grid.SetRowLabelSize(self.width_col_label)
342
343
344 self.grid.EnableDragColSize(False)
345 self.grid.EnableDragRowSize(False)
346
347
348 sizer.Add(self.grid, 1, wx.ALL|wx.EXPAND, 0)
349
350
352 """Associate the selected data pipe with a new auto-analysis.
353
354 @param event: The wx event.
355 @type event: wx event
356 """
357
358
359 if not hasattr(ds, 'relax_gui'):
360 self.gui.init_data()
361
362
363 type = get_type(self.selected_pipe)
364 bundle = get_bundle(self.selected_pipe)
365
366
367 if self.selected_pipe == None:
368 raise RelaxError("No data pipe has been selected - this is not possible.")
369 if bundle == None:
370 raise RelaxError("The selected data pipe is not associated with a data pipe bundle.")
371
372
373 names = {
374 'noe': 'Steady-state NOE',
375 'r1': 'R1 relaxation',
376 'r2': 'R2 relaxation',
377 'mf': 'Model-free',
378 'relax_disp': 'Relaxation dispersion'
379 }
380
381
382 self.gui.analysis.new_analysis(analysis_type=type, analysis_name=names[type], pipe_name=self.selected_pipe, pipe_bundle=bundle)
383
384
386 """Event handler for the close window action.
387
388 @param event: The wx event.
389 @type event: wx event
390 """
391
392
393 self.observer_setup(register=False)
394
395
396 self.Hide()
397
398
417
418
420 """Bundle the date pipe.
421
422 @param event: The wx event.
423 @type event: wx event
424 """
425
426
427 uf_store['pipe.bundle'](event, wx_parent=self, pipe=self.selected_pipe)
428
429
431 """Delete the date pipe.
432
433 @param event: The wx event.
434 @type event: wx event
435 """
436
437
438 msg = "Are you sure you would like to delete the '%s' data pipe? This operation cannot be undone." % self.selected_pipe
439 if status.show_gui and Question(msg, parent=self, size=(350, 200), default=False).ShowModal() == wx.ID_NO:
440 return
441
442
443 delete(self.selected_pipe)
444
445
447 """Switch to the selected date pipe.
448
449 @param event: The wx event.
450 @type event: wx event
451 """
452
453
454 switch(self.selected_pipe)
455
456
457 wx.CallAfter(self.Raise)
458
459
461 """Catch the resize to allow the grid to be resized.
462
463 @param event: The wx event.
464 @type event: wx event
465 """
466
467
468 self.size_cols()
469
470
471 event.Skip()
472
473
475 """Set the column sizes."""
476
477
478 x, y = self.grid.GetSize()
479
480
481 n = 5
482
483
484 width_col_curr = 80
485
486
487 width = int((x - self.width_col_label - width_col_curr) / (n - 1))
488
489
490 for i in range(n):
491
492 if i == 3:
493 self.grid.SetColSize(i, width_col_curr)
494
495
496 else:
497 self.grid.SetColSize(i, width)
498
499
501 """Update the grid in a thread safe way using wx.CallAfter."""
502
503
504 wx.CallAfter(self.update_grid_safe)
505
506
507 wx.GetApp().Yield(True)
508
509
511 """Update the grid with the pipe data."""
512
513
514 self.grid.Freeze()
515
516
517 status.pipe_lock.acquire('pipe editor window')
518
519
520 self.grid.DeleteRows(numRows=self.grid.GetNumberRows()-1)
521
522
523 for i in range(self.grid.GetNumberCols()):
524 self.grid.SetCellValue(0, i, str_to_gui(""))
525
526
527 pipe_list = pipe_names()
528 n = len(pipe_list)
529
530
531 if n >= 1:
532 self.grid.AppendRows(numRows=n-1)
533
534
535 for i in range(n):
536
537 self.grid.SetCellValue(i, 0, str_to_gui(pipe_list[i]))
538
539
540 self.grid.SetCellValue(i, 1, str_to_gui(get_type(pipe_list[i])))
541
542
543 self.grid.SetCellValue(i, 2, str_to_gui(get_bundle(pipe_list[i])))
544
545
546 if pipe_list[i] == cdp_name():
547 self.grid.SetCellValue(i, 3, str_to_gui("cdp"))
548
549
550 self.grid.SetCellValue(i, 4, str_to_gui(self.gui.analysis.page_name_from_bundle(get_bundle(pipe_list[i]))))
551
552
553 for i in range(self.grid.GetNumberRows()):
554
555 self.grid.SetRowSize(i, 27)
556
557
558 for j in range(self.grid.GetNumberCols()):
559
560 self.grid.SetReadOnly(i, j)
561
562
563 status.pipe_lock.release('pipe editor window')
564
565
566 self.grid.Thaw()
567