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

Source Code for Module gui.wizard

   1  ############################################################################### 
   2  #                                                                             # 
   3  # Copyright (C) 2010-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  """Base class module for the wizard GUI elements.""" 
  25   
  26  # Python module imports. 
  27  import wx 
  28  from wx.lib import buttons, scrolledpanel 
  29   
  30  # relax module imports. 
  31  from data import Relax_data_store; ds = Relax_data_store() 
  32  from generic_fns.mol_res_spin import id_string_doc 
  33  from relax_errors import RelaxImplementError 
  34  from status import Status; status = Status() 
  35   
  36  # relax GUI module imports. 
  37  from gui.interpreter import Interpreter; interpreter = Interpreter() 
  38  from gui.filedialog import RelaxFileDialog 
  39  from gui.fonts import font 
  40  from gui.icons import relax_icons 
  41  from gui.misc import add_border, bool_to_gui, gui_to_int, gui_to_str, int_to_gui, open_file, protected_exec, str_to_gui 
  42  from gui.message import Question 
  43  from gui import paths 
  44  from gui.wizard_elements import Integer, String, String_list, String_list_of_lists 
  45   
  46   
47 -class Wiz_page(wx.Panel):
48 """The wizard pages to be placed inside the wizard. 49 50 To inherit from this class, you must minimally supply the add_contents() method. This method should build the specific GUI elements. The following methods are also designed to be overwritten: 51 52 - add_artwork(), this builds the left hand artwork section of the page. 53 - add_contents(), this builds the right hand section of the page. 54 - on_display(), this is executed when the page is displayed. 55 - on_display_post(), this is executed when the page is displayed, directly after the on_display method. 56 - on_execute(), this is executed when the wizard is terminated or the apply button is hit. 57 - on_next(), this is executed when moving to the next wizard page. 58 59 The following methods can be used by add_contents() to create standard GUI elements: 60 61 - chooser() 62 - combo_box() 63 - file_selection() 64 - free_file_format() 65 - input_field() 66 - text() 67 68 These are described in full detail in their docstrings. 69 """ 70 71 # Some class variables. 72 art_spacing = 20 73 divider = None 74 height_desc = 220 75 height_element = 27 76 image_path = paths.IMAGE_PATH + "relax.gif" 77 main_text = '' 78 setup_fail = False 79 size_button = (100, 33) 80 size_square_button = (33, 33) 81 title = '' 82
83 - def __init__(self, parent):
84 """Set up the window. 85 86 @param parent: The parent GUI element. 87 @type parent: wx.object instance 88 """ 89 90 # Store the args. 91 self.parent = parent 92 93 # Execute the base class method. 94 wx.Panel.__init__(self, parent, id=-1) 95 96 # Initilise some variables. 97 self.exec_status = False 98 99 # The wizard GUI element storage. 100 self._elements = {} 101 102 # Pack a sizer into the panel. 103 box_main = wx.BoxSizer(wx.HORIZONTAL) 104 self.SetSizer(box_main) 105 106 # Add the artwork. 107 self.add_artwork(box_main) 108 109 # The size of the image. 110 image_x, image_y = self.image.GetSize() 111 112 # Calculate the size of the main section, and the subdivisions. 113 self._main_size = parent._size_x - image_x - self.art_spacing - 2*parent._border 114 if self.divider: 115 self._div_left = self.divider 116 self._div_right = self._main_size - self.divider 117 else: 118 self._div_left = self._div_right = self._main_size / 2 119 120 # Add the main sizer. 121 main_sizer = self._build_main_section(box_main) 122 123 # Add the title. 124 self._add_title(main_sizer) 125 126 # Add the description. 127 self.add_desc(main_sizer, max_y=self.height_desc) 128 129 # Add the specific GUI elements (bounded by spacers). 130 main_sizer.AddStretchSpacer() 131 self.add_contents(main_sizer)
132 133
134 - def _add_title(self, sizer):
135 """Add the title to the dialog. 136 137 @param sizer: A sizer object. 138 @type sizer: wx.Sizer instance 139 """ 140 141 # Spacing. 142 sizer.AddSpacer(10) 143 144 # The text. 145 title = wx.StaticText(self, -1, self.title) 146 147 # Font. 148 title.SetFont(font.title) 149 150 # Add the title. 151 sizer.Add(title, 0, wx.ALIGN_CENTRE|wx.ALL, 0) 152 153 # Spacing. 154 sizer.AddSpacer(10)
155 156
157 - def _apply(self, event):
158 """Apply the operation. 159 160 @param event: The wx event. 161 @type event: wx event 162 """ 163 164 # Execute. 165 self.exec_status = protected_exec(self.on_execute) 166 167 # Execution failure. 168 if not self.exec_status: 169 return 170 171 # Finished. 172 self.on_completion() 173 174 # Execute the on_apply() method. 175 self.on_apply()
176 177
178 - def _build_main_section(self, sizer):
179 """Add the main part of the dialog. 180 181 @param sizer: A sizer object. 182 @type sizer: wx.Sizer instance 183 @return: The sizer object for the main part of the dialog. 184 @rtype: wx.Sizer instance 185 """ 186 187 # Use a grid sizer for packing the elements. 188 main_sizer = wx.BoxSizer(wx.VERTICAL) 189 190 # Pack the sizer. 191 sizer.Add(main_sizer, 1, wx.EXPAND|wx.ALL, 0) 192 193 # Return the sizer. 194 return main_sizer
195 196
197 - def _free_file_format_reset(self, event):
198 """Reset the free file format widget contents to the original values. 199 200 @param event: The wx event. 201 @type event: wx event 202 """ 203 204 # Ask a question. 205 if status.show_gui and Question('Would you really like to reset the free file format settings?', parent=self).ShowModal() == wx.ID_NO: 206 return 207 208 # First reset. 209 ds.relax_gui.free_file_format.reset() 210 211 # Then update the values. 212 self._free_file_format_set_vals()
213 214
215 - def _free_file_format_save(self, event):
216 """Save the free file format widget contents into the relax data store. 217 218 @param event: The wx event. 219 @type event: wx event 220 """ 221 222 # Get the column numbers. 223 ds.relax_gui.free_file_format.spin_id_col = gui_to_int(self.spin_id_col.GetValue()) 224 ds.relax_gui.free_file_format.mol_name_col = gui_to_int(self.mol_name_col.GetValue()) 225 ds.relax_gui.free_file_format.res_num_col = gui_to_int(self.res_num_col.GetValue()) 226 ds.relax_gui.free_file_format.res_name_col = gui_to_int(self.res_name_col.GetValue()) 227 ds.relax_gui.free_file_format.spin_num_col = gui_to_int(self.spin_num_col.GetValue()) 228 ds.relax_gui.free_file_format.spin_name_col = gui_to_int(self.spin_name_col.GetValue()) 229 230 # The data and error. 231 if hasattr(self, 'data_col'): 232 ds.relax_gui.free_file_format.data_col = gui_to_int(self.data_col.GetValue()) 233 if hasattr(self, 'err_col'): 234 ds.relax_gui.free_file_format.err_col = gui_to_int(self.err_col.GetValue()) 235 236 # The column separator. 237 ds.relax_gui.free_file_format.sep = str(self.sep.GetValue()) 238 if ds.relax_gui.free_file_format.sep == 'white space': 239 ds.relax_gui.free_file_format.sep = None
240 241
242 - def _free_file_format_set_vals(self):
243 """Set the free file format widget contents to the values from the relax data store.""" 244 245 # The column numbers. 246 self.spin_id_col.SetValue(int_to_gui(ds.relax_gui.free_file_format.spin_id_col)) 247 self.mol_name_col.SetValue(int_to_gui(ds.relax_gui.free_file_format.mol_name_col)) 248 self.res_num_col.SetValue(int_to_gui(ds.relax_gui.free_file_format.res_num_col)) 249 self.res_name_col.SetValue(int_to_gui(ds.relax_gui.free_file_format.res_name_col)) 250 self.spin_num_col.SetValue(int_to_gui(ds.relax_gui.free_file_format.spin_num_col)) 251 self.spin_name_col.SetValue(int_to_gui(ds.relax_gui.free_file_format.spin_name_col)) 252 if hasattr(self, 'data_col'): 253 self.data_col.SetValue(int_to_gui(ds.relax_gui.free_file_format.data_col)) 254 if hasattr(self, 'err_col'): 255 self.err_col.SetValue(int_to_gui(ds.relax_gui.free_file_format.err_col)) 256 257 # The column separator. 258 if not ds.relax_gui.free_file_format.sep: 259 self.sep.SetValue(str_to_gui("white space")) 260 else: 261 self.sep.SetValue(str_to_gui(ds.relax_gui.free_file_format.sep))
262 263
264 - def Clear(self, key):
265 """Special wizard method for clearing the value of the GUI element corresponding to the key. 266 267 @param key: The key corresponding to the desired GUI element. 268 @type key: str 269 """ 270 271 # Call the element's method. 272 self._elements[key].Clear()
273 274
275 - def GetValue(self, key):
276 """Special wizard method for getting the value of the GUI element corresponding to the key. 277 278 @param key: The key corresponding to the desired GUI element. 279 @type key: str 280 @return: The value that the specific GUI element's GetValue() method returns. 281 @rtype: unknown 282 """ 283 284 # Call the element's method. 285 return self._elements[key].GetValue()
286 287
288 - def ResetChoices(self, key, combo_choices=None, combo_data=None, combo_default=None):
289 """Special wizard method for resetting the list of choices in a ComboBox type element. 290 291 @param key: The key corresponding to the desired GUI element. 292 @type key: str 293 @keyword combo_choices: The list of choices to present to the user. This is only used if the element_type is set to 'combo'. 294 @type combo_choices: list of str 295 @keyword combo_data: The data returned by a call to GetValue(). This is only used if the element_type is set to 'combo'. If supplied, it should be the same length at the combo_choices list. If not supplied, the combo_choices list will be used for the returned data. 296 @type combo_data: list 297 @keyword combo_default: The default value of the ComboBox. This is only used if the element_type is set to 'combo'. 298 @type combo_default: str or None 299 """ 300 301 # Call the element's method. 302 self._elements[key].ResetChoices(combo_choices=combo_choices, combo_data=combo_data, combo_default=combo_default)
303 304
305 - def SetValue(self, key, value):
306 """Special wizard method for setting the value of the GUI element corresponding to the key. 307 308 @param key: The key corresponding to the desired GUI element. 309 @type key: str 310 @param value: The value that the specific GUI element's SetValue() method expects. 311 @type value: unknown 312 """ 313 314 # Call the element's method. 315 self._elements[key].SetValue(value)
316 317
318 - def add_artwork(self, sizer):
319 """Add the artwork to the dialog. 320 321 @param sizer: A sizer object. 322 @type sizer: wx.Sizer instance 323 """ 324 325 # Add the graphics. 326 if self.image_path: 327 self.image = wx.StaticBitmap(self, -1, wx.Bitmap(self.image_path, wx.BITMAP_TYPE_ANY)) 328 sizer.Add(self.image, 0, wx.TOP|wx.ALIGN_CENTER_HORIZONTAL, 0) 329 330 # A spacer. 331 sizer.AddSpacer(self.art_spacing)
332 333
334 - def add_contents(self, sizer):
335 """Add the specific GUI elements (dummy method). 336 337 @param sizer: A sizer object. 338 @type sizer: wx.Sizer instance 339 """ 340 341 # This must be supplied. 342 raise RelaxImplementError
343 344
345 - def add_desc(self, sizer, max_y=220):
346 """Add the description to the dialog. 347 348 @param sizer: A sizer object. 349 @type sizer: wx.Sizer instance 350 @keyword max_y: The maximum height, in number of pixels, for the description. 351 @type max_y: int 352 """ 353 354 # A line with spacing. 355 sizer.AddSpacer(5) 356 sizer.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.ALL, 0) 357 sizer.AddSpacer(5) 358 359 # Create a scrolled panel. 360 panel = scrolledpanel.ScrolledPanel(self, -1, name="desc") 361 362 # A sizer for the panel. 363 panel_sizer = wx.BoxSizer(wx.VERTICAL) 364 365 # The text. 366 text = wx.StaticText(panel, -1, self.main_text, style=wx.TE_MULTILINE) 367 text.SetFont(font.normal) 368 369 # Wrap the text. 370 text.Wrap(self._main_size - 20) 371 372 # The text size. 373 x, y = text.GetSizeTuple() 374 375 # Scrolling needed. 376 if y > max_y-10: 377 # Set the panel size. 378 panel.SetInitialSize((self._main_size, max_y)) 379 380 # No scrolling. 381 else: 382 # Rewrap the text. 383 text.Wrap(self._main_size) 384 385 # Set the panel size. 386 panel.SetInitialSize(text.GetSize()) 387 388 # Add the text. 389 panel_sizer.Add(text, 0, wx.ALIGN_LEFT, 0) 390 391 # Set up and add the panel to the sizer. 392 panel.SetSizer(panel_sizer) 393 panel.SetAutoLayout(1) 394 panel.SetupScrolling(scroll_x=False, scroll_y=True) 395 sizer.Add(panel, 0, wx.ALL|wx.EXPAND) 396 397 # A line with spacing. 398 sizer.AddSpacer(5) 399 sizer.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.ALL, 0) 400 sizer.AddSpacer(5)
401 402
403 - def boolean_selector(self, sizer, desc, tooltip=None, divider=None, padding=0, spacer=None, default=True):
404 """Build the boolean selector widget for selecting between True and False. 405 406 @param sizer: The sizer to put the combo box widget into. 407 @type sizer: wx.Sizer instance 408 @param desc: The text description. 409 @type desc: str 410 @keyword tooltip: The tooltip which appears on hovering over the text or input field. 411 @type tooltip: str 412 @keyword divider: The optional position of the divider. If None, the class variable _div_left will be used. 413 @type divider: None or int 414 @keyword padding: Spacing to the left and right of the widgets. 415 @type padding: int 416 @keyword spacer: The amount of spacing to add below the field in pixels. If None, a stretchable spacer will be used. 417 @type spacer: None or int 418 @keyword default: The default boolean value. 419 @type default: bool 420 """ 421 422 # Init. 423 sub_sizer = wx.BoxSizer(wx.HORIZONTAL) 424 425 # Left padding. 426 sub_sizer.AddSpacer(padding) 427 428 # The description. 429 text = wx.StaticText(self, -1, desc, style=wx.ALIGN_LEFT) 430 text.SetFont(font.normal) 431 sub_sizer.Add(text, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, 0) 432 433 # The divider. 434 if not divider: 435 divider = self._div_left 436 437 # Spacing. 438 x, y = text.GetSize() 439 sub_sizer.AddSpacer((divider - x, 0)) 440 441 # The combo box element. 442 style = wx.CB_DROPDOWN | wx.CB_READONLY 443 combo = wx.ComboBox(self, -1, value=bool_to_gui(default), style=style, choices=['True', 'False']) 444 combo.SetMinSize((50, self.height_element)) 445 combo.SetFont(font.normal) 446 sub_sizer.Add(combo, 1, wx.ADJUST_MINSIZE|wx.ALIGN_CENTER_VERTICAL, 0) 447 448 # Right padding. 449 sub_sizer.AddSpacer(padding) 450 451 # Add to the main sizer. 452 sizer.Add(sub_sizer, 1, wx.EXPAND|wx.ALL, 0) 453 454 # Spacing below the widget. 455 if spacer == None: 456 sizer.AddStretchSpacer() 457 else: 458 sizer.AddSpacer(spacer) 459 460 # Tooltip. 461 if tooltip: 462 text.SetToolTipString(tooltip) 463 combo.SetToolTipString(tooltip) 464 465 # Return the combo box element. 466 return combo
467 468
469 - def chooser(self, sizer, desc, func, choices):
470 """Build the choice element. 471 472 @param sizer: The sizer to put the input field into. 473 @type sizer: wx.Sizer instance 474 @param desc: The text description. 475 @type desc: str 476 @param func: The function to bind the event to 477 @type func: func 478 @param choices: The list of choices. 479 @type choices: list of str 480 """ 481 482 # Init. 483 sub_sizer = wx.BoxSizer(wx.HORIZONTAL) 484 485 # The description. 486 text = wx.StaticText(self, -1, desc, style=wx.ALIGN_LEFT) 487 text.SetFont(font.normal) 488 sub_sizer.Add(text, 1, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, 0) 489 490 # Spacing. 491 sub_sizer.AddSpacer(10) 492 493 # The choice element. 494 type_choice = wx.Choice(self, -1, style=wx.ALIGN_LEFT, choices=choices) 495 sub_sizer.Add(type_choice, 1, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, 0) 496 type_choice.SetFont(font.normal) 497 self.Bind(wx.EVT_CHOICE, func, type_choice) 498 499 # Add to the main sizer (followed by stretchable spacing). 500 sizer.Add(sub_sizer) 501 sizer.AddStretchSpacer()
502 503
504 - def combo_box(self, sizer, desc, choices=[], evt_fn=None, tooltip=None, divider=None, padding=0, spacer=None, read_only=True):
505 """Build the combo box widget for list selections. 506 507 @param sizer: The sizer to put the combo box widget into. 508 @type sizer: wx.Sizer instance 509 @param desc: The text description. 510 @type desc: str 511 @param choices: The list of choices. 512 @type choices: list of str 513 @param evt_fn: The event handling function. 514 @type evt_fn: func 515 @keyword tooltip: The tooltip which appears on hovering over the text or input field. 516 @type tooltip: str 517 @keyword divider: The optional position of the divider. If None, the class variable _div_left will be used. 518 @type divider: None or int 519 @keyword padding: Spacing to the left and right of the widgets. 520 @type padding: int 521 @keyword spacer: The amount of spacing to add below the field in pixels. If None, a stretchable spacer will be used. 522 @type spacer: None or int 523 @keyword read_only: A flag which if True means that text cannot be typed into the combo box widget. 524 @type read_only: bool 525 @return: The combo box object. 526 @rtype: wx.ComboBox instance 527 """ 528 529 # Init. 530 sub_sizer = wx.BoxSizer(wx.HORIZONTAL) 531 532 # Left padding. 533 sub_sizer.AddSpacer(padding) 534 535 # The description. 536 text = wx.StaticText(self, -1, desc, style=wx.ALIGN_LEFT) 537 text.SetFont(font.normal) 538 sub_sizer.Add(text, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, 0) 539 540 # The divider. 541 if not divider: 542 divider = self._div_left 543 544 # Spacing. 545 x, y = text.GetSize() 546 sub_sizer.AddSpacer((divider - x, 0)) 547 548 # The combo box element. 549 style = wx.CB_DROPDOWN 550 if read_only: 551 style = style | wx.CB_READONLY 552 combo = wx.ComboBox(self, -1, value='', style=style, choices=choices) 553 combo.SetMinSize((50, self.height_element)) 554 combo.SetFont(font.normal) 555 sub_sizer.Add(combo, 1, wx.ADJUST_MINSIZE|wx.ALIGN_CENTER_VERTICAL, 0) 556 557 # Right padding. 558 sub_sizer.AddSpacer(padding) 559 560 # Add to the main sizer. 561 sizer.Add(sub_sizer, 1, wx.EXPAND|wx.ALL, 0) 562 563 # Spacing below the widget. 564 if spacer == None: 565 sizer.AddStretchSpacer() 566 else: 567 sizer.AddSpacer(spacer) 568 569 # Bind events. 570 if evt_fn: 571 self.Bind(wx.EVT_COMBOBOX, evt_fn, combo) 572 573 # Tooltip. 574 if tooltip: 575 text.SetToolTipString(tooltip) 576 combo.SetToolTipString(tooltip) 577 578 # Return the combo box element. 579 return combo
580 581
582 - def element_int(self, key=None, sizer=None, desc=None, tooltip=None, divider=None, padding=0, spacer=None):
583 """Set up the integer element and store it. 584 585 @keyword key: The dictionary key to store the element with. 586 @type key: str 587 @keyword sizer: The sizer to put the input field widget into. 588 @type sizer: wx.Sizer instance 589 @keyword desc: The text description. 590 @type desc: str 591 @keyword tooltip: The tooltip which appears on hovering over the text or input field. 592 @type tooltip: str 593 @keyword divider: The optional position of the divider. If None, the class variable _div_left will be used. 594 @type divider: None or int 595 @keyword padding: Spacing to the left and right of the widgets. 596 @type padding: int 597 @keyword spacer: The amount of spacing to add below the field in pixels. If None, a stretchable spacer will be used. 598 @type spacer: None or int 599 """ 600 601 # Create the element. 602 element = Integer(name=key, parent=self, sizer=sizer, desc=desc, tooltip=tooltip, divider=divider, padding=padding, spacer=spacer) 603 604 # Store it. 605 self._elements[key] = element
606 607
608 - def element_string(self, key=None, element_type='text', sizer=None, desc=None, combo_choices=None, combo_data=None, combo_default=None, tooltip=None, divider=None, padding=0, spacer=None, read_only=False):
609 """Set up the string element and store it. 610 611 @keyword key: The dictionary key to store the element with. 612 @type key: str 613 @keyword element_type: The type of GUI element to create. If set to 'text', a wx.TextCtrl element will be used. If set to 'combo', a wx.ComboBox element will be used. 614 @type element_type: str 615 @keyword sizer: The sizer to put the input field widget into. 616 @type sizer: wx.Sizer instance 617 @keyword desc: The text description. 618 @type desc: str 619 @keyword combo_choices: The list of choices to present to the user. This is only used if the element_type is set to 'combo'. 620 @type combo_choices: list of str 621 @keyword combo_data: The data returned by a call to GetValue(). This is only used if the element_type is set to 'combo'. If supplied, it should be the same length at the combo_choices list. If not supplied, the combo_choices list will be used for the returned data. 622 @type combo_data: list 623 @keyword combo_default: The default value of the ComboBox. This is only used if the element_type is set to 'combo'. 624 @type combo_default: str or None 625 @keyword tooltip: The tooltip which appears on hovering over the text or input field. 626 @type tooltip: str 627 @keyword divider: The optional position of the divider. If None, the class variable _div_left will be used. 628 @type divider: None or int 629 @keyword padding: Spacing to the left and right of the widgets. 630 @type padding: int 631 @keyword spacer: The amount of spacing to add below the field in pixels. If None, a stretchable spacer will be used. 632 @type spacer: None or int 633 @keyword read_only: A flag which if True means that the text of the element cannot be edited. 634 @type read_only: bool 635 """ 636 637 # Create the element. 638 element = String(name=key, parent=self, element_type=element_type, sizer=sizer, desc=desc, combo_choices=combo_choices, combo_data=combo_data, combo_default=combo_default, tooltip=tooltip, divider=divider, padding=padding, spacer=spacer, read_only=read_only) 639 640 # Store it. 641 self._elements[key] = element
642 643
644 - def element_string_list(self, key=None, element_type='default', sizer=None, desc=None, combo_choices=None, combo_data=None, combo_default=None, combo_list_size=None, tooltip=None, divider=None, padding=0, spacer=None):
645 """Set up the element and store it. 646 647 @keyword key: The dictionary key to store the element with. 648 @type key: str 649 @keyword element_type: The type of GUI element to create. If set to 'default', the wx.TextCtrl element with a button to bring up a dialog with ListCtrl will be used. If set to 'combo_list', the special gui.components.combo_list.Combo_list element will be used. 650 @type element_type: str 651 @keyword sizer: The sizer to put the input field widget into. 652 @type sizer: wx.Sizer instance 653 @keyword desc: The text description. 654 @type desc: str 655 @keyword combo_choices: The list of choices to present to the user. This is only used if the element_type is set to 'combo'. 656 @type combo_choices: list of str 657 @keyword combo_data: The data returned by a call to GetValue(). This is only used if the element_type is set to 'combo'. If supplied, it should be the same length at the combo_choices list. If not supplied, the combo_choices list will be used for the returned data. 658 @type combo_data: list 659 @keyword combo_default: The default value of the ComboBox. This is only used if the element_type is set to 'combo'. 660 @type combo_default: str or None 661 @keyword combo_list_size: The number of initial entries in a Combo_list object. 662 @type combo_list_size: int or None 663 @keyword tooltip: The tooltip which appears on hovering over the text or input field. 664 @type tooltip: str 665 @keyword divider: The optional position of the divider. If None, the class variable _div_left will be used. 666 @type divider: None or int 667 @keyword padding: Spacing to the left and right of the widgets. 668 @type padding: int 669 @keyword spacer: The amount of spacing to add below the field in pixels. If None, a stretchable spacer will be used. 670 @type spacer: None or int 671 """ 672 673 # Create the element. 674 element = String_list(name=key, element_type=element_type, parent=self, sizer=sizer, desc=desc, combo_choices=combo_choices, combo_data=combo_data, combo_default=combo_default, combo_list_size=combo_list_size, tooltip=tooltip, divider=divider, padding=padding, spacer=spacer) 675 676 # Store it. 677 self._elements[key] = element
678 679
680 - def element_string_list_of_lists(self, key=None, titles=None, sizer=None, desc=None, tooltip=None, divider=None, padding=0, spacer=None):
681 """Set up the element and store it. 682 683 @keyword key: The dictionary key to store the element with. 684 @type key: str 685 @keyword titles: The titles of each of the elements of the fixed width second dimension. 686 @type titles: list of str 687 @keyword sizer: The sizer to put the input field widget into. 688 @type sizer: wx.Sizer instance 689 @keyword desc: The text description. 690 @type desc: str 691 @keyword tooltip: The tooltip which appears on hovering over the text or input field. 692 @type tooltip: str 693 @keyword divider: The optional position of the divider. If None, the class variable _div_left will be used. 694 @type divider: None or int 695 @keyword padding: Spacing to the left and right of the widgets. 696 @type padding: int 697 @keyword spacer: The amount of spacing to add below the field in pixels. If None, a stretchable spacer will be used. 698 @type spacer: None or int 699 """ 700 701 # Create the element. 702 element = String_list_of_lists(name=key, titles=titles, parent=self, sizer=sizer, desc=desc, tooltip=tooltip, divider=divider, padding=padding, spacer=spacer) 703 704 # Store it. 705 self._elements[key] = element
706 707
708 - def file_selection(self, sizer, desc, message='File selection', wildcard=wx.FileSelectorDefaultWildcardStr, style=wx.FD_DEFAULT_STYLE, tooltip=None, divider=None, padding=0, spacer=None, preview=True):
709 """Build the file selection element. 710 711 @param sizer: The sizer to put the input field into. 712 @type sizer: wx.Sizer instance 713 @param desc: The text description. 714 @type desc: str 715 @keyword message: The file selector prompt string. 716 @type message: String 717 @keyword wildcard: The file wildcard pattern. For example for opening PDB files, this could be "PDB files (*.pdb)|*.pdb;*.PDB". 718 @type wildcard: String 719 @keyword style: The dialog style. To open a single file, set to wx.FD_OPEN. To open multiple files, set to wx.FD_OPEN|wx.FD_MULTIPLE. To save a single file, set to wx.FD_SAVE. To save multiple files, set to wx.FD_SAVE|wx.FD_MULTIPLE. 720 @type style: long 721 @keyword tooltip: The tooltip which appears on hovering over all the GUI elements. 722 @type tooltip: str 723 @keyword divider: The optional position of the divider. If None, the class variable _div_left will be used. 724 @type divider: None or int 725 @keyword padding: Spacing to the left and right of the widgets. 726 @type padding: int 727 @keyword spacer: The amount of spacing to add below the field in pixels. If None, a stretchable spacer will be used. 728 @type spacer: None or int 729 @keyword preview: A flag which if true will allow the file to be previewed. 730 @type preview: bool 731 @return: The file selection GUI element. 732 @rtype: wx.TextCtrl 733 """ 734 735 # Init. 736 sub_sizer = wx.BoxSizer(wx.HORIZONTAL) 737 738 # Left padding. 739 sub_sizer.AddSpacer(padding) 740 741 # The description. 742 text = wx.StaticText(self, -1, desc, style=wx.ALIGN_LEFT) 743 text.SetFont(font.normal) 744 sub_sizer.Add(text, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, 0) 745 746 # The divider. 747 if not divider: 748 divider = self._div_left 749 750 # Spacing. 751 x, y = text.GetSize() 752 sub_sizer.AddSpacer((divider - x, 0)) 753 754 # The input field. 755 if not hasattr(self, 'file_selection_field'): 756 self.file_selection_field = [] 757 self.file_selection_field.append(wx.TextCtrl(self, -1, '')) 758 field = self.file_selection_field[-1] 759 field.SetMinSize((-1, self.height_element)) 760 field.SetFont(font.normal) 761 sub_sizer.Add(field, 1, wx.ADJUST_MINSIZE|wx.ALIGN_CENTER_VERTICAL, 0) 762 763 # The file selection object. 764 obj = RelaxFileDialog(self, field=field, message=message, wildcard=wildcard, style=style) 765 766 # A little spacing. 767 sub_sizer.AddSpacer(5) 768 769 # The file selection button. 770 button = wx.BitmapButton(self, -1, wx.Bitmap(paths.icon_16x16.open, wx.BITMAP_TYPE_ANY)) 771 button.SetMinSize((self.height_element, self.height_element)) 772 sub_sizer.Add(button, 0, wx.ADJUST_MINSIZE|wx.ALIGN_CENTER_VERTICAL, 0) 773 self.Bind(wx.EVT_BUTTON, obj.select_event, button) 774 775 # File preview. 776 if not hasattr(self, 'file_selection_preview_button'): 777 self.file_selection_preview_button = [] 778 if not preview: 779 self.file_selection_preview_button.append(None) 780 else: 781 # A little spacing. 782 sub_sizer.AddSpacer(5) 783 784 # The preview button. 785 self.file_selection_preview_button.append(wx.BitmapButton(self, -1, wx.Bitmap(paths.icon_16x16.document_preview, wx.BITMAP_TYPE_ANY))) 786 button = self.file_selection_preview_button[-1] 787 button.SetMinSize((self.height_element, self.height_element)) 788 sub_sizer.Add(button, 0, wx.ADJUST_MINSIZE|wx.ALIGN_CENTER_VERTICAL, 0) 789 self.Bind(wx.EVT_BUTTON, self.preview_file, button) 790 791 # Right padding. 792 sub_sizer.AddSpacer(padding) 793 794 # Add to the main sizer (followed by stretchable spacing). 795 sizer.Add(sub_sizer, 1, wx.EXPAND|wx.ALL, 0) 796 797 # Spacing below the widget. 798 if spacer == None: 799 sizer.AddStretchSpacer() 800 else: 801 sizer.AddSpacer(spacer) 802 803 # Tooltip. 804 if tooltip: 805 text.SetToolTipString(tooltip) 806 field.SetToolTipString(tooltip) 807 button.SetToolTipString(tooltip) 808 else: 809 button.SetToolTipString("Select the file.") 810 811 # Return the field element. 812 return field
813 814
815 - def free_file_format(self, sizer, padding=10, spacer=3, data_cols=False, save=True, reset=True):
816 """Build the free format file settings widget. 817 818 @param sizer: The sizer to put the input field into. 819 @type sizer: wx.Sizer instance 820 @keyword padding: The size of the padding between the wx.StaticBoxSizer border and the internal elements, in pixels. 821 @type padding: int 822 @keyword spacer: The horizontal spacing between the elements, in pixels. 823 @type spacer: int 824 @keyword data_cols: A flag which if True causes the data and error column elements to be displayed. 825 @type data_cols: bool 826 @keyword save: A flag which if True will cause the save button to be displayed. 827 @type save: bool 828 @keyword reset: A flag which if True will cause the reset button to be displayed. 829 @type reset: bool 830 """ 831 832 # A static box to hold all the widgets. 833 box = wx.StaticBox(self, -1, "Free format file settings") 834 box.SetFont(font.subtitle) 835 836 # Init. 837 main_sizer = wx.StaticBoxSizer(box, wx.HORIZONTAL) 838 field_sizer = wx.BoxSizer(wx.VERTICAL) 839 button_sizer = wx.BoxSizer(wx.VERTICAL) 840 841 # The border of the widget. 842 border = wx.BoxSizer() 843 844 # Place the box sizer inside the border. 845 border.Add(main_sizer, 1, wx.ALL|wx.EXPAND, 0) 846 847 # Add to the main sizer (followed by stretchable spacing). 848 sizer.Add(border, 0, wx.EXPAND) 849 sizer.AddStretchSpacer() 850 851 # Calculate the divider position. 852 divider = self._div_left - border.GetMinSize()[0] / 2 - padding 853 854 # The columns. 855 self.spin_id_col = self.input_field(field_sizer, "Spin ID column:", divider=divider, padding=padding, spacer=spacer) 856 self.mol_name_col = self.input_field(field_sizer, "Molecule name column:", divider=divider, padding=padding, spacer=spacer) 857 self.res_num_col = self.input_field(field_sizer, "Residue number column:", divider=divider, padding=padding, spacer=spacer) 858 self.res_name_col = self.input_field(field_sizer, "Residue name column:", divider=divider, padding=padding, spacer=spacer) 859 self.spin_num_col = self.input_field(field_sizer, "Spin number column:", divider=divider, padding=padding, spacer=spacer) 860 self.spin_name_col = self.input_field(field_sizer, "Spin name column:", divider=divider, padding=padding, spacer=spacer) 861 if data_cols: 862 self.data_col = self.input_field(field_sizer, "Data column:", divider=divider, padding=padding, spacer=spacer) 863 self.err_col = self.input_field(field_sizer, "Error column:", divider=divider, padding=padding, spacer=spacer) 864 865 # The column separator. 866 self.sep = self.combo_box(field_sizer, "Column separator:", ["white space", ",", ";", ":", ""], divider=divider, padding=padding, spacer=spacer, read_only=False) 867 868 # Add the field sizer to the main sizer. 869 main_sizer.Add(field_sizer, 1, wx.ALL|wx.EXPAND, 0) 870 871 # Set the values. 872 self._free_file_format_set_vals() 873 874 # Buttons! 875 if save or reset: 876 # Add a save button. 877 if save: 878 # Build the button. 879 button = buttons.ThemedGenBitmapTextButton(self, -1, None, "") 880 button.SetBitmapLabel(wx.Bitmap(paths.icon_22x22.save, wx.BITMAP_TYPE_ANY)) 881 button.SetFont(font.normal) 882 button.SetToolTipString("Save the free file format settings within the relax data store.") 883 button.SetMinSize(self.size_square_button) 884 885 # Add the button. 886 button_sizer.Add(button, 0, wx.ADJUST_MINSIZE, 0) 887 888 # Padding. 889 button_sizer.AddSpacer(padding) 890 891 # Bind the click event. 892 self.Bind(wx.EVT_BUTTON, self._free_file_format_save, button) 893 894 # Add a reset button. 895 if reset: 896 # Build the button. 897 button = buttons.ThemedGenBitmapTextButton(self, -1, None, "") 898 button.SetBitmapLabel(wx.Bitmap(paths.icon_22x22.edit_delete, wx.BITMAP_TYPE_ANY)) 899 button.SetFont(font.normal) 900 button.SetToolTipString("Reset the free file format settings to the original values.") 901 button.SetMinSize(self.size_square_button) 902 903 # Add the button. 904 button_sizer.Add(button, 0, wx.ADJUST_MINSIZE, 0) 905 906 # Bind the click event. 907 self.Bind(wx.EVT_BUTTON, self._free_file_format_reset, button) 908 909 # Add the button sizer to the widget (with spacing). 910 main_sizer.AddSpacer(padding) 911 main_sizer.Add(button_sizer, 0, wx.ALL, 0)
912 913
914 - def input_field(self, sizer, desc, tooltip=None, divider=None, padding=0, spacer=None):
915 """Build the input field widget. 916 917 @param sizer: The sizer to put the input field widget into. 918 @type sizer: wx.Sizer instance 919 @param desc: The text description. 920 @type desc: str 921 @keyword tooltip: The tooltip which appears on hovering over the text or input field. 922 @type tooltip: str 923 @keyword divider: The optional position of the divider. If None, the class variable _div_left will be used. 924 @type divider: None or int 925 @keyword padding: Spacing to the left and right of the widgets. 926 @type padding: int 927 @keyword spacer: The amount of spacing to add below the field in pixels. If None, a stretchable spacer will be used. 928 @type spacer: None or int 929 @return: The input field object. 930 @rtype: wx.TextCtrl instance 931 """ 932 933 # Init. 934 sub_sizer = wx.BoxSizer(wx.HORIZONTAL) 935 936 # Left padding. 937 sub_sizer.AddSpacer(padding) 938 939 # The description. 940 text = wx.StaticText(self, -1, desc, style=wx.ALIGN_LEFT) 941 text.SetFont(font.normal) 942 sub_sizer.Add(text, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, 0) 943 944 # The divider. 945 if not divider: 946 divider = self._div_left 947 948 # Spacing. 949 x, y = text.GetSize() 950 sub_sizer.AddSpacer((divider - x, 0)) 951 952 # The input field. 953 field = wx.TextCtrl(self, -1, '') 954 field.SetMinSize((50, self.height_element)) 955 field.SetFont(font.normal) 956 sub_sizer.Add(field, 1, wx.ADJUST_MINSIZE|wx.ALIGN_CENTER_VERTICAL, 0) 957 958 # Right padding. 959 sub_sizer.AddSpacer(padding) 960 961 # Add to the main sizer. 962 sizer.Add(sub_sizer, 1, wx.EXPAND|wx.ALL, 0) 963 964 # Spacing below the widget. 965 if spacer == None: 966 sizer.AddStretchSpacer() 967 else: 968 sizer.AddSpacer(spacer) 969 970 # Tooltip. 971 if tooltip: 972 text.SetToolTipString(tooltip) 973 field.SetToolTipString(tooltip) 974 975 # Return the object. 976 return field
977 978
979 - def on_apply(self):
980 """To be over-ridden if an action is to be performed on hitting the apply button. 981 982 This method will be called when clicking on the apply button. 983 """
984 985
986 - def on_completion(self):
987 """To be over-ridden if an action is to be performed just after executing self.on_execute(). 988 989 This method is called just after self.on_execute() has been called 990 """
991 992
993 - def on_display(self):
994 """To be over-ridden if an action is to be performed prior to displaying the page. 995 996 This method will be called by the wizard class method _display_page() just after hiding all other pages but prior to displaying this page. 997 """
998 999
1000 - def on_display_post(self):
1001 """To be over-ridden if an action is to be performed after the execution of the on_display() method. 1002 1003 This method will be called by the wizard class method _display_page() just after hiding all other pages but prior to displaying this page. 1004 """
1005 1006
1007 - def on_execute(self):
1008 """To be over-ridden if an action is to be performed just before exiting the page. 1009 1010 This method is called when terminating the wizard or hitting the apply button. 1011 """
1012 1013
1014 - def on_init(self):
1015 """To be over-ridden if an action is to be performed when a page is newly displayed. 1016 1017 This method will be called by the wizard class method _display_page() at the very end. 1018 """
1019 1020
1021 - def on_next(self):
1022 """To be over-ridden if an action is to be performed just before moving to the next page. 1023 1024 This method is called when moving to the next page of the wizard. 1025 """
1026 1027
1028 - def preview_file(self, event):
1029 """Preview a file. 1030 1031 @param event: The wx event. 1032 @type event: wx event 1033 """ 1034 1035 # Find the correct button. 1036 button = event.GetEventObject() 1037 index = None 1038 for i in range(len(self.file_selection_preview_button)): 1039 if button == self.file_selection_preview_button[i]: 1040 index = i 1041 break 1042 1043 # No match. 1044 if index == None: 1045 return 1046 1047 # The file name. 1048 file = gui_to_str(self.file_selection_field[index].GetValue()) 1049 1050 # No file, so do nothing. 1051 if file == None: 1052 return 1053 1054 # Open the file as text. 1055 open_file(file, force_text=True)
1056 1057
1058 - def spin_control(self, sizer, desc, default='', min=0, max=100, tooltip=None, divider=None, padding=0, spacer=None):
1059 """Build the spin control widget. 1060 1061 @param sizer: The sizer to put the spin control widget into. 1062 @type sizer: wx.Sizer instance 1063 @param desc: The text description. 1064 @type desc: str 1065 @keyword default: The default value of the control. 1066 @type default: int 1067 @keyword min: The minimum value allowed. 1068 @type min: int 1069 @keyword max: The maximum value allowed. 1070 @type max: int 1071 @keyword tooltip: The tooltip which appears on hovering over the text or spin control. 1072 @type tooltip: str 1073 @keyword divider: The optional position of the divider. If None, the class variable _div_left will be used. 1074 @type divider: None or int 1075 @keyword padding: Spacing to the left and right of the widgets. 1076 @type padding: int 1077 @keyword spacer: The amount of spacing to add below the field in pixels. If None, a stretchable spacer will be used. 1078 @type spacer: None or int 1079 @return: The spin control object. 1080 @rtype: wx.TextCtrl instance 1081 """ 1082 1083 # Init. 1084 sub_sizer = wx.BoxSizer(wx.HORIZONTAL) 1085 1086 # Left padding. 1087 sub_sizer.AddSpacer(padding) 1088 1089 # The description. 1090 text = wx.StaticText(self, -1, desc, style=wx.ALIGN_LEFT) 1091 text.SetFont(font.normal) 1092 sub_sizer.Add(text, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, 0) 1093 1094 # The divider. 1095 if not divider: 1096 divider = self._div_left 1097 1098 # Spacing. 1099 x, y = text.GetSize() 1100 sub_sizer.AddSpacer((divider - x, 0)) 1101 1102 # The spin control. 1103 field = wx.SpinCtrl(self, id=-1, initial=default, min=min, max=max) 1104 field.SetMinSize((50, self.height_element)) 1105 field.SetFont(font.normal) 1106 sub_sizer.Add(field, 1, wx.ADJUST_MINSIZE|wx.ALIGN_CENTER_VERTICAL, 0) 1107 1108 # Right padding. 1109 sub_sizer.AddSpacer(padding) 1110 1111 # Add to the main sizer. 1112 sizer.Add(sub_sizer, 1, wx.EXPAND|wx.ALL, 0) 1113 1114 # Spacing below the widget. 1115 if spacer == None: 1116 sizer.AddStretchSpacer() 1117 else: 1118 sizer.AddSpacer(spacer) 1119 1120 # Tooltip. 1121 if tooltip: 1122 text.SetToolTipString(tooltip) 1123 field.SetToolTipString(tooltip) 1124 1125 # Return the object. 1126 return field
1127 1128
1129 - def spin_id_element(self, sizer, desc="The spin ID string:", choices=['@N', '@C'], default=None, divider=None, padding=0, spacer=None):
1130 """Build a special the input field widget. 1131 1132 @param sizer: The sizer to put the input field widget into. 1133 @type sizer: wx.Sizer instance 1134 @keyword desc: The text description. 1135 @type desc: str 1136 @keyword choices: The list of choices to present to the user. 1137 @type choices: list of str 1138 @keyword default: The default value. 1139 @type default: str or None 1140 @keyword divider: The optional position of the divider. If None, the class variable _div_left will be used. 1141 @type divider: None or int 1142 @keyword padding: Spacing to the left and right of the widgets. 1143 @type padding: int 1144 @keyword spacer: The amount of spacing to add below the field in pixels. If None, a stretchable spacer will be used. 1145 @type spacer: None or int 1146 @return: The input field object. 1147 @rtype: wx.TextCtrl instance 1148 """ 1149 1150 # Init. 1151 sub_sizer = wx.BoxSizer(wx.HORIZONTAL) 1152 1153 # Left padding. 1154 sub_sizer.AddSpacer(padding) 1155 1156 # The description. 1157 text = wx.StaticText(self, -1, desc, style=wx.ALIGN_LEFT) 1158 text.SetFont(font.normal) 1159 sub_sizer.Add(text, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, 0) 1160 1161 # The divider. 1162 if not divider: 1163 divider = self._div_left 1164 1165 # Spacing. 1166 x, y = text.GetSize() 1167 sub_sizer.AddSpacer((divider - x, 0)) 1168 1169 # The input field. 1170 field = wx.ComboBox(self, -1, '', choices=choices) 1171 field.SetMinSize((50, self.height_element)) 1172 field.SetFont(font.normal) 1173 sub_sizer.Add(field, 1, wx.ADJUST_MINSIZE|wx.ALIGN_CENTER_VERTICAL, 0) 1174 1175 # Set the default. 1176 field.SetValue(str_to_gui(default)) 1177 1178 # Right padding. 1179 sub_sizer.AddSpacer(padding) 1180 1181 # Add to the main sizer. 1182 sizer.Add(sub_sizer, 1, wx.EXPAND|wx.ALL, 0) 1183 1184 # Spacing below the widget. 1185 if spacer == None: 1186 sizer.AddStretchSpacer() 1187 else: 1188 sizer.AddSpacer(spacer) 1189 1190 # Tooltip (the ID string documentation, with starting and ending newlines removed). 1191 text.SetToolTipString(id_string_doc[1][1:-1]) 1192 field.SetToolTipString(id_string_doc[1][1:-1]) 1193 1194 # Return the object. 1195 return field
1196 1197
1198 - def text(self, sizer, desc, default='', divider=None, padding=0, spacer=None):
1199 """Build the input field. 1200 1201 @param sizer: The sizer to put the input field into. 1202 @type sizer: wx.Sizer instance 1203 @param desc: The text description. 1204 @type desc: str 1205 @keyword default: The default text. 1206 @type default: str 1207 @keyword divider: The optional position of the divider. If None, the class variable _div_left will be used. 1208 @type divider: None or int 1209 @keyword padding: Spacing to the left and right of the widgets. 1210 @type padding: int 1211 @keyword spacer: The amount of spacing to add below the field in pixels. If None, a stretchable spacer will be used. 1212 @type spacer: None or int 1213 @return: The input field object. 1214 @rtype: wx.TextCtrl instance 1215 """ 1216 1217 # Init. 1218 sub_sizer = wx.BoxSizer(wx.HORIZONTAL) 1219 1220 # Left padding. 1221 sub_sizer.AddSpacer(padding) 1222 1223 # The description. 1224 text = wx.StaticText(self, -1, desc, style=wx.ALIGN_LEFT) 1225 text.SetFont(font.normal) 1226 sub_sizer.Add(text, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, 0) 1227 1228 # The divider. 1229 if not divider: 1230 divider = self._div_left 1231 1232 # Spacing. 1233 x, y = text.GetSize() 1234 sub_sizer.AddSpacer((divider - x, 0)) 1235 1236 # The non-editable text. 1237 text = wx.TextCtrl(self, -1, default, style=wx.ALIGN_LEFT) 1238 text.SetEditable(False) 1239 text.SetFont(font.normal) 1240 colour = self.GetBackgroundColour() 1241 text.SetOwnBackgroundColour(colour) 1242 text.SetMinSize((self._div_right, self.height_element)) 1243 sub_sizer.Add(text, 0, wx.ADJUST_MINSIZE|wx.ALIGN_CENTER_VERTICAL, 0) 1244 1245 # Right padding. 1246 sub_sizer.AddSpacer(padding) 1247 1248 # Add to the main sizer (followed by stretchable spacing). 1249 sizer.Add(sub_sizer) 1250 1251 # Spacing below the widget. 1252 if spacer == None: 1253 sizer.AddStretchSpacer() 1254 else: 1255 sizer.AddSpacer(spacer) 1256 1257 # Return the object. 1258 return text
1259 1260 1261
1262 -class Wiz_window(wx.Dialog):
1263 """The wizard.""" 1264 1265 # Some class variables. 1266 _size_button = (100, 33) 1267 ICON_APPLY = paths.icon_22x22.dialog_ok_apply 1268 ICON_BACK = paths.icon_22x22.go_previous_view 1269 ICON_CANCEL = paths.icon_22x22.dialog_cancel 1270 ICON_FINISH = paths.icon_22x22.dialog_ok 1271 ICON_NEXT = paths.icon_22x22.go_next_view 1272 ICON_OK = paths.icon_22x22.dialog_ok 1273 ICON_SKIP = paths.icon_22x22.skip 1274 TEXT_APPLY = " Apply" 1275 TEXT_BACK = " Back" 1276 TEXT_CANCEL = " Cancel" 1277 TEXT_FINISH = " Finish" 1278 TEXT_NEXT = " Next" 1279 TEXT_OK = " OK" 1280 TEXT_SKIP = " Skip" 1281 1282
1283 - def __init__(self, parent=None, size_x=400, size_y=400, title='', border=10, style=wx.DEFAULT_DIALOG_STYLE):
1284 """Set up the window. 1285 1286 @keyword parent: The parent window. 1287 @type parent: wx.Window instance 1288 @keyword size_x: The width of the wizard. 1289 @type size_x: int 1290 @keyword size_y: The height of the wizard. 1291 @type size_y: int 1292 @keyword title: The title of the wizard dialog. 1293 @type title: str 1294 @keyword border: The size of the border inside the wizard. 1295 @type border: int 1296 @keyword style: The dialog style. 1297 @type style: wx style 1298 """ 1299 1300 # Store the args. 1301 self._size_x = size_x 1302 self._size_y = size_y 1303 self._border = border 1304 1305 # Execute the base class method. 1306 wx.Dialog.__init__(self, parent, id=-1, title=title, style=style) 1307 1308 # Set up the window icon. 1309 self.SetIcons(relax_icons) 1310 1311 # The sizer for the dialog. 1312 sizer = wx.BoxSizer(wx.VERTICAL) 1313 self.SetSizer(sizer) 1314 1315 # Build the central sizer, with borders. 1316 self._main_sizer = add_border(sizer, border=border, packing=wx.VERTICAL) 1317 1318 # Set the default size of the dialog. 1319 self.SetSize((size_x, size_y)) 1320 1321 # Centre the dialog. 1322 self.Centre() 1323 1324 # Initialise the page storage. 1325 self._current_page = 0 1326 self._num_pages = 0 1327 self._pages = [] 1328 self._page_sizers = [] 1329 self._button_sizers = [] 1330 self._button_apply_flag = [] 1331 self._button_skip_flag = [] 1332 self._buttons = [] 1333 self._button_ids = [] 1334 self._exec_on_next = [] 1335 self._exec_count = [] 1336 self._proceed_on_error = [] 1337 self._uf_flush = [] 1338 self._seq_fn_list = [] 1339 self._seq_next = [] 1340 self._seq_prev = [] 1341 self._skip_flag = [] 1342 1343 # A max of 10 pages should be plenty enough (any more and the developer should be shot!). 1344 for i in range(10): 1345 # Append some Nones. 1346 self._pages.append(None) 1347 1348 # Initialise all box sizers for the wizard pages. 1349 self._page_sizers.append(wx.BoxSizer(wx.VERTICAL)) 1350 1351 # Initialise all box sizers for the buttons. 1352 self._button_sizers.append(wx.BoxSizer(wx.HORIZONTAL)) 1353 1354 # Set all button flags. 1355 self._button_apply_flag.append(True) 1356 self._button_skip_flag.append(False) 1357 1358 # Initialise the button storage. 1359 self._buttons.append({'back': None, 1360 'apply': None, 1361 'next': None, 1362 'ok': None, 1363 'finish': None, 1364 'cancel': None}) 1365 1366 # Initialise a set of unique button IDs. 1367 self._button_ids.append({'back': wx.NewId(), 1368 'apply': wx.NewId(), 1369 'next': wx.NewId(), 1370 'ok': wx.NewId(), 1371 'finish': wx.NewId(), 1372 'cancel': wx.NewId()}) 1373 1374 # Execute on next by default. 1375 self._exec_on_next.append(True) 1376 1377 # Execution count. 1378 self._exec_count.append(0) 1379 1380 # Proceed to next page on errors by default. 1381 self._proceed_on_error.append(True) 1382 1383 # No user function flushing of the GUI interpreter thread prior to proceeding. 1384 self._uf_flush.append(False) 1385 1386 # Page sequence initialisation. 1387 self._seq_fn_list.append(self._next_fn) 1388 self._seq_next.append(None) 1389 self._seq_prev.append(None) 1390 1391 # Page skipping. 1392 self._skip_flag.append(False)
1393 1394
1395 - def _build_buttons(self):
1396 """Construct the buttons for all pages of the wizard.""" 1397 1398 # Loop over each page. 1399 for i in range(self._num_pages): 1400 # The back button (only for multi-pages, after the first). 1401 if self._num_pages > 1 and i > 0: 1402 # Create the button. 1403 button = buttons.ThemedGenBitmapTextButton(self, -1, None, self.TEXT_BACK) 1404 button.SetBitmapLabel(wx.Bitmap(self.ICON_BACK, wx.BITMAP_TYPE_ANY)) 1405 button.SetFont(font.normal) 1406 button.SetToolTipString("Return to the previous page") 1407 button.SetMinSize(self._size_button) 1408 self._button_sizers[i].Add(button, 0, wx.ADJUST_MINSIZE, 0) 1409 self.Bind(wx.EVT_BUTTON, self._go_back, button) 1410 self._buttons[i]['back'] = button 1411 1412 # Spacer. 1413 self._button_sizers[i].AddSpacer(5) 1414 1415 # The apply button. 1416 if self._button_apply_flag[i]: 1417 # Create the button. 1418 button = buttons.ThemedGenBitmapTextButton(self, -1, None, self.TEXT_APPLY) 1419 button.SetBitmapLabel(wx.Bitmap(self.ICON_APPLY, wx.BITMAP_TYPE_ANY)) 1420 button.SetFont(font.normal) 1421 button.SetToolTipString("Apply the operation") 1422 button.SetMinSize(self._size_button) 1423 self._button_sizers[i].Add(button, 0, wx.ADJUST_MINSIZE, 0) 1424 self.Bind(wx.EVT_BUTTON, self._pages[i]._apply, button) 1425 self._buttons[i]['apply'] = button 1426 1427 # Spacer. 1428 self._button_sizers[i].AddSpacer(5) 1429 1430 # The skip button. 1431 if self._button_skip_flag[i]: 1432 # Create the button. 1433 button = buttons.ThemedGenBitmapTextButton(self, -1, None, self.TEXT_SKIP) 1434 button.SetBitmapLabel(wx.Bitmap(self.ICON_SKIP, wx.BITMAP_TYPE_ANY)) 1435 button.SetFont(font.normal) 1436 button.SetToolTipString("Skip the operation") 1437 button.SetMinSize(self._size_button) 1438 self._button_sizers[i].Add(button, 0, wx.ADJUST_MINSIZE, 0) 1439 self.Bind(wx.EVT_BUTTON, self._skip, button) 1440 self._buttons[i]['skip'] = button 1441 1442 # Spacer. 1443 self._button_sizers[i].AddSpacer(5) 1444 1445 # The next button (only for multi-pages, excluding the last). 1446 if self._num_pages > 1 and i < self._num_pages - 1: 1447 # Create the button. 1448 button = buttons.ThemedGenBitmapTextButton(self, -1, None, self.TEXT_NEXT) 1449 button.SetBitmapLabel(wx.Bitmap(self.ICON_NEXT, wx.BITMAP_TYPE_ANY)) 1450 button.SetFont(font.normal) 1451 button.SetToolTipString("Move to the next page") 1452 button.SetMinSize(self._size_button) 1453 self._button_sizers[i].Add(button, 0, wx.ADJUST_MINSIZE, 0) 1454 self.Bind(wx.EVT_BUTTON, self._go_next, button) 1455 self._buttons[i]['next'] = button 1456 1457 # The OK button (only for single pages). 1458 if self._num_pages == 1: 1459 button = buttons.ThemedGenBitmapTextButton(self, -1, None, self.TEXT_OK) 1460 button.SetBitmapLabel(wx.Bitmap(self.ICON_OK, wx.BITMAP_TYPE_ANY)) 1461 button.SetFont(font.normal) 1462 button.SetToolTipString("Accept the operation") 1463 button.SetMinSize(self._size_button) 1464 self._button_sizers[i].Add(button, 0, wx.ADJUST_MINSIZE, 0) 1465 self.Bind(wx.EVT_BUTTON, self._ok, button) 1466 self._buttons[i]['ok'] = button 1467 1468 # The finish button (only for the last page with multi-pages). 1469 if self._num_pages > 1 and i == self._num_pages - 1: 1470 button = buttons.ThemedGenBitmapTextButton(self, -1, None, self.TEXT_FINISH) 1471 button.SetBitmapLabel(wx.Bitmap(self.ICON_FINISH, wx.BITMAP_TYPE_ANY)) 1472 button.SetFont(font.normal) 1473 button.SetToolTipString("Accept the operation") 1474 button.SetMinSize(self._size_button) 1475 self._button_sizers[i].Add(button, 0, wx.ADJUST_MINSIZE, 0) 1476 self.Bind(wx.EVT_BUTTON, self._ok, button) 1477 self._buttons[i]['finish'] = button 1478 1479 # Spacer. 1480 self._button_sizers[i].AddSpacer(15) 1481 1482 # The cancel button. 1483 button = buttons.ThemedGenBitmapTextButton(self, -1, None, self.TEXT_CANCEL) 1484 button.SetBitmapLabel(wx.Bitmap(self.ICON_CANCEL, wx.BITMAP_TYPE_ANY)) 1485 button.SetFont(font.normal) 1486 button.SetToolTipString("Abort the operation") 1487 button.SetMinSize(self._size_button) 1488 self._button_sizers[i].Add(button, 0, wx.ADJUST_MINSIZE, 0) 1489 self.Bind(wx.EVT_BUTTON, self._cancel, button) 1490 self._buttons[i]['cancel'] = button
1491 1492
1493 - def _cancel(self, event):
1494 """Cancel the operation. 1495 1496 @param event: The wx event. 1497 @type event: wx event 1498 """ 1499 1500 # Close the window. 1501 self.Close()
1502 1503
1504 - def _display_page(self, i):
1505 """Display the given page. 1506 1507 @param i: The index of the page to display. 1508 @type i: int 1509 """ 1510 1511 # Hide all of the original contents. 1512 for j in range(self._num_pages): 1513 if self._main_sizer.IsShown(self._page_sizers[j]): 1514 self._main_sizer.Hide(self._page_sizers[j]) 1515 1516 # Show the desired page. 1517 if status.show_gui: 1518 self._main_sizer.Show(self._page_sizers[i]) 1519 1520 # Execute the page's on_display() method. 1521 self._pages[i].on_display() 1522 self._pages[i].on_display_post() 1523 1524 # Re-perform the window layout. 1525 self.Layout() 1526 self.Refresh() 1527 1528 # Execute the page's on_init() method. 1529 self._pages[i].on_init()
1530 1531
1532 - def _go_back(self, event):
1533 """Return to the previous page. 1534 1535 @param event: The wx event. 1536 @type event: wx event 1537 """ 1538 1539 # Work back in the sequence. 1540 self._current_page = self._seq_prev[self._current_page] 1541 1542 # Display the previous page. 1543 self._display_page(self._current_page)
1544 1545
1546 - def _go_next(self, event):
1547 """Move to the next page. 1548 1549 @param event: The wx event. 1550 @type event: wx event 1551 """ 1552 1553 # Operations for non-skipped pages. 1554 if not self._skip_flag[self._current_page]: 1555 # Execute the page's on_next() method. 1556 self._pages[self._current_page].on_next() 1557 1558 # Execute the page's on_execute() method (via the _apply() method). 1559 if self._exec_on_next[self._current_page]: 1560 self._pages[self._current_page]._apply(event) 1561 1562 # UF flush. 1563 if self._uf_flush[self._current_page]: 1564 interpreter.flush() 1565 1566 # Check for execution errors. 1567 if not self._pages[self._current_page].exec_status: 1568 # Do not proceed. 1569 if not self._proceed_on_error[self._current_page]: 1570 return 1571 1572 # Increment the execution counter. 1573 self._exec_count[self._current_page] += 1 1574 1575 # Determine the next page. 1576 next_page = self._seq_fn_list[self._current_page]() 1577 1578 # No next page, so terminate. 1579 if self._pages[next_page] == None: 1580 self._ok(None) 1581 return 1582 1583 # Update the sequence lists. 1584 self._seq_next[self._current_page] = next_page 1585 self._seq_prev[next_page] = self._current_page 1586 1587 # Change the current page. 1588 self._current_page = next_page 1589 1590 # Display the next page. 1591 self._display_page(self._current_page)
1592 1593
1594 - def _next_fn(self):
1595 """Standard function for setting the next page to the one directly next in the sequence. 1596 1597 @return: The index of the next page, which is the current page index plus one. 1598 @rtype: int 1599 """ 1600 1601 # Return the next page. 1602 return self._current_page + 1
1603 1604
1605 - def _ok(self, event):
1606 """Accept the operation. 1607 1608 @param event: The wx event. 1609 @type event: wx event 1610 """ 1611 1612 # Loop over the pages in the sequence and execute their _apply() methods, if not already done and not skipped. 1613 for i in self._seq_loop(): 1614 if not self._exec_count[i] and not self._skip_flag[i]: 1615 # Execute the _apply method. 1616 self._pages[i]._apply(event) 1617 1618 # UF flush. 1619 if self._uf_flush[i]: 1620 interpreter.flush() 1621 1622 # Check for execution errors. 1623 if not self._pages[self._current_page].exec_status: 1624 # Do not proceed. 1625 if not self._proceed_on_error[self._current_page]: 1626 return 1627 1628 # Increment the execution counter. 1629 self._exec_count[i] += 1 1630 1631 # Then close the dialog. 1632 if self.IsModal(): 1633 self.EndModal(wx.ID_OK) 1634 else: 1635 self.Close()
1636 1637
1638 - def _seq_loop(self):
1639 """Loop over the sequence in the forwards direction.""" 1640 1641 # Initialise. 1642 current = 0 1643 1644 # First yield the initial element (always zero!). 1645 yield current 1646 1647 # Loop over the sequence. 1648 while True: 1649 # Update. 1650 next = self._seq_next[current] 1651 current = next 1652 1653 # End of the sequence. 1654 if next == None: 1655 break 1656 1657 # Yield the next index. 1658 yield next
1659 1660
1661 - def _skip(self, event):
1662 """Skip the page. 1663 1664 @param event: The wx event. 1665 @type event: wx event 1666 """ 1667 1668 # Set the skip flag. 1669 self._skip_flag[self._current_page] = True 1670 1671 # Go to the next page. 1672 self._go_next(None)
1673 1674
1675 - def add_page(self, panel, apply_button=True, skip_button=False, exec_on_next=True, proceed_on_error=True, uf_flush=False):
1676 """Add a new page to the wizard. 1677 1678 @param panel: The page to add to the wizard. 1679 @type panel: wx.Panel instance 1680 @keyword apply_button: A flag which if true will show the apply button for that page. 1681 @type apply_button: bool 1682 @keyword skip_button: A flag which if true will show the skip button for that page. 1683 @type skip_button: bool 1684 @keyword exec_on_next: A flag which if true will run the on_execute() method when clicking on the next button. 1685 @type exec_on_next: bool 1686 @keyword proceed_on_error: A flag which if True will proceed to the next page (or quit if there are no more pages) despite the occurrence of an error in execution. If False, the page will remain open (the GUI interpreter thread will be flushed first to synchronise). 1687 @type proceed_on_error: bool 1688 @keyword uf_flush: A flag which if True will cause the GUI interpreter thread to be flushed to clear out all user function call prior to proceeding. 1689 @type uf_flush: bool 1690 @return: The index of the page in the wizard. 1691 @rtype: int 1692 """ 1693 1694 # Store the page. 1695 index = self._num_pages 1696 self._num_pages += 1 1697 self._pages[index] = panel 1698 1699 # Store a new sizer for the page and its buttons. 1700 self._main_sizer.Add(self._page_sizers[index], 1, wx.ALL|wx.EXPAND, 0) 1701 1702 # Add the sizer for the top half. 1703 top_sizer = wx.BoxSizer(wx.VERTICAL) 1704 self._page_sizers[index].Add(top_sizer, 1, wx.ALL|wx.EXPAND, 0) 1705 1706 # Add the page to the top sizer. 1707 top_sizer.Add(panel, 1, wx.ALL|wx.EXPAND, 0) 1708 1709 # Add the sizer for the wizard buttons. 1710 self._page_sizers[index].Add(self._button_sizers[index], 0, wx.ALIGN_RIGHT|wx.ALL, 0) 1711 1712 # Store the flags. 1713 self._button_apply_flag[index] = apply_button 1714 self._button_skip_flag[index] = skip_button 1715 self._exec_on_next[index] = exec_on_next 1716 self._proceed_on_error[index] = proceed_on_error 1717 if not proceed_on_error or uf_flush: 1718 self._uf_flush[index] = True 1719 1720 # Store the index of the page. 1721 panel.page_index = self._num_pages - 1 1722 1723 # Return the index of the page. 1724 return panel.page_index
1725 1726
1727 - def block_next(self, block=True):
1728 """Prevent moving forwards (or unblock). 1729 1730 @keyword block: A flag which if True will block forwards movement and if False will unblock. 1731 @type block: bool 1732 """ 1733 1734 # The buttons to disable. 1735 buttons = ['next', 'ok', 'finish'] 1736 1737 # Disable or enable the buttons. 1738 for i in range(len(buttons)): 1739 # The button. 1740 button = self._buttons[self._current_page][buttons[i]] 1741 if button == None: 1742 continue 1743 1744 # Block. 1745 if block: 1746 button.Disable() 1747 1748 # Unblock. 1749 else: 1750 button.Enable()
1751 1752
1753 - def get_page(self, index):
1754 """Get a page from the wizard. 1755 1756 @param index: The index of the page. 1757 @type index: int 1758 @return: The page object. 1759 @rtype: Wiz_page instance. 1760 """ 1761 1762 # Return the page. 1763 return self._pages[index]
1764 1765
1766 - def run(self, modal=False):
1767 """Execute the wizard. 1768 1769 @keyword modal: A flag which if True will cause the wizard to be run as a modal dialog. 1770 @type modal: bool 1771 @return: The status from the modal operation, i.e. True if the wizard is run, False if cancelled or other error occur. For modeless operation, this returns nothing. 1772 @rtype: bool or None 1773 """ 1774 1775 # Check that all pages have been set up correctly, returning without doing anything if not. 1776 for i in range(self._num_pages): 1777 if self._pages[i].setup_fail: 1778 return 1779 1780 # Build the buttons for the entire wizard. 1781 self._build_buttons() 1782 1783 # Display the first page. 1784 self._display_page(0) 1785 1786 # No GUI. 1787 if not status.show_gui: 1788 return 1789 1790 # Modal operation. 1791 if modal: 1792 # Show the wizard (it should be closed by the _cancel() or _ok() methods). 1793 wiz_status = self.ShowModal() 1794 1795 # Return the status. 1796 return wiz_status 1797 1798 # Modeless operation. 1799 else: 1800 # Show the wizard. 1801 self.Show()
1802 1803
1804 - def set_seq_next_fn(self, index, fn):
1805 """A user specified function for non-linear page changing. 1806 1807 @param index: The index of the page the function should be associated with. 1808 @type index: int 1809 @param fn: The function for determining the page after the current. This function should return the index of the next page. 1810 @type fn: func or method. 1811 """ 1812 1813 # Store the function. 1814 self._seq_fn_list[index] = fn
1815