Package gui :: Package wizards :: Module wiz_objects
[hide private]
[frames] | no frames]

Source Code for Module gui.wizards.wiz_objects

   1  ############################################################################### 
   2  #                                                                             # 
   3  # Copyright (C) 2010-2015 Edward d'Auvergne                                   # 
   4  #                                                                             # 
   5  # This file is part of the program relax (http://www.nmr-relax.com).          # 
   6  #                                                                             # 
   7  # This program 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 3 of the License, or           # 
  10  # (at your option) any later version.                                         # 
  11  #                                                                             # 
  12  # This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.       # 
  19  #                                                                             # 
  20  ############################################################################### 
  21   
  22  # Module docstring. 
  23  """Base class module for the wizard GUI elements.""" 
  24   
  25  # Python module imports. 
  26  import wx 
  27  from wx.lib import buttons, scrolledpanel 
  28   
  29  # relax module imports. 
  30  from data_store import Relax_data_store; ds = Relax_data_store() 
  31  from graphics import IMAGE_PATH, fetch_icon 
  32  from gui.fonts import font 
  33  from gui.icons import relax_icons 
  34  from gui.interpreter import Interpreter; interpreter = Interpreter() 
  35  from gui.misc import add_border, bitmap_setup 
  36  from gui.string_conv import float_to_gui, str_to_gui 
  37  from lib.check_types import is_float 
  38  from lib.errors import RelaxImplementError 
  39  from status import Status; status = Status() 
  40   
  41   
  42  # The wx ID for the special accelerator table that allows the ESC button to close relax wizards. 
  43  ESC_ID = wx.NewId() 
  44   
  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 - input_field() 65 - text() 66 67 These are described in full detail in their docstrings. 68 """ 69 70 # Some class variables. 71 art_spacing = 20 72 divider = None 73 height_element = 27 74 image_path = IMAGE_PATH + "relax.gif" 75 main_text = '' 76 setup_fail = False 77 size_button = (100, 33) 78 size_square_button = (33, 33) 79 title = '' 80
81 - def __init__(self, parent, height_desc=220):
82 """Set up the window. 83 84 @param parent: The parent GUI element. 85 @type parent: wx.object instance 86 @keyword height_desc: The height in pixels of the description part of the wizard. 87 @type height_desc: int or None 88 """ 89 90 # Store the args. 91 self.parent = parent 92 self.height_desc = height_desc 93 94 # Execute the base class method. 95 wx.Panel.__init__(self, parent, id=-1) 96 97 # Initilise some variables. 98 self.exec_status = False 99 100 # Pack a sizer into the panel. 101 box_main = wx.BoxSizer(wx.HORIZONTAL) 102 self.SetSizer(box_main) 103 104 # Add the artwork. 105 self.add_artwork(box_main) 106 107 # The size of the image. 108 image_x, image_y = self.image.GetSize() 109 110 # Calculate the size of the main section, and the subdivisions. 111 self._main_size = parent._size_x - image_x - self.art_spacing - 2*parent._border 112 if self.divider: 113 self._div_left = self.divider 114 self._div_right = self._main_size - self.divider 115 else: 116 self._div_left = self._div_right = self._main_size / 2 117 118 # Add the main sizer. 119 main_sizer = self._build_main_section(box_main) 120 121 # Add the title. 122 self._add_title(main_sizer) 123 124 # Add the description. 125 self.add_desc(main_sizer, max_y=self.height_desc) 126 127 # Add the specific GUI elements (bounded by spacers). 128 main_sizer.AddStretchSpacer() 129 self.add_contents(main_sizer)
130 131
132 - def _add_title(self, sizer):
133 """Add the title to the dialog. 134 135 @param sizer: A sizer object. 136 @type sizer: wx.Sizer instance 137 """ 138 139 # Spacing. 140 sizer.AddSpacer(10) 141 142 # The text. 143 title = wx.StaticText(self, -1, self.title) 144 145 # Font. 146 title.SetFont(font.title) 147 148 # Add the title. 149 sizer.Add(title, 0, wx.ALIGN_CENTRE|wx.ALL, 0) 150 151 # Spacing. 152 sizer.AddSpacer(10)
153 154
155 - def _apply(self, event=None):
156 """Apply the operation. 157 158 @keyword event: The wx event. 159 @type event: wx event 160 """ 161 162 # A bit of user feedback. 163 wx.BeginBusyCursor() 164 165 # Execute. 166 self.exec_status = self.on_execute() 167 168 # Execution failure. 169 if not self.exec_status: 170 if wx.IsBusy(): 171 wx.EndBusyCursor() 172 return 173 174 # Finished. 175 self.on_completion() 176 177 # Execute the on_apply() method. 178 self.on_apply() 179 180 # Turn off the busy cursor if needed. 181 if wx.IsBusy(): 182 wx.EndBusyCursor()
183 184
185 - def _build_main_section(self, sizer):
186 """Add the main part of the dialog. 187 188 @param sizer: A sizer object. 189 @type sizer: wx.Sizer instance 190 @return: The sizer object for the main part of the dialog. 191 @rtype: wx.Sizer instance 192 """ 193 194 # Use a grid sizer for packing the elements. 195 main_sizer = wx.BoxSizer(wx.VERTICAL) 196 197 # Pack the sizer. 198 sizer.Add(main_sizer, 1, wx.EXPAND|wx.ALL, 0) 199 200 # Return the sizer. 201 return main_sizer
202 203
204 - def add_artwork(self, sizer):
205 """Add the artwork to the dialog. 206 207 @param sizer: A sizer object. 208 @type sizer: wx.Sizer instance 209 """ 210 211 # Add the graphics. 212 if self.image_path: 213 self.image = wx.StaticBitmap(self, -1, bitmap_setup(self.image_path)) 214 sizer.Add(self.image, 0, wx.TOP|wx.ALIGN_CENTER_HORIZONTAL, 0) 215 216 # A spacer. 217 sizer.AddSpacer(self.art_spacing)
218 219
220 - def add_contents(self, sizer):
221 """Add the specific GUI elements (dummy method). 222 223 @param sizer: A sizer object. 224 @type sizer: wx.Sizer instance 225 """ 226 227 # This must be supplied. 228 raise RelaxImplementError
229 230
231 - def add_desc(self, sizer, max_y=220):
232 """Add the description to the dialog. 233 234 @param sizer: A sizer object. 235 @type sizer: wx.Sizer instance 236 @keyword max_y: The maximum height, in number of pixels, for the description. 237 @type max_y: int 238 """ 239 240 # A line with spacing. 241 sizer.AddSpacer(5) 242 sizer.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.ALL, 0) 243 sizer.AddSpacer(5) 244 245 # Create a scrolled panel. 246 panel = scrolledpanel.ScrolledPanel(self, -1, name="desc") 247 248 # A sizer for the panel. 249 panel_sizer = wx.BoxSizer(wx.VERTICAL) 250 251 # The text. 252 text = wx.StaticText(panel, -1, self.main_text, style=wx.TE_MULTILINE) 253 text.SetFont(font.normal) 254 255 # Wrap the text. 256 text.Wrap(self._main_size - 20) 257 258 # The text size. 259 x, y = text.GetSizeTuple() 260 261 # Scrolling needed. 262 if y > max_y-10: 263 # Set the panel size. 264 panel.SetInitialSize((self._main_size, max_y)) 265 266 # No scrolling. 267 else: 268 # Rewrap the text. 269 text.Wrap(self._main_size) 270 271 # Set the panel size. 272 panel.SetInitialSize(text.GetSize()) 273 274 # Add the text. 275 panel_sizer.Add(text, 0, wx.ALIGN_LEFT, 0) 276 277 # Set up and add the panel to the sizer. 278 panel.SetSizer(panel_sizer) 279 panel.SetAutoLayout(1) 280 panel.SetupScrolling(scroll_x=False, scroll_y=True) 281 sizer.Add(panel, 0, wx.ALL|wx.EXPAND) 282 283 # A line with spacing. 284 sizer.AddSpacer(5) 285 sizer.Add(wx.StaticLine(self, -1), 0, wx.EXPAND|wx.ALL, 0) 286 sizer.AddSpacer(5)
287 288
289 - def on_apply(self):
290 """To be over-ridden if an action is to be performed on hitting the apply button. 291 292 This method will be called when clicking on the apply button. 293 """
294 295
296 - def on_back(self):
297 """To be over-ridden if an action is to be performed just before moving back to the previous page. 298 299 This method is called when moving back to the previous page of the wizard. 300 """
301 302
303 - def on_completion(self):
304 """To be over-ridden if an action is to be performed just after executing self.on_execute(). 305 306 This method is called just after self.on_execute() has been called 307 """
308 309
310 - def on_display(self):
311 """To be over-ridden if an action is to be performed prior to displaying the page. 312 313 This method will be called by the wizard class method _display_page() just after hiding all other pages but prior to displaying this page. 314 """
315 316
317 - def on_display_post(self):
318 """To be over-ridden if an action is to be performed after the execution of the on_display() method. 319 320 This method will be called by the wizard class method _display_page() just after hiding all other pages but prior to displaying this page. 321 """
322 323
324 - def on_execute(self):
325 """To be over-ridden if an action is to be performed just before exiting the page. 326 327 This method is called when terminating the wizard or hitting the apply button. 328 """ 329 330 return True
331 332
333 - def on_init(self):
334 """To be over-ridden if an action is to be performed when a page is newly displayed. 335 336 This method will be called by the wizard class method _display_page() at the very end. 337 """
338 339
340 - def on_next(self):
341 """To be over-ridden if an action is to be performed just before moving to the next page. 342 343 This method is called when moving to the next page of the wizard. 344 """
345 346 347
348 -class Wiz_window(wx.Dialog):
349 """The wizard.""" 350 351 # Some class variables. 352 _size_button = (100, 33) 353 ICON_APPLY = fetch_icon('oxygen.actions.dialog-ok-apply', "22x22") 354 ICON_BACK = fetch_icon('oxygen.actions.go-previous-view', "22x22") 355 ICON_CANCEL = fetch_icon('oxygen.actions.dialog-cancel', "22x22") 356 ICON_FINISH = fetch_icon('oxygen.actions.dialog-ok', "22x22") 357 ICON_NEXT = fetch_icon('oxygen.actions.go-next-view', "22x22") 358 ICON_OK = fetch_icon('oxygen.actions.dialog-ok', "22x22") 359 ICON_SKIP = fetch_icon('oxygen.actions.arrow-right-double-relax-blue', "22x22") 360 TEXT_APPLY = " Apply" 361 TEXT_BACK = " Back" 362 TEXT_CANCEL = " Cancel" 363 TEXT_FINISH = " Finish" 364 TEXT_NEXT = " Next" 365 TEXT_OK = " OK" 366 TEXT_SKIP = " Skip" 367 368
369 - def __init__(self, parent=None, size_x=400, size_y=400, title='', border=10, style=wx.DEFAULT_DIALOG_STYLE):
370 """Set up the window. 371 372 @keyword parent: The parent window. 373 @type parent: wx.Window instance 374 @keyword size_x: The width of the wizard. 375 @type size_x: int 376 @keyword size_y: The height of the wizard. 377 @type size_y: int 378 @keyword title: The title of the wizard dialog. 379 @type title: str 380 @keyword border: The size of the border inside the wizard. 381 @type border: int 382 @keyword style: The dialog style. 383 @type style: wx style 384 """ 385 386 # Store the args. 387 self._size_x = size_x 388 self._size_y = size_y 389 self._border = border 390 self.title = title 391 392 # Execute the base class method. 393 wx.Dialog.__init__(self, parent, id=-1, title=title, style=style) 394 395 # Set up the window icon. 396 self.SetIcons(relax_icons) 397 398 # The sizer for the dialog. 399 sizer = wx.BoxSizer(wx.VERTICAL) 400 self.SetSizer(sizer) 401 402 # Build the central sizer, with borders. 403 self._main_sizer = add_border(sizer, border=border, packing=wx.VERTICAL) 404 405 # Set the default size of the dialog. 406 self.SetSize((size_x, size_y)) 407 408 # Centre the dialog. 409 self.Centre() 410 411 # Initialise the page storage. 412 self._current_page = 0 413 self._num_pages = 0 414 self._pages = [] 415 self._page_sizers = [] 416 self._button_sizers = [] 417 self._top_sizers = [] 418 self._button_apply_flag = [] 419 self._button_skip_flag = [] 420 self._buttons = [] 421 self._button_ids = [] 422 self._exec_on_next = [] 423 self._exec_count = [] 424 self._proceed_on_error = [] 425 self._uf_flush = [] 426 self._seq_fn_list = [] 427 self._seq_next = [] 428 self._seq_prev = [] 429 self._skip_flag = [] 430 431 # Flag to suppress later button addition. 432 self._buttons_built = False 433 434 # Bind some events. 435 self.Bind(wx.EVT_CLOSE, self._handler_close) 436 437 # ESC to exit, via an accelerator table which creates menu events. 438 self.acc_list = [(wx.ACCEL_NORMAL, wx.WXK_ESCAPE, ESC_ID)] 439 self.acc_table = wx.AcceleratorTable(self.acc_list) 440 self.SetAcceleratorTable(self.acc_table) 441 self.Bind(wx.EVT_MENU, self._handler_escape, id=ESC_ID)
442 443
444 - def _apply(self, event=None):
445 """Execute the current page's 'Apply' method. 446 447 @keyword event: The wx event. 448 @type event: wx event 449 """ 450 451 # Execute the current page's apply() method. 452 self._pages[self._current_page]._apply()
453 454
455 - def _build_buttons(self):
456 """Construct the buttons for all pages of the wizard.""" 457 458 # Loop over each page. 459 for i in range(self._num_pages): 460 # The back button (only for multi-pages, after the first). 461 if self._num_pages > 1 and i > 0: 462 # Create the button. 463 button = buttons.ThemedGenBitmapTextButton(self, -1, None, self.TEXT_BACK) 464 button.SetBitmapLabel(wx.Bitmap(self.ICON_BACK, wx.BITMAP_TYPE_ANY)) 465 button.SetFont(font.normal) 466 button.SetToolTipString("Return to the previous page") 467 button.SetMinSize(self._size_button) 468 self._button_sizers[i].Add(button, 0, wx.ADJUST_MINSIZE, 0) 469 self.Bind(wx.EVT_BUTTON, self._go_back, button) 470 self._buttons[i]['back'] = button 471 472 # Spacer. 473 self._button_sizers[i].AddSpacer(5) 474 475 # The apply button. 476 if self._button_apply_flag[i]: 477 # Create the button. 478 button = buttons.ThemedGenBitmapTextButton(self, -1, None, self.TEXT_APPLY) 479 button.SetBitmapLabel(wx.Bitmap(self.ICON_APPLY, wx.BITMAP_TYPE_ANY)) 480 button.SetFont(font.normal) 481 button.SetToolTipString("Apply the operation") 482 button.SetMinSize(self._size_button) 483 self._button_sizers[i].Add(button, 0, wx.ADJUST_MINSIZE, 0) 484 self.Bind(wx.EVT_BUTTON, self._pages[i]._apply, button) 485 self._buttons[i]['apply'] = button 486 487 # Spacer. 488 self._button_sizers[i].AddSpacer(5) 489 490 # The skip button. 491 if self._button_skip_flag[i]: 492 # Create the button. 493 button = buttons.ThemedGenBitmapTextButton(self, -1, None, self.TEXT_SKIP) 494 button.SetBitmapLabel(wx.Bitmap(self.ICON_SKIP, wx.BITMAP_TYPE_ANY)) 495 button.SetFont(font.normal) 496 button.SetToolTipString("Skip the operation") 497 button.SetMinSize(self._size_button) 498 self._button_sizers[i].Add(button, 0, wx.ADJUST_MINSIZE, 0) 499 self.Bind(wx.EVT_BUTTON, self._skip, button) 500 self._buttons[i]['skip'] = button 501 502 # Spacer. 503 self._button_sizers[i].AddSpacer(5) 504 505 # The next button (only for multi-pages, excluding the last). 506 if self._num_pages > 1 and i < self._num_pages - 1: 507 # Create the button. 508 button = buttons.ThemedGenBitmapTextButton(self, -1, None, self.TEXT_NEXT) 509 button.SetBitmapLabel(wx.Bitmap(self.ICON_NEXT, wx.BITMAP_TYPE_ANY)) 510 button.SetFont(font.normal) 511 button.SetToolTipString("Move to the next page") 512 button.SetMinSize(self._size_button) 513 self._button_sizers[i].Add(button, 0, wx.ADJUST_MINSIZE, 0) 514 self.Bind(wx.EVT_BUTTON, self._go_next, button) 515 self._buttons[i]['next'] = button 516 517 # The OK button (only for single pages). 518 if self._num_pages == 1: 519 button = buttons.ThemedGenBitmapTextButton(self, -1, None, self.TEXT_OK) 520 button.SetBitmapLabel(wx.Bitmap(self.ICON_OK, wx.BITMAP_TYPE_ANY)) 521 button.SetFont(font.normal) 522 button.SetToolTipString("Accept the operation") 523 button.SetMinSize(self._size_button) 524 self._button_sizers[i].Add(button, 0, wx.ADJUST_MINSIZE, 0) 525 self.Bind(wx.EVT_BUTTON, self._ok, button) 526 self._buttons[i]['ok'] = button 527 528 # The finish button (only for the last page with multi-pages). 529 if self._num_pages > 1 and i == self._num_pages - 1: 530 button = buttons.ThemedGenBitmapTextButton(self, -1, None, self.TEXT_FINISH) 531 button.SetBitmapLabel(wx.Bitmap(self.ICON_FINISH, wx.BITMAP_TYPE_ANY)) 532 button.SetFont(font.normal) 533 button.SetToolTipString("Accept the operation") 534 button.SetMinSize(self._size_button) 535 self._button_sizers[i].Add(button, 0, wx.ADJUST_MINSIZE, 0) 536 self.Bind(wx.EVT_BUTTON, self._ok, button) 537 self._buttons[i]['finish'] = button 538 539 # Spacer. 540 self._button_sizers[i].AddSpacer(15) 541 542 # The cancel button. 543 button = buttons.ThemedGenBitmapTextButton(self, -1, None, self.TEXT_CANCEL) 544 button.SetBitmapLabel(wx.Bitmap(self.ICON_CANCEL, wx.BITMAP_TYPE_ANY)) 545 button.SetFont(font.normal) 546 button.SetToolTipString("Abort the operation") 547 button.SetMinSize(self._size_button) 548 self._button_sizers[i].Add(button, 0, wx.ADJUST_MINSIZE, 0) 549 self.Bind(wx.EVT_BUTTON, self._cancel, button) 550 self._buttons[i]['cancel'] = button 551 552 # Flag to suppress later button addition. 553 self._buttons_built = True
554 555
556 - def _cancel(self, event=None):
557 """Cancel the operation. 558 559 @keyword event: The wx event. 560 @type event: wx event 561 """ 562 563 # Execute the page's on_next() method to allow the page to clean itself up. 564 self._pages[self._current_page].on_next() 565 566 # Close the window. 567 self.Close()
568 569
570 - def _display_page(self, i):
571 """Display the given page. 572 573 @param i: The index of the page to display. 574 @type i: int 575 """ 576 577 # Hide all of the original contents. 578 for j in range(self._num_pages): 579 if self._main_sizer.IsShown(self._page_sizers[j]): 580 self._main_sizer.Hide(self._page_sizers[j]) 581 582 # Show the desired page. 583 if status.show_gui: 584 self._main_sizer.Show(self._page_sizers[i]) 585 586 # Execute the page's on_display() method. 587 self._pages[i].on_display() 588 self._pages[i].on_display_post() 589 590 # Re-perform the window layout. 591 self.Layout() 592 self.Refresh() 593 594 # Execute the page's on_init() method. 595 self._pages[i].on_init() 596 597 # Set the focus to this page to allow the keyboard to be functional without a mouse click. 598 self._pages[i].SetFocus()
599 600
601 - def _go_back(self, event=None):
602 """Return to the previous page. 603 604 @keyword event: The wx event. 605 @type event: wx event 606 """ 607 608 # Execute the page's on_next() method. 609 self._pages[self._current_page].on_back() 610 611 # Work back in the sequence. 612 self._current_page = self._seq_prev[self._current_page] 613 614 # Display the previous page. 615 self._display_page(self._current_page)
616 617
618 - def _go_next(self, event=None):
619 """Move to the next page. 620 621 @keyword event: The wx event. 622 @type event: wx event 623 """ 624 625 # Execute the page's on_next() method. 626 self._pages[self._current_page].on_next() 627 628 # Operations for non-skipped pages. 629 if not self._skip_flag[self._current_page]: 630 # Execute the page's on_execute() method (via the _apply() method). 631 if self._exec_on_next[self._current_page]: 632 self._pages[self._current_page]._apply(event) 633 634 # UF flush. 635 if self._uf_flush[self._current_page]: 636 interpreter.flush() 637 638 # Check for execution errors. 639 if not self._pages[self._current_page].exec_status: 640 # Do not proceed. 641 if not self._proceed_on_error[self._current_page]: 642 return 643 644 # Increment the execution counter. 645 self._exec_count[self._current_page] += 1 646 647 # Determine the next page. 648 next_page = self._seq_fn_list[self._current_page]() 649 650 # No next page, so terminate. 651 if next_page >= len(self._pages): 652 self._ok(None) 653 return 654 655 # Update the sequence lists. 656 self._seq_next[self._current_page] = next_page 657 self._seq_prev[next_page] = self._current_page 658 659 # Change the current page. 660 self._current_page = next_page 661 662 # Display the next page. 663 self._display_page(self._current_page)
664 665
666 - def _handler_close(self, event=None):
667 """Event handler for the close window action. 668 669 @keyword event: The wx event. 670 @type event: wx event 671 """ 672 673 # Execute the page's on_next() method to allow the page to clean itself up. 674 self._pages[self._current_page].on_next() 675 676 # Continue with the window closing. 677 event.Skip()
678 679
680 - def _handler_escape(self, event=None):
681 """Event handler for key strokes. 682 683 @keyword event: The wx event. 684 @type event: wx event 685 """ 686 687 # Close the window. 688 self.Close()
689 690
691 - def _next_fn(self):
692 """Standard function for setting the next page to the one directly next in the sequence. 693 694 @return: The index of the next page, which is the current page index plus one. 695 @rtype: int 696 """ 697 698 # Return the next page. 699 return self._current_page + 1
700 701
702 - def _ok(self, event=None):
703 """Accept the operation. 704 705 @keyword event: The wx event. 706 @type event: wx event 707 """ 708 709 # Loop over the pages in the sequence and execute their _apply() methods, if not already done and not skipped. 710 for i in self._seq_loop(): 711 if not self._exec_count[i] and not self._skip_flag[i]: 712 # Execute the _apply method. 713 self._pages[i]._apply(event) 714 715 # UF flush. 716 if self._uf_flush[i]: 717 interpreter.flush() 718 719 # Check for execution errors. 720 if not self._pages[self._current_page].exec_status: 721 # Do not proceed. 722 if not self._proceed_on_error[self._current_page]: 723 return 724 725 # Increment the execution counter. 726 self._exec_count[i] += 1 727 728 # Execute the current page's on_next() method to allow the page to clean itself up. 729 self._pages[self._current_page].on_next() 730 731 # Then close the dialog. 732 if self.IsModal(): 733 self.EndModal(wx.ID_OK) 734 else: 735 self.Close()
736 737
738 - def _seq_loop(self):
739 """Loop over the sequence in the forwards direction.""" 740 741 # Initialise. 742 current = 0 743 744 # First yield the initial element (always zero!). 745 yield current 746 747 # Loop over the sequence. 748 while True: 749 # Update. 750 next = self._seq_next[current] 751 current = next 752 753 # End of the sequence. 754 if next == None: 755 break 756 757 # Yield the next index. 758 yield next
759 760
761 - def _skip(self, event=None):
762 """Skip the page. 763 764 @keyword event: The wx event. 765 @type event: wx event 766 """ 767 768 # Set the skip flag. 769 self._skip_flag[self._current_page] = True 770 771 # Go to the next page. 772 self._go_next(None)
773 774
775 - def Destroy(self):
776 """Override the default wx.Dialog.Destroy() method.""" 777 778 # Call the parent method to close the dialog. 779 self.Close() 780 781 # Loop over each page, destroying it and all its elements to avoid memory leaks. 782 for i in range(len(self._buttons)): 783 # Destroy the buttons. 784 for name in self._buttons[i]: 785 if hasattr(self._buttons[i][name], 'Destroy'): 786 self._buttons[i][name].Destroy() 787 self._buttons[i][name] = None 788 789 # Destroy each page. 790 if hasattr(self._pages[i], 'Destroy'): 791 self._pages[i].Destroy() 792 self._pages[i] = None 793 794 # Call the parent method to destroy the dialog. 795 super(Wiz_window, self).DestroyChildren() 796 super(Wiz_window, self).Destroy()
797 798
799 - def add_page(self, panel, apply_button=True, skip_button=False, exec_on_next=True, proceed_on_error=True, uf_flush=False):
800 """Add a new page to the wizard. 801 802 @param panel: The page to add to the wizard. 803 @type panel: wx.Panel instance 804 @keyword apply_button: A flag which if true will show the apply button for that page. 805 @type apply_button: bool 806 @keyword skip_button: A flag which if true will show the skip button for that page. 807 @type skip_button: bool 808 @keyword exec_on_next: A flag which if true will run the on_execute() method when clicking on the next button. 809 @type exec_on_next: bool 810 @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). 811 @type proceed_on_error: bool 812 @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. 813 @type uf_flush: bool 814 @return: The index of the page in the wizard. 815 @rtype: int 816 """ 817 818 # Store the page. 819 index = self._num_pages 820 self._num_pages += 1 821 self._pages.append(panel) 822 823 # Initialise all box sizers for the wizard page, and store them. 824 self._page_sizers.append(wx.BoxSizer(wx.VERTICAL)) 825 self._main_sizer.Add(self._page_sizers[index], 1, wx.ALL|wx.EXPAND, 0) 826 827 # Add the sizer for the top half. 828 self._top_sizers.append(wx.BoxSizer(wx.VERTICAL)) 829 self._page_sizers[index].Add(self._top_sizers[index], 1, wx.ALL|wx.EXPAND, 0) 830 831 # Add the page to the top sizer. 832 self._top_sizers[index].Add(panel, 1, wx.ALL|wx.EXPAND, 0) 833 834 # Initialise all box sizers for the buttons, and store them. 835 self._button_sizers.append(wx.BoxSizer(wx.HORIZONTAL)) 836 837 # Add the sizer for the wizard buttons. 838 self._page_sizers[index].Add(self._button_sizers[index], 0, wx.ALIGN_RIGHT|wx.ALL, 0) 839 840 # Store all button flags. 841 self._button_apply_flag.append(apply_button) 842 self._button_skip_flag.append(skip_button) 843 844 # Initialise the button storage. 845 self._buttons.append({'back': None, 846 'apply': None, 847 'next': None, 848 'ok': None, 849 'finish': None, 850 'cancel': None}) 851 852 # Initialise a set of unique button IDs. 853 self._button_ids.append({'back': -1, 854 'apply': -1, 855 'next': -1, 856 'ok': -1, 857 'finish': -1, 858 'cancel': -1}) 859 860 # Execute on next by default. 861 self._exec_on_next.append(exec_on_next) 862 863 # Execution count. 864 self._exec_count.append(0) 865 866 # Proceed to next page on errors by default. 867 self._proceed_on_error.append(proceed_on_error) 868 869 # User function flushing of the GUI interpreter thread prior to proceeding. 870 if not proceed_on_error or uf_flush: 871 self._uf_flush.append(True) 872 else: 873 self._uf_flush.append(False) 874 875 # Page sequence initialisation. 876 self._seq_fn_list.append(self._next_fn) 877 self._seq_next.append(None) 878 self._seq_prev.append(None) 879 880 # Page skipping. 881 self._skip_flag.append(False) 882 883 # Store the index of the page. 884 panel.page_index = index 885 886 # Return the index of the page. 887 return index
888 889
890 - def block_next(self, block=True):
891 """Prevent moving forwards (or unblock). 892 893 @keyword block: A flag which if True will block forwards movement and if False will unblock. 894 @type block: bool 895 """ 896 897 # The buttons to disable. 898 buttons = ['next', 'ok', 'finish'] 899 900 # Disable or enable the buttons. 901 for i in range(len(buttons)): 902 # The button. 903 button = self._buttons[self._current_page][buttons[i]] 904 if button == None: 905 continue 906 907 # Block. 908 if block: 909 button.Disable() 910 911 # Unblock. 912 else: 913 button.Enable()
914 915
916 - def get_page(self, index):
917 """Get a page from the wizard. 918 919 @param index: The index of the page. 920 @type index: int 921 @return: The page object. 922 @rtype: Wiz_page instance. 923 """ 924 925 # Return the page. 926 return self._pages[index]
927 928
929 - def reset(self):
930 """Reset the wizard.""" 931 932 # Clear the execution counts. 933 for i in range(len(self._exec_count)): 934 self._exec_count[i] = 0
935 936
937 - def run(self, modal=False):
938 """Execute the wizard. 939 940 @keyword modal: A flag which if True will cause the wizard to be run as a modal dialog. 941 @type modal: bool 942 @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. 943 @rtype: bool or None 944 """ 945 946 # Check that all pages have been set up correctly, returning without doing anything if not. 947 for i in range(self._num_pages): 948 if self._pages[i].setup_fail: 949 return 950 951 # Build the buttons for the entire wizard. 952 if not self._buttons_built: 953 self._build_buttons() 954 955 # Display the first page. 956 self._display_page(0) 957 958 # Display failure. 959 if self._pages[0].setup_fail: 960 return 961 962 # No GUI. 963 if not status.show_gui: 964 return 965 966 # Modal operation. 967 if modal: 968 # Show the wizard (it should be closed by the _cancel() or _ok() methods). 969 wiz_status = self.ShowModal() 970 971 # Return the status. 972 return wiz_status 973 974 # Modeless operation. 975 else: 976 # Show the wizard. 977 self.Show()
978 979
980 - def set_seq_next_fn(self, index, fn):
981 """A user specified function for non-linear page changing. 982 983 @param index: The index of the page the function should be associated with. 984 @type index: int 985 @param fn: The function for determining the page after the current. This function should return the index of the next page. 986 @type fn: func or method. 987 """ 988 989 # Store the function. 990 self._seq_fn_list[index] = fn
991 992
993 - def setup_page(self, page=None, **kargs):
994 """Allow a specified user function page to be remotely set up. 995 996 @keyword page: The page to setup. This is the page index key. 997 @type page: str 998 """ 999 1000 # Get the page. 1001 page = self.get_page(self.page_indices[page]) 1002 1003 # Loop over the keyword arguments and set them. 1004 for arg in kargs: 1005 # The value. 1006 value = kargs[arg] 1007 if isinstance(value, str): 1008 value = str_to_gui(value) 1009 elif is_float(value): 1010 value = float_to_gui(value) 1011 1012 # Set the argument. 1013 page.SetValue(arg, value)
1014