wxWizard

team bbl


The wizard is a great way to break a complex set of choices and settings down into a sequence of simple dialogs. It can be presented to novice users to help them get started with a particular feature in an application, such as gathering information for a new project, exporting data, and so on. Often the settings presented in a wizard can be altered elsewhere in the application's user interface, but presenting them in a wizard focuses the user on the essentials for getting a specific task completed.

A wizard comprises a series of dialog-like pages set inside a window that normally has an image on the left (the same for all pages, or different for each page), and a row of buttons along the bottom for navigating between pages and getting help. As the user progresses through the wizard, the old page is hidden and a new one is shown. The path through a wizard can be determined by choices the user makes, so not all available pages are necessarily shown each time a wizard is presented.

When the standard wizard buttons are pressed, events are sent to the pages (and to the wxWizard object). You can catch events either in the page class or in a class derived from wxWizard.

To show a wizard, create an instance of wxWizard (or a derived class) and create the pages as children of the wizard. You can use wxWizardPageSimple (or a derived class) and chain the pages together with wxWizardPageSimple::Chain. Or, if you need to determine the path through the wizard dynamically according to user selections, you can derive from wxWizardPage and override GetPrev and GetNext. Add each page to the sizer returned by GetPageAreaSizer so that the wizard can adapt its size to the largest page.

wxWizard's only special window style is wxWIZARD_EX_HELPBUTTON, which adds a Help button to the wizard's row of standard buttons. This is an "extra" style, which must be set with SetExtraStyle before Create is called.

wxWizard Events

wxWizard generates wxWizardEvent events, which are described in Table 12-7. These events are sent first to the page, and if not processed, to the wizard itself. Except for EVT_WIZARD_FINISHED, event handlers can call wxWizard Event::GetPage to determine the currently active page.

Table 12-7. wxWizard Events

EVT_WIZARD_PAGE_CHANGED(id, func)

Use this event to detect when a page has been changed. The event handler function can call wxWizardEvent:: GetDirection (true if going forward).

EVT_WIZARD_PAGE_CHANGING(id, func)

Use to detect when a page is about to be changed (including when the Finish button was clicked); the event can be vetoed. The event handler function can call wxWizardEvent::GetDirection (true if going forward).

EVT_WIZARD_CANCEL(id, func)

Used to detect when the user has clicked the Cancel button; this can be vetoed.

EVT_WIZARD_HELP(id, func)

Use to show help when the user clicks on the Help button.

EVT_WIZARD_FINISHED(id, func)

Use to react to the user clicking on the Finish button. This event is generated just after the dialog has been closed.


wxWizard Member Functions

These are the main member functions for wxWizard.

GetPageAreaSizer returns the sizer that manages the page area. Add all pages to this sizer, or one page from which the others can be reached with GetNext, to make the wizard size itself according to the maximum page size. If you don't do this, you should call FitToPage for the first page before running the wizard, or for all pages if calling wxWizardPage::GetNext might not visit all pages.

GetCurrentPage returns the current active page, or NULL if RunWizard is not executing.

GetPageSize returns the size available for all pages. You can use SetPageSize to set the page size used by all pages, but this is deprecated in favor of adding pages to the sizer returned by GetPageAreaSizer.

Call RunWizard to set the wizard running, passing the first page to be shown. RunWizard returns TRue if the user successfully finished the wizard, or false if the user cancelled it.

To specify the border around the page area, call SetBorder. The default is zero.

wxWizard Example

Let's examine the wxWizard sample from the wxWidgets distribution. It consists of four pages, illustrated in Figure 12-3 (the numbers are for clarity and are not on the actual dialogs).

Figure 12-3. Wizard sample


The first page is so simple that it doesn't have its own derived classthe driving function MyFrame::OnRunWizard simply creates an instance of wxWizardPageSimple and adds a static text control to it, like this:

 #include "wx/wizard.h" wxWizard *wizard = new wxWizard(this, wxID_ANY,                   wxT("Absolutely Useless Wizard"),                   wxBitmap(wiztest_xpm),                   wxDefaultPosition,                   wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER); // PAGE 1 wxWizardPageSimple *page1 = new wxWizardPageSimple(wizard); wxStaticText *text = new wxStaticText(page1, wxID_ANY,     wxT("This wizard doesn't help you\nto do anything at all.\n")     wxT("\n")     wxT("The next pages will present you\nwith more useless controls."),          wxPoint(5,5)); 

The second page, wxCheckboxPage, is derived from wxWizardPage and implements GetPrev and GetNext. GetPrev always returns the first page, but GetNext can return either the next page or the last page, depending on whether the user checked Skip the Next Page. Here's the declaration and implementation of wxCheckBoxPage:

 // this shows how to dynamically (i.e. during run-time) arrange // the page order // PAGE 2 class wxCheckboxPage : public wxWizardPage { public:     wxCheckboxPage(wxWizard *parent,                    wxWizardPage *prev,                    wxWizardPage *next)         : wxWizardPage(parent)     {         m_prev = prev;         m_next = next;         wxBoxSizer *mainSizer = new wxBoxSizer(wxVERTICAL);         mainSizer->Add(             new wxStaticText(this, wxID_ANY, wxT("Try checking the box below and\n")                                    wxT("then going back and clearing it")),             0, // No vertical stretching             wxALL,             5 // Border width         );         m_checkbox = new wxCheckBox(this, wxID_ANY,                          wxT("&Skip the next page"));         mainSizer->Add(             m_checkbox,             0, // No vertical stretching             wxALL,             5 // Border width         );         SetSizer(mainSizer);         mainSizer->Fit(this);     }     // implement wxWizardPage functions     virtual wxWizardPage *GetPrev() const { return m_prev; }     virtual wxWizardPage *GetNext() const     {         return m_checkbox->GetValue() ? m_next->GetNext() : m_next;     } private:     wxWizardPage *m_prev,                  *m_next;     wxCheckBox *m_checkbox; }; 

The third page, wxRadioboxPage, intercepts cancel and page changing events. If you try to cancel at this point, you will be asked to confirm the cancel: if you click on No, wxWizardEvent::Veto will be called and the wizard will not be cancelled. OnWizardPageChanging vetoes any attempt to go forwards or backwards that hasn't first been specified using the radio buttons. In a realistic application, you might use the page changing event to ensure that the user has filled out all mandatory fields in this page before proceeding. Or you may want to prevent the user from going back one page for some reason. This is the code for wxRadioboxPage:

[View full width]

// This is a more complicated example of validity checking: // using events we may allow the user to return to the previous // page, but not to proceed. It also demonstrates how to // intercept a Cancel button press. // PAGE 3 class wxRadioboxPage : public wxWizardPageSimple { public: // directions in which we allow the user to proceed from this // page enum { Forward, Backward, Both, Neither }; wxRadioboxPage(wxWizard *parent) : wxWizardPageSimple(parent) { // should correspond to the enum above static wxString choices[] = { wxT("forward"), wxT("backward"), wxT("both"), wxT ("neither") }; m_radio = new wxRadioBox(this, wxID_ANY, wxT("Allow to proceed:"), wxDefaultPosition, wxDefaultSize, WXSIZEOF(choices), choices, 1, wxRA_SPECIFY_COLS); m_radio->SetSelection(Both); wxBoxSizer *mainSizer = new wxBoxSizer(wxVERTICAL); mainSizer->Add( m_radio, 0, // No stretching wxALL, 5 // Border ); SetSizer(mainSizer); mainSizer->Fit(this); } // wizard event handlers void OnWizardCancel(wxWizardEvent& event) { if ( wxMessageBox(wxT("Do you really want to cancel?"), wxT("Question"), wxICON_QUESTION | wxYES_NO, this) != wxYES ) { // not confirmed event.Veto(); } } void OnWizardPageChanging(wxWizardEvent& event) { int sel = m_radio->GetSelection(); if ( sel == Both ) return; if ( event.GetDirection() && sel == Forward ) return; if ( !event.GetDirection() && sel == Backward ) return; wxMessageBox(wxT("You can't go there"), wxT("Not allowed"), wxICON_WARNING | wxOK, this); event.Veto(); } private: wxRadioBox *m_radio; DECLARE_EVENT_TABLE() };

The fourth and last page, wxValidationPage, overrides transferDataFromWindow to do a validation check on the state of the check box. transferDataFromWindow is called whenever the Back or Next buttons are clicked, and if the validation or data transfer fails, the page is not changed. As with all dialogs, instead of overriding transferDataFromWindow, you can use validators for the page controls. This page also demonstrates the use of an image for a particular page, overriding the image passed to the wizard constructor. Here's the code for wxValidationPage:

 // This shows how to simply control the validity of the user input // by just overriding TransferDataFromWindow() - of course, in a // real program, the check wouldn't be so trivial and the data // will be saved somewhere too. // // It also shows how to use a different bitmap for one of the pages. // PAGE 4 class wxValidationPage : public wxWizardPageSimple { public:     wxValidationPage(wxWizard *parent) : wxWizardPageSimple(parent)     {         m_bitmap = wxBitmap(wiztest2_xpm);         m_checkbox = new wxCheckBox(this, wxID_ANY,                           wxT("&Check me"));         wxBoxSizer *mainSizer = new wxBoxSizer(wxVERTICAL);         mainSizer->Add(             new wxStaticText(this, wxID_ANY,                      wxT("You need to check the checkbox\n")                      wxT("below before going to the next page\n")),             0,             wxALL,             5         );         mainSizer->Add(             m_checkbox,             0, // No stretching             wxALL,             5 // Border         );         SetSizer(mainSizer);         mainSizer->Fit(this);     }     virtual bool TransferDataFromWindow()     {         if ( !m_checkbox->GetValue() )         {             wxMessageBox(wxT("Check the checkbox first!"),                          wxT("No way"),                          wxICON_WARNING | wxOK, this);             return false;         }         return true;     } private:     wxCheckBox *m_checkbox; }; 

The code that puts all the pages together and starts the wizard looks like this:

 void MyFrame::OnRunWizard(wxCommandEvent& event) {     wxWizard *wizard = new wxWizard(this, wxID_ANY,                     wxT("Absolutely Useless Wizard"),                     wxBitmap(wiztest_xpm),                     wxDefaultPosition,                     wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);     // a wizard page may be either an object of a predefined class     wxWizardPageSimple *page1 = new wxWizardPageSimple(wizard);     wxStaticText *text = new wxStaticText(page1, wxID_ANY,          wxT("This wizard doesn't help you\nto do anything at all.\n")          wxT("\n")          wxT("The next pages will present you\nwith more useless controls."),          wxPoint(5,5)         );     // ... or a derived class     wxRadioboxPage *page3 = new wxRadioboxPage(wizard);     wxValidationPage *page4 = new wxValidationPage(wizard);     // set the page order using a convenience function  could     // also use SetNext/Prev directly as below     wxWizardPageSimple::Chain(page3, page4);     // this page is not a wxWizardPageSimple, so we use SetNext/Prev     // to insert it into the chain of pages     wxCheckboxPage *page2 = new wxCheckboxPage(wizard, page1, page3);     page1->SetNext(page2);     page3->SetPrev(page2);     // allow the wizard to size itself around the pages     wizard->GetPageAreaSizer()->Add(page1);     if ( wizard->RunWizard(page1) )     {         wxMessageBox(wxT("The wizard successfully completed"),          wxT("That's all"), wxICON_INFORMATION | wxOK);     }     wizard->Destroy(); } 

When the wizard is finished or canceled, MyFrame intercepts the events and, in this example, reports them on the frame's status bar. You could equally intercept these events in a class derived from wxWizard.

The full listing of the sample can be found in Appendix J, "Code Listings," and the code can be found in examples/chap12 on the CD-ROM.

    team bbl



    Cross-Platform GUI Programming with wxWidgets
    Cross-Platform GUI Programming with wxWidgets
    ISBN: 0131473816
    EAN: 2147483647
    Year: 2005
    Pages: 262

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net