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