wxWizard

From WxWiki
Jump to: navigation, search
Official Classes SmallBlocks.png Archive Containers Controls Data Structures Database Date & Time Debug Device Contexts Dialogs Document & Views Drag & Drop Events Filesystem Frames Graphics Grid Cell Help HTML Logging Miscellaneous Networking Printing Sizers Streams Threading Windows

wxWizard is the central class for implementing 'wizard-like' dialogs.

These dialogs are mostly familiar to Windows users and are nothing other than a sequence of 'pages', each displayed inside a dialog which has the buttons to navigate to the next (and previous) pages.

The wizards are typically used to decompose a complex dialog into several simple steps and are mainly useful to the novice users, hence it is important to keep them as simple as possible.

To show a wizard dialog, you must first create an instance of the wxWizard class using either the non-default constructor or a default one followed by call to the wxWizard::Create function. Then you should add all pages you want the wizard to show and call wxWizard::RunWizard(). Finally, don't forget to call "wizard->Destroy()", otherwise your application will hang on exit due to an undestroyed window.

You can supply a bitmap to display on the left of the wizard, either for all pages or for individual pages. If you need to have the bitmap resize to the height of the wizard, call wxWizard::SetBitmapPlacement() and if necessary, wxWizard::SetBitmapBackgroundColour() and wxWizard::SetMinimumBitmapWidth().

To make wizard pages scroll when the display is too small to fit the whole dialog, you can switch layout adaptation on globally with wxDialog::EnableLayoutAdaptation() or per dialog with wxDialog::SetLayoutAdaptationMode(). For more about layout adaptation, see Automatic Scrolled Dialogs.

Disabling the "Next" button

Quoth Vadim: "You can do it simply by using "wxWindow::FindWindowById(wxID_FORWARD)->Disable()" In wxPython: wx.FindWindowById(wx.ID_FORWARD).Disable()


However, if the current focus is a single line textCtrl, I find that pressing Enter will still advance to the next page even though the Next button is disabled! (using wxPython 2.6.3.2 on Linux. It works as it should under Windows.) Any workarounds?

  • You might also want to catch wxEVT_WIZARD_PAGE_CHANGING events, and veto as necessary. --Tierra 16:09, 10 March 2007 (PST)

better way to enable/disable next/back buttons

inline void setControlEnable(int id, bool state)
{
	wxWindow *win = wxWindow::FindWindowById(id);
	if(win) win->Enable(state);
}

usage example in your code:

setControlEnable(wxID_FORWARD, false);
setControlEnable(wxID_BACKWARD, true);

Using wx.wizard.Wizard as your only window (updated for wxPython 2.5.1.5)

The trick is to construct your wx.App, then construct your wizard and call RunWizard() on it, and only the (once RunWizard() has returned) call wx.App.MainLoop() to clean up. Here is an example:

  import wx
  import wx.wizard as wiz
  
  def makePageTitle(wizPg, title):
      sizer = wx.BoxSizer(wx.VERTICAL)
      wizPg.SetSizer(sizer)
      title = wx.StaticText(wizPg, -1, title)
      title.SetFont(wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD))
      sizer.AddWindow(title, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
      sizer.AddWindow(wx.StaticLine(wizPg, -1), 0, wx.EXPAND|wx.ALL, 5)
      return sizer
  
  class TitledPage(wiz.WizardPageSimple):
      def __init__(self, parent, title):
          wiz.WizardPageSimple.__init__(self, parent)
          self.sizer = makePageTitle(self, title)
  
  app = wx.PySimpleApp()
  wizard = wiz.Wizard(None, -1, "Simple Wizard")
  page1 = TitledPage(wizard, "Page 1")
  page2 = TitledPage(wizard, "Page 2")
  wiz.WizardPageSimple_Chain(page1, page2)
  wizard.FitToPage(page1)
  wizard.RunWizard(page1)
  wizard.Destroy()
  app.MainLoop()

Using wxWizard as your only window

The trick is to construct your wxApp, then construct your wizard and call RunWizard() on it, and only then (once RunWizard() has returned) call wxApp.MainLoop() to clean up. Here is an example (from Robin Dunn):

from wxPython.wx import *
from wxPython.wizard import *

def makePageTitle(wizPg, title):
    sizer = wxBoxSizer(wxVERTICAL)
    wizPg.SetSizer(sizer)
    title = wxStaticText(wizPg, -1, title)
    title.SetFont(wxFont(18, wxSWISS, wxNORMAL, wxBOLD))
    sizer.AddWindow(title, 0, wxALIGN_CENTRE|wxALL, 5)
    sizer.AddWindow(wxStaticLine(wizPg, -1), 0, wxEXPAND|wxALL, 5)
    return sizer

class TitledPage(wxWizardPageSimple):
    def __init__(self, parent, title):
        wxWizardPageSimple.__init__(self, parent)
        self.sizer = makePageTitle(self, title)

app = wxPySimpleApp()
wizard = wxWizard(None, -1, "Simple Wizard")
page1 = TitledPage(wizard, "Page 1")
page2 = TitledPage(wizard, "Page 2")
wxWizardPageSimple_Chain(page1, page2)
wizard.FitToPage(page1)
wizard.RunWizard(page1)
wizard.Destroy()
app.MainLoop()

Calculate the size

For the simple cases the following procedure will do:

    void MyWizard::ComputeAndSetSize(wxWizardPage* startPagePtr)
    {
        wxSize size = startPagePtr->GetBestSize();
        for(wxWizardPage* pPtr = startPagePtr->GetNext();
            pPtr;
            pPtr = pPtr->GetNext())
        {
            wxSize tmpSize = pPtr->GetBestSize();
            if(tmpSize.GetHeight() > size.GetHeight())
                size.SetHeight(tmpSize.GetHeight());
            if(tmpSize.GetWidth() > size.GetWidth())
                size.SetWidth(tmpSize.GetWidth());
        }
        SetPageSize(size);
    }

Design tips

  • Use wxWizardPageSimple if the sequence of pages is static.
  • Use a subclass of either wxWizardPage or wxWizardPageSimple if you need anything more than just the basic logic.
  • If possible, define a common superclass for all your wizard pages. Apply to it a member "state" which allows you to control further aspects of the whole process.
    • Handle EVT_WIZARD_PAGE_CHANGING if your pages need information about the chosen direction. The event implements GetDirection(). You can transport this information by using the "state" member mentioned above.
    • Overwrite the Transfer* and/or Validate* methods to be independend of the default behaviour. You could for example skip any transfer of data to the page if the last direction was backwards in the chain of pages.
  • Use your own subclasses of wxPanel for each page content. It will be easy to re-use this panel in a dialog. Keep in mind that every single one of these panels should handle all events sent by itself and therefore might need an own event table. It's sometimes a good idea to provide actions (not handlers) for Ok/Cancel, too.

See Also