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,2019 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 # Single page or a wizard? 459 single_page = True 460 if self._num_pages > 1: 461 single_page = False 462 463 # Loop over each page. 464 for i in range(self._num_pages): 465 # The back button (only for multi-pages, after the first). 466 if not single_page and i > 0: 467 # Create the button. 468 button = buttons.ThemedGenBitmapTextButton(self, -1, None, self.TEXT_BACK) 469 button.SetBitmapLabel(wx.Bitmap(self.ICON_BACK, wx.BITMAP_TYPE_ANY)) 470 button.SetFont(font.normal) 471 button.SetToolTipString("Return to the previous page.") 472 button.SetMinSize(self._size_button) 473 self._button_sizers[i].Add(button, 0, wx.ADJUST_MINSIZE, 0) 474 self.Bind(wx.EVT_BUTTON, self._go_back, button) 475 self._buttons[i]['back'] = button 476 477 # Spacer. 478 self._button_sizers[i].AddSpacer(5) 479 480 # The apply button. 481 if self._button_apply_flag[i]: 482 # Create the button. 483 button = buttons.ThemedGenBitmapTextButton(self, -1, None, self.TEXT_APPLY) 484 button.SetBitmapLabel(wx.Bitmap(self.ICON_APPLY, wx.BITMAP_TYPE_ANY)) 485 button.SetFont(font.normal) 486 if single_page: 487 button.SetToolTipString("Apply the operation and leave the window open.") 488 else: 489 button.SetToolTipString("Apply the operation and stay on the current page.") 490 button.SetMinSize(self._size_button) 491 self._button_sizers[i].Add(button, 0, wx.ADJUST_MINSIZE, 0) 492 self.Bind(wx.EVT_BUTTON, self._pages[i]._apply, button) 493 self._buttons[i]['apply'] = button 494 495 # Spacer. 496 self._button_sizers[i].AddSpacer(5) 497 498 # The skip button. 499 if self._button_skip_flag[i]: 500 # Create the button. 501 button = buttons.ThemedGenBitmapTextButton(self, -1, None, self.TEXT_SKIP) 502 button.SetBitmapLabel(wx.Bitmap(self.ICON_SKIP, wx.BITMAP_TYPE_ANY)) 503 button.SetFont(font.normal) 504 button.SetToolTipString("Skip the operation and move to the next page.") 505 button.SetMinSize(self._size_button) 506 self._button_sizers[i].Add(button, 0, wx.ADJUST_MINSIZE, 0) 507 self.Bind(wx.EVT_BUTTON, self._skip, button) 508 self._buttons[i]['skip'] = button 509 510 # Spacer. 511 self._button_sizers[i].AddSpacer(5) 512 513 # The next button (only for multi-pages, excluding the last). 514 if not single_page and i < self._num_pages - 1: 515 # Create the button. 516 button = buttons.ThemedGenBitmapTextButton(self, -1, None, self.TEXT_NEXT) 517 button.SetBitmapLabel(wx.Bitmap(self.ICON_NEXT, wx.BITMAP_TYPE_ANY)) 518 button.SetFont(font.normal) 519 button.SetToolTipString("Apply the operation and move to the next page.") 520 button.SetMinSize(self._size_button) 521 self._button_sizers[i].Add(button, 0, wx.ADJUST_MINSIZE, 0) 522 self.Bind(wx.EVT_BUTTON, self._go_next, button) 523 self._buttons[i]['next'] = button 524 525 # The OK button (only for single pages). 526 if single_page: 527 button = buttons.ThemedGenBitmapTextButton(self, -1, None, self.TEXT_OK) 528 button.SetBitmapLabel(wx.Bitmap(self.ICON_OK, wx.BITMAP_TYPE_ANY)) 529 button.SetFont(font.normal) 530 button.SetToolTipString("Apply the operation and close the window.") 531 button.SetMinSize(self._size_button) 532 self._button_sizers[i].Add(button, 0, wx.ADJUST_MINSIZE, 0) 533 self.Bind(wx.EVT_BUTTON, self._ok, button) 534 self._buttons[i]['ok'] = button 535 536 # The finish button (only for the last page with multi-pages). 537 if not single_page and i == self._num_pages - 1: 538 button = buttons.ThemedGenBitmapTextButton(self, -1, None, self.TEXT_FINISH) 539 button.SetBitmapLabel(wx.Bitmap(self.ICON_FINISH, wx.BITMAP_TYPE_ANY)) 540 button.SetFont(font.normal) 541 button.SetToolTipString("Apply the operation and close the wizard.") 542 button.SetMinSize(self._size_button) 543 self._button_sizers[i].Add(button, 0, wx.ADJUST_MINSIZE, 0) 544 self.Bind(wx.EVT_BUTTON, self._ok, button) 545 self._buttons[i]['finish'] = button 546 547 # Spacer. 548 self._button_sizers[i].AddSpacer(15) 549 550 # The cancel button. 551 button = buttons.ThemedGenBitmapTextButton(self, -1, None, self.TEXT_CANCEL) 552 button.SetBitmapLabel(wx.Bitmap(self.ICON_CANCEL, wx.BITMAP_TYPE_ANY)) 553 button.SetFont(font.normal) 554 if single_page: 555 button.SetToolTipString("Abort the operation and close the window.") 556 else: 557 button.SetToolTipString("Abort the operation and close the wizard.") 558 button.SetMinSize(self._size_button) 559 self._button_sizers[i].Add(button, 0, wx.ADJUST_MINSIZE, 0) 560 self.Bind(wx.EVT_BUTTON, self._cancel, button) 561 self._buttons[i]['cancel'] = button 562 563 # Flag to suppress later button addition. 564 self._buttons_built = True
565 566
567 - def _cancel(self, event=None):
568 """Cancel the operation. 569 570 @keyword event: The wx event. 571 @type event: wx event 572 """ 573 574 # Execute the page's on_next() method to allow the page to clean itself up. 575 self._pages[self._current_page].on_next() 576 577 # Close the window. 578 self.Close()
579 580
581 - def _display_page(self, i):
582 """Display the given page. 583 584 @param i: The index of the page to display. 585 @type i: int 586 """ 587 588 # Hide all of the original contents. 589 for j in range(self._num_pages): 590 if self._main_sizer.IsShown(self._page_sizers[j]): 591 self._main_sizer.Hide(self._page_sizers[j]) 592 593 # Show the desired page. 594 if status.show_gui: 595 self._main_sizer.Show(self._page_sizers[i]) 596 597 # Execute the page's on_display() method. 598 self._pages[i].on_display() 599 self._pages[i].on_display_post() 600 601 # Re-perform the window layout. 602 self.Layout() 603 self.Refresh() 604 605 # Execute the page's on_init() method. 606 self._pages[i].on_init() 607 608 # Set the focus to this page to allow the keyboard to be functional without a mouse click. 609 self._pages[i].SetFocus()
610 611
612 - def _go_back(self, event=None):
613 """Return to the previous page. 614 615 @keyword event: The wx event. 616 @type event: wx event 617 """ 618 619 # Execute the page's on_next() method. 620 self._pages[self._current_page].on_back() 621 622 # Work back in the sequence. 623 self._current_page = self._seq_prev[self._current_page] 624 625 # Display the previous page. 626 self._display_page(self._current_page)
627 628
629 - def _go_next(self, event=None):
630 """Move to the next page. 631 632 @keyword event: The wx event. 633 @type event: wx event 634 """ 635 636 # Execute the page's on_next() method. 637 self._pages[self._current_page].on_next() 638 639 # Operations for non-skipped pages. 640 if not self._skip_flag[self._current_page]: 641 # Execute the page's on_execute() method (via the _apply() method). 642 if self._exec_on_next[self._current_page]: 643 self._pages[self._current_page]._apply(event) 644 645 # UF flush. 646 if self._uf_flush[self._current_page]: 647 interpreter.flush() 648 649 # Check for execution errors. 650 if not self._pages[self._current_page].exec_status: 651 # Do not proceed. 652 if not self._proceed_on_error[self._current_page]: 653 return 654 655 # Increment the execution counter. 656 self._exec_count[self._current_page] += 1 657 658 # Determine the next page. 659 next_page = self._seq_fn_list[self._current_page]() 660 661 # No next page, so terminate. 662 if next_page >= len(self._pages): 663 self._ok(None) 664 return 665 666 # Update the sequence lists. 667 self._seq_next[self._current_page] = next_page 668 self._seq_prev[next_page] = self._current_page 669 670 # Change the current page. 671 self._current_page = next_page 672 673 # Display the next page. 674 self._display_page(self._current_page)
675 676
677 - def _handler_close(self, event=None):
678 """Event handler for the close window action. 679 680 @keyword event: The wx event. 681 @type event: wx event 682 """ 683 684 # Execute the page's on_next() method to allow the page to clean itself up. 685 self._pages[self._current_page].on_next() 686 687 # Continue with the window closing. 688 event.Skip()
689 690
691 - def _handler_escape(self, event=None):
692 """Event handler for key strokes. 693 694 @keyword event: The wx event. 695 @type event: wx event 696 """ 697 698 # Close the window. 699 self.Close()
700 701
702 - def _next_fn(self):
703 """Standard function for setting the next page to the one directly next in the sequence. 704 705 @return: The index of the next page, which is the current page index plus one. 706 @rtype: int 707 """ 708 709 # Return the next page. 710 return self._current_page + 1
711 712
713 - def _ok(self, event=None):
714 """Accept the operation. 715 716 @keyword event: The wx event. 717 @type event: wx event 718 """ 719 720 # Loop over the pages in the sequence and execute their _apply() methods, if not already done and not skipped. 721 for i in self._seq_loop(): 722 if not self._exec_count[i] and not self._skip_flag[i]: 723 # Execute the _apply method. 724 self._pages[i]._apply(event) 725 726 # UF flush. 727 if self._uf_flush[i]: 728 interpreter.flush() 729 730 # Check for execution errors. 731 if not self._pages[self._current_page].exec_status: 732 # Do not proceed. 733 if not self._proceed_on_error[self._current_page]: 734 return 735 736 # Increment the execution counter. 737 self._exec_count[i] += 1 738 739 # Execute the current page's on_next() method to allow the page to clean itself up. 740 self._pages[self._current_page].on_next() 741 742 # Then close the dialog. 743 if self.IsModal(): 744 self.EndModal(wx.ID_OK) 745 else: 746 self.Close()
747 748
749 - def _seq_loop(self):
750 """Loop over the sequence in the forwards direction.""" 751 752 # Initialise. 753 current = 0 754 755 # First yield the initial element (always zero!). 756 yield current 757 758 # Loop over the sequence. 759 while True: 760 # Update. 761 next = self._seq_next[current] 762 current = next 763 764 # End of the sequence. 765 if next == None: 766 break 767 768 # Yield the next index. 769 yield next
770 771
772 - def _skip(self, event=None):
773 """Skip the page. 774 775 @keyword event: The wx event. 776 @type event: wx event 777 """ 778 779 # Set the skip flag. 780 self._skip_flag[self._current_page] = True 781 782 # Go to the next page. 783 self._go_next(None)
784 785
786 - def Destroy(self):
787 """Override the default wx.Dialog.Destroy() method.""" 788 789 # Call the parent method to close the dialog. 790 self.Close() 791 792 # Loop over each page, destroying it and all its elements to avoid memory leaks. 793 for i in range(len(self._buttons)): 794 # Destroy the buttons. 795 for name in self._buttons[i]: 796 if hasattr(self._buttons[i][name], 'Destroy'): 797 self._buttons[i][name].Destroy() 798 self._buttons[i][name] = None 799 800 # Destroy each page. 801 if hasattr(self._pages[i], 'Destroy'): 802 self._pages[i].Destroy() 803 self._pages[i] = None 804 805 # Call the parent method to destroy the dialog. 806 super(Wiz_window, self).DestroyChildren() 807 super(Wiz_window, self).Destroy()
808 809
810 - def add_page(self, panel, apply_button=True, skip_button=False, exec_on_next=True, proceed_on_error=True, uf_flush=False):
811 """Add a new page to the wizard. 812 813 @param panel: The page to add to the wizard. 814 @type panel: wx.Panel instance 815 @keyword apply_button: A flag which if true will show the apply button for that page. 816 @type apply_button: bool 817 @keyword skip_button: A flag which if true will show the skip button for that page. 818 @type skip_button: bool 819 @keyword exec_on_next: A flag which if true will run the on_execute() method when clicking on the next button. 820 @type exec_on_next: bool 821 @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). 822 @type proceed_on_error: bool 823 @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. 824 @type uf_flush: bool 825 @return: The index of the page in the wizard. 826 @rtype: int 827 """ 828 829 # Store the page. 830 index = self._num_pages 831 self._num_pages += 1 832 self._pages.append(panel) 833 834 # Initialise all box sizers for the wizard page, and store them. 835 self._page_sizers.append(wx.BoxSizer(wx.VERTICAL)) 836 self._main_sizer.Add(self._page_sizers[index], 1, wx.ALL|wx.EXPAND, 0) 837 838 # Add the sizer for the top half. 839 self._top_sizers.append(wx.BoxSizer(wx.VERTICAL)) 840 self._page_sizers[index].Add(self._top_sizers[index], 1, wx.ALL|wx.EXPAND, 0) 841 842 # Add the page to the top sizer. 843 self._top_sizers[index].Add(panel, 1, wx.ALL|wx.EXPAND, 0) 844 845 # Initialise all box sizers for the buttons, and store them. 846 self._button_sizers.append(wx.BoxSizer(wx.HORIZONTAL)) 847 848 # Add the sizer for the wizard buttons. 849 self._page_sizers[index].Add(self._button_sizers[index], 0, wx.ALIGN_RIGHT|wx.ALL, 0) 850 851 # Store all button flags. 852 self._button_apply_flag.append(apply_button) 853 self._button_skip_flag.append(skip_button) 854 855 # Initialise the button storage. 856 self._buttons.append({'back': None, 857 'apply': None, 858 'next': None, 859 'ok': None, 860 'finish': None, 861 'cancel': None}) 862 863 # Initialise a set of unique button IDs. 864 self._button_ids.append({'back': -1, 865 'apply': -1, 866 'next': -1, 867 'ok': -1, 868 'finish': -1, 869 'cancel': -1}) 870 871 # Execute on next by default. 872 self._exec_on_next.append(exec_on_next) 873 874 # Execution count. 875 self._exec_count.append(0) 876 877 # Proceed to next page on errors by default. 878 self._proceed_on_error.append(proceed_on_error) 879 880 # User function flushing of the GUI interpreter thread prior to proceeding. 881 if not proceed_on_error or uf_flush: 882 self._uf_flush.append(True) 883 else: 884 self._uf_flush.append(False) 885 886 # Page sequence initialisation. 887 self._seq_fn_list.append(self._next_fn) 888 self._seq_next.append(None) 889 self._seq_prev.append(None) 890 891 # Page skipping. 892 self._skip_flag.append(False) 893 894 # Store the index of the page. 895 panel.page_index = index 896 897 # Return the index of the page. 898 return index
899 900
901 - def block_next(self, block=True):
902 """Prevent moving forwards (or unblock). 903 904 @keyword block: A flag which if True will block forwards movement and if False will unblock. 905 @type block: bool 906 """ 907 908 # The buttons to disable. 909 buttons = ['next', 'ok', 'finish'] 910 911 # Disable or enable the buttons. 912 for i in range(len(buttons)): 913 # The button. 914 button = self._buttons[self._current_page][buttons[i]] 915 if button == None: 916 continue 917 918 # Block. 919 if block: 920 button.Disable() 921 922 # Unblock. 923 else: 924 button.Enable()
925 926
927 - def get_page(self, index):
928 """Get a page from the wizard. 929 930 @param index: The index of the page. 931 @type index: int 932 @return: The page object. 933 @rtype: Wiz_page instance. 934 """ 935 936 # Return the page. 937 return self._pages[index]
938 939
940 - def reset(self):
941 """Reset the wizard.""" 942 943 # Clear the execution counts. 944 for i in range(len(self._exec_count)): 945 self._exec_count[i] = 0
946 947
948 - def run(self, modal=False):
949 """Execute the wizard. 950 951 @keyword modal: A flag which if True will cause the wizard to be run as a modal dialog. 952 @type modal: bool 953 @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. 954 @rtype: bool or None 955 """ 956 957 # Check that all pages have been set up correctly, returning without doing anything if not. 958 for i in range(self._num_pages): 959 if self._pages[i].setup_fail: 960 return 961 962 # Build the buttons for the entire wizard. 963 if not self._buttons_built: 964 self._build_buttons() 965 966 # Display the first page. 967 self._display_page(0) 968 969 # Display failure. 970 if self._pages[0].setup_fail: 971 return 972 973 # No GUI. 974 if not status.show_gui: 975 return 976 977 # Modal operation. 978 if modal: 979 # Show the wizard (it should be closed by the _cancel() or _ok() methods). 980 wiz_status = self.ShowModal() 981 982 # Return the status. 983 return wiz_status 984 985 # Modeless operation. 986 else: 987 # Show the wizard. 988 self.Show()
989 990
991 - def set_seq_next_fn(self, index, fn):
992 """A user specified function for non-linear page changing. 993 994 @param index: The index of the page the function should be associated with. 995 @type index: int 996 @param fn: The function for determining the page after the current. This function should return the index of the next page. 997 @type fn: func or method. 998 """ 999 1000 # Store the function. 1001 self._seq_fn_list[index] = fn
1002 1003
1004 - def setup_page(self, page=None, **kargs):
1005 """Allow a specified user function page to be remotely set up. 1006 1007 @keyword page: The page to setup. This is the page index key. 1008 @type page: str 1009 """ 1010 1011 # Get the page. 1012 page = self.get_page(self.page_indices[page]) 1013 1014 # Loop over the keyword arguments and set them. 1015 for arg in kargs: 1016 # The value. 1017 value = kargs[arg] 1018 if isinstance(value, str): 1019 value = str_to_gui(value) 1020 elif is_float(value): 1021 value = float_to_gui(value) 1022 1023 # Set the argument. 1024 page.SetValue(arg, value)
1025