Package gui :: Module pipe_editor
[hide private]
[frames] | no frames]

Source Code for Module gui.pipe_editor

  1  ############################################################################### 
  2  #                                                                             # 
  3  # Copyright (C) 2011-2012 Edward d'Auvergne                                   # 
  4  #                                                                             # 
  5  # This file is part of the program relax.                                     # 
  6  #                                                                             # 
  7  # relax is free software; you can redistribute it and/or modify               # 
  8  # it under the terms of the GNU General Public License as published by        # 
  9  # the Free Software Foundation; either version 2 of the License, or           # 
 10  # (at your option) any later version.                                         # 
 11  #                                                                             # 
 12  # relax is distributed in the hope that it will be useful,                    # 
 13  # but WITHOUT ANY WARRANTY; without even the implied warranty of              # 
 14  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               # 
 15  # GNU General Public License for more details.                                # 
 16  #                                                                             # 
 17  # You should have received a copy of the GNU General Public License           # 
 18  # along with relax; if not, write to the Free Software                        # 
 19  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA   # 
 20  #                                                                             # 
 21  ############################################################################### 
 22   
 23  # Module docstring. 
 24  """The pipe editor GUI element.""" 
 25   
 26  # Python module imports. 
 27  import wx 
 28   
 29  # relax module imports. 
 30  from data import Relax_data_store; ds = Relax_data_store() 
 31  from generic_fns.pipes import cdp_name, delete, get_type, pipe_names, switch 
 32  from status import Status; status = Status() 
 33   
 34  # relax GUI module imports. 
 35  from gui.components.menu import build_menu_item 
 36  from gui.fonts import font 
 37  from gui.icons import relax_icons 
 38  from gui.message import Question 
 39  from gui.misc import add_border, gui_to_str, str_to_gui 
 40  from gui.paths import icon_16x16, icon_22x22, WIZARD_IMAGE_PATH 
 41  from gui.user_functions import User_functions 
 42   
 43   
44 -class Pipe_editor(wx.Frame):
45 """The pipe editor window object.""" 46
47 - def __init__(self, gui=None, size_x=800, size_y=500, border=10):
48 """Set up the relax controller frame. 49 50 @keyword gui: The main GUI object. 51 @type gui: wx.Frame instance 52 @keyword size_x: The initial and minimum width of the window. 53 @type size_x: int 54 @keyword size_y: The initial and minimum height of the window. 55 @type size_y: int 56 @keyword border: The size of the internal border of the window. 57 @type border: int 58 """ 59 60 # Store the args. 61 self.gui = gui 62 self.border = border 63 64 # Create GUI elements 65 wx.Frame.__init__(self, None, id=-1, title="Data pipe editor") 66 67 # Initialise the user functions. 68 self.user_functions = User_functions(self) 69 70 # Set up the window icon. 71 self.SetIcons(relax_icons) 72 73 # Initialise some data. 74 self.width_col_label = 40 75 76 # Set the normal and minimum window sizes. 77 self.SetMinSize((size_x, size_y)) 78 self.SetSize((size_x, size_y)) 79 80 # Place all elements within a panel (to remove the dark grey in MS Windows). 81 self.main_panel = wx.Panel(self, -1) 82 83 # Pack a sizer into the panel. 84 main_sizer = wx.BoxSizer(wx.VERTICAL) 85 self.main_panel.SetSizer(main_sizer) 86 87 # Build the central sizer, with borders. 88 sizer = add_border(main_sizer, border=border, packing=wx.VERTICAL) 89 90 # Add the contents. 91 sizer.AddSpacer(10) 92 self.add_logo(sizer) 93 sizer.AddSpacer(20) 94 self.add_buttons(sizer) 95 sizer.AddSpacer(10) 96 self.add_table(sizer) 97 98 # Bind some events. 99 self.grid.Bind(wx.EVT_SIZE, self.resize) 100 self.Bind(wx.EVT_CLOSE, self.handler_close) 101 self.grid.Bind(wx.grid.EVT_GRID_CELL_RIGHT_CLICK, self.menu) 102 103 # Initialise the observer name. 104 self.name = 'pipe editor' 105 106 # Update the grid. 107 self.update_grid()
108 109
110 - def Show(self, show=True):
111 """Change the behaviour of showing the window to update the content. 112 113 @keyword show: A flag which is True shows the window. 114 @type show: bool 115 """ 116 117 # Update the grid. 118 self.update_grid() 119 self.activate() 120 121 # Register the grid for updating when a user function completes or when the GUI analysis tabs change. 122 status.observers.pipe_alteration.register(self.name, self.update_grid) 123 status.observers.gui_analysis.register(self.name, self.update_grid) 124 status.observers.exec_lock.register(self.name, self.activate) 125 126 # Show the window using the base class method. 127 if status.show_gui: 128 super(Pipe_editor, self).Show(show)
129 130
131 - def activate(self):
132 """Activate or deactivate certain elements in response to the execution lock.""" 133 134 # Turn off all buttons. 135 if status.exec_lock.locked(): 136 wx.CallAfter(self.button_create.Enable, False) 137 wx.CallAfter(self.button_copy.Enable, False) 138 wx.CallAfter(self.button_delete.Enable, False) 139 wx.CallAfter(self.button_hybrid.Enable, False) 140 wx.CallAfter(self.button_switch.Enable, False) 141 142 # Turn on all buttons. 143 else: 144 wx.CallAfter(self.button_create.Enable, True) 145 wx.CallAfter(self.button_copy.Enable, True) 146 wx.CallAfter(self.button_delete.Enable, True) 147 wx.CallAfter(self.button_hybrid.Enable, True) 148 wx.CallAfter(self.button_switch.Enable, True)
149 150
151 - def menu(self, event):
152 """The pop up menu. 153 154 @param event: The wx event. 155 @type event: wx event 156 """ 157 158 # Get the row. 159 row = event.GetRow() 160 161 # Get the name of the data pipe. 162 self.selected_pipe = gui_to_str(self.grid.GetCellValue(row, 0)) 163 164 # No data pipe. 165 if not self.selected_pipe: 166 return 167 168 # The pipe type. 169 pipe_type = get_type(self.selected_pipe) 170 171 # Initialise the menu. 172 menu = wx.Menu() 173 174 # Menu entry: delete the data pipe. 175 item = build_menu_item(menu, parent=self, text="&Delete the pipe", icon=icon_16x16.remove, fn=self.pipe_delete) 176 menu.AppendItem(item) 177 if status.exec_lock.locked(): 178 item.Enable(False) 179 180 # Menu entry: switch to this data pipe. 181 item = build_menu_item(menu, parent=self, text="&Switch to this pipe", icon=icon_16x16.pipe_switch, fn=self.pipe_switch) 182 menu.AppendItem(item) 183 if status.exec_lock.locked(): 184 item.Enable(False) 185 186 # Menu entry: new auto-analysis tab. 187 if self.gui.analysis.page_index_from_pipe(self.selected_pipe) == None and pipe_type in ['noe', 'r1', 'r2', 'mf']: 188 item = build_menu_item(menu, parent=self, text="&Associate with a new auto-analysis", icon=icon_16x16.new, fn=self.associate_auto) 189 menu.AppendItem(item) 190 if status.exec_lock.locked(): 191 item.Enable(False) 192 193 # Show the menu. 194 if status.show_gui: 195 self.PopupMenu(menu) 196 197 # Kill the menu once done. 198 menu.Destroy()
199 200
201 - def add_buttons(self, sizer):
202 """Add the buttons to the sizer. 203 204 @param sizer: The sizer element to pack the buttons into. 205 @type sizer: wx.Sizer instance 206 """ 207 208 # Create a horizontal layout for the buttons. 209 button_sizer = wx.BoxSizer(wx.HORIZONTAL) 210 sizer.Add(button_sizer, 0, wx.ALL|wx.EXPAND, 0) 211 212 # The create button. 213 self.button_create = wx.lib.buttons.ThemedGenBitmapTextButton(self.main_panel, -1, None, " Create") 214 self.button_create.SetBitmapLabel(wx.Bitmap(icon_22x22.add, wx.BITMAP_TYPE_ANY)) 215 self.button_create.SetFont(font.normal) 216 self.button_create.SetToolTipString("Create a new data pipe.") 217 button_sizer.Add(self.button_create, 1, wx.ALL|wx.EXPAND, 0) 218 self.Bind(wx.EVT_BUTTON, self.uf_launch, self.button_create) 219 220 # The copy button. 221 self.button_copy = wx.lib.buttons.ThemedGenBitmapTextButton(self.main_panel, -1, None, " Copy") 222 self.button_copy.SetBitmapLabel(wx.Bitmap(icon_22x22.copy, wx.BITMAP_TYPE_ANY)) 223 self.button_copy.SetFont(font.normal) 224 self.button_copy.SetToolTipString("Copy a data pipe.") 225 button_sizer.Add(self.button_copy, 1, wx.ALL|wx.EXPAND, 0) 226 self.Bind(wx.EVT_BUTTON, self.uf_launch, self.button_copy) 227 228 # The delete button. 229 self.button_delete = wx.lib.buttons.ThemedGenBitmapTextButton(self.main_panel, -1, None, " Delete") 230 self.button_delete.SetBitmapLabel(wx.Bitmap(icon_22x22.list_remove, wx.BITMAP_TYPE_ANY)) 231 self.button_delete.SetFont(font.normal) 232 self.button_delete.SetToolTipString("Delete a data pipe.") 233 button_sizer.Add(self.button_delete, 1, wx.ALL|wx.EXPAND, 0) 234 self.Bind(wx.EVT_BUTTON, self.uf_launch, self.button_delete) 235 236 # The hybridise button. 237 self.button_hybrid = wx.lib.buttons.ThemedGenBitmapTextButton(self.main_panel, -1, None, " Hybridise") 238 self.button_hybrid.SetBitmapLabel(wx.Bitmap(icon_22x22.pipe_hybrid, wx.BITMAP_TYPE_ANY)) 239 self.button_hybrid.SetFont(font.normal) 240 self.button_hybrid.SetToolTipString("Hybridise data pipes.") 241 button_sizer.Add(self.button_hybrid, 1, wx.ALL|wx.EXPAND, 0) 242 self.Bind(wx.EVT_BUTTON, self.uf_launch, self.button_hybrid) 243 244 # The switch button. 245 self.button_switch = wx.lib.buttons.ThemedGenBitmapTextButton(self.main_panel, -1, None, " Switch") 246 self.button_switch.SetBitmapLabel(wx.Bitmap(icon_22x22.pipe_switch, wx.BITMAP_TYPE_ANY)) 247 self.button_switch.SetFont(font.normal) 248 self.button_switch.SetToolTipString("Switch data pipes.") 249 button_sizer.Add(self.button_switch, 1, wx.ALL|wx.EXPAND, 0) 250 self.Bind(wx.EVT_BUTTON, self.uf_launch, self.button_switch)
251 252
253 - def uf_launch(self, event):
254 """Launch the user function GUI wizards. 255 256 @param event: The wx event. 257 @type event: wx event 258 """ 259 260 # Launch the respective user functions. 261 if event.GetEventObject() == self.button_create: 262 self.user_functions.pipe.create() 263 elif event.GetEventObject() == self.button_copy: 264 self.user_functions.pipe.copy() 265 elif event.GetEventObject() == self.button_delete: 266 self.user_functions.pipe.delete() 267 elif event.GetEventObject() == self.button_hybrid: 268 self.user_functions.pipe.hybrid() 269 elif event.GetEventObject() == self.button_switch: 270 self.user_functions.pipe.switch()
271 272
273 - def add_logo(self, box):
274 """Add the logo to the sizer. 275 276 @param box: The sizer element to pack the logo into. 277 @type box: wx.Sizer instance 278 """ 279 280 # The pipe logo. 281 logo = wx.StaticBitmap(self.main_panel, -1, wx.Bitmap(WIZARD_IMAGE_PATH+'pipe_200x90.png', wx.BITMAP_TYPE_ANY)) 282 283 # Pack the logo. 284 box.Add(logo, 0, wx.ALIGN_CENTER_HORIZONTAL, 0)
285 286
287 - def add_table(self, sizer):
288 """Add the table to the sizer. 289 290 @param sizer: The sizer element to pack the table into. 291 @type sizer: wx.Sizer instance 292 """ 293 294 # Grid of all data pipes. 295 self.grid = wx.grid.Grid(self.main_panel, -1) 296 297 # Initialise to a single row and 4 columns. 298 self.grid.CreateGrid(1, 4) 299 300 # Set the headers. 301 self.grid.SetColLabelValue(0, "Data pipe") 302 self.grid.SetColLabelValue(1, "Type") 303 self.grid.SetColLabelValue(2, "Current") 304 self.grid.SetColLabelValue(3, "Analysis tab") 305 306 # Properties. 307 self.grid.SetDefaultCellFont(font.normal) 308 self.grid.SetLabelFont(font.normal_bold) 309 310 # Set the row label widths. 311 self.grid.SetRowLabelSize(self.width_col_label) 312 313 # No cell resizing allowed. 314 self.grid.EnableDragColSize(False) 315 self.grid.EnableDragRowSize(False) 316 317 # Add grid to sizer. 318 sizer.Add(self.grid, 1, wx.ALL|wx.EXPAND, 0)
319 320
321 - def associate_auto(self, event):
322 """Associate the selected data pipe with a new auto-analysis. 323 324 @param event: The wx event. 325 @type event: wx event 326 """ 327 328 # Initialise the GUI data store object if needed. 329 if not hasattr(ds, 'relax_gui'): 330 self.gui.init_data() 331 332 # The type. 333 type = get_type(self.selected_pipe) 334 335 # The name. 336 names = { 337 'noe': 'Steady-state NOE', 338 'r1': 'R1 relaxation', 339 'r2': 'R2 relaxation', 340 'mf': 'Model-free' 341 } 342 343 # Create a new analysis with the selected data pipe. 344 self.gui.analysis.new_analysis(analysis_type=type, analysis_name=names[type], pipe_name=self.selected_pipe)
345 346
347 - def handler_close(self, event):
348 """Event handler for the close window action. 349 350 @param event: The wx event. 351 @type event: wx event 352 """ 353 354 # Unregister the methods from the observers to avoid unnecessary updating. 355 status.observers.pipe_alteration.unregister(self.name) 356 status.observers.gui_analysis.unregister(self.name) 357 status.observers.exec_lock.unregister(self.name) 358 359 # Close the window. 360 self.Hide()
361 362
363 - def pipe_delete(self, event):
364 """Delete the date pipe. 365 366 @param event: The wx event. 367 @type event: wx event 368 """ 369 370 # Ask if this should be done. 371 msg = "Are you sure you would like to delete the '%s' data pipe? This operation cannot be undone." % self.selected_pipe 372 if status.show_gui and Question(msg, parent=self, default=False).ShowModal() == wx.ID_NO: 373 return 374 375 # Delete the data pipe. 376 delete(self.selected_pipe)
377 378
379 - def pipe_switch(self, event):
380 """Switch to the selected date pipe. 381 382 @param event: The wx event. 383 @type event: wx event 384 """ 385 386 # Switch to the selected data pipe. 387 switch(self.selected_pipe) 388 389 # Bug fix for MS Windows. 390 wx.CallAfter(self.Raise)
391 392
393 - def resize(self, event):
394 """Catch the resize to allow the grid to be resized. 395 396 @param event: The wx event. 397 @type event: wx event 398 """ 399 400 # Set the column sizes. 401 self.size_cols() 402 403 # Continue with the normal resizing. 404 event.Skip()
405 406
407 - def size_cols(self):
408 """Set the column sizes.""" 409 410 # The grid size. 411 x, y = self.grid.GetSize() 412 413 # Number of columns. 414 n = 4 415 416 # The width of the current data pipe column. 417 width_col_curr = 80 418 419 # Set to equal sizes. 420 width = int((x - self.width_col_label - width_col_curr) / (n - 1)) 421 422 # Set the column sizes. 423 for i in range(n): 424 # The cdp column. 425 if i == 2: 426 self.grid.SetColSize(i, width_col_curr) 427 428 # All others. 429 else: 430 self.grid.SetColSize(i, width)
431 432
433 - def update_grid(self):
434 """Update the grid in a thread safe way using wx.CallAfter.""" 435 436 # Thread safe. 437 wx.CallAfter(self.update_grid_safe) 438 439 # Flush the events. 440 wx.GetApp().Yield(True)
441 442
443 - def update_grid_safe(self):
444 """Update the grid with the pipe data.""" 445 446 # First freeze the grid, so that the GUI element doesn't update until the end. 447 self.grid.Freeze() 448 449 # Acquire the pipe lock. 450 status.pipe_lock.acquire('pipe editor window') 451 452 # Delete the rows, leaving a single row. 453 self.grid.DeleteRows(numRows=self.grid.GetNumberRows()-1) 454 455 # Clear the contents of the first row. 456 for i in range(self.grid.GetNumberCols()): 457 self.grid.SetCellValue(0, i, str_to_gui("")) 458 459 # The data pipes. 460 pipe_list = pipe_names() 461 n = len(pipe_list) 462 463 # Append the appropriate number of rows. 464 if n >= 1: 465 self.grid.AppendRows(numRows=n-1) 466 467 # Loop over the data pipes. 468 for i in range(n): 469 # Set the pipe name. 470 self.grid.SetCellValue(i, 0, str_to_gui(pipe_list[i])) 471 472 # Set the pipe type. 473 self.grid.SetCellValue(i, 1, str_to_gui(get_type(pipe_list[i]))) 474 475 # Set the current pipe. 476 if pipe_list[i] == cdp_name(): 477 self.grid.SetCellValue(i, 2, str_to_gui("cdp")) 478 479 # Set the tab the pipe belongs to. 480 self.grid.SetCellValue(i, 3, str_to_gui(self.gui.analysis.page_name_from_pipe(pipe_list[i]))) 481 482 # Set the grid properties once finalised. 483 for i in range(self.grid.GetNumberRows()): 484 # Row properties. 485 self.grid.SetRowSize(i, 27) 486 487 # Loop over the columns. 488 for j in range(self.grid.GetNumberCols()): 489 # Cell properties. 490 self.grid.SetReadOnly(i, j) 491 492 # Release the lock. 493 status.pipe_lock.release('pipe editor window') 494 495 # Unfreeze. 496 self.grid.Thaw()
497