This is my first attempt at writing a robust Perl library and I would like some honest feedback on how I could make this better. It's mainly a class that creates a GUI wizard control in Tk.

######################################################################+#################
#
# Class: Tk::Wizard
#
# Description: The Tk::Wizard class is a Tk component that allows u+sers to create a
# Microsoft style application wizard without having to w+rite the code to
# manage the wizard framework components, like event dis+patching, etc.
#
######################################################################+#################
package Tk::Wizard;
use SelfLoader;
use Tk;
use Tk::JPEG;
use Tk::Text;
use strict;
no strict 'refs';
######################################################################+################################
#
# Method: new
#
# Description: Creates a new object of type Tk::Wizard and sets the+ default state
#
# Parameters: None
#
# Exceptions: N/A in this release
#
######################################################################+################################
sub new($) {
my $invocant = shift;
my $class = ref( $invocant) || $invocant;
my $self = { # required for GUI operation
hWin => $_[0],
# configuration parameters
title => "Generic Wizard",
filename => "image.jpg",
# event handling references
preNextButtonAction => undef,
postNextButtonAction => undef,
prePrevButtonAction => undef,
postPrevButtonAction => undef,
preHelpButtonAction => undef,
helpButtonAction => undef,
postHelpButtonAction => undef,
preFinishButtonAction => undef,
finishButtonAction => undef,
postFinishButtonAction => undef,
preCancelButtonAction => undef,
preCloseWindowAction => undef,
# wizard page control list and ptr
wizardPageList => [], # ref to empty +array
wizardPagePtr => 0,
# internally used to track the wizard page being shown
wizardFrame => ""
};
return bless( $self, $class);
} # end of sub new
######################################################################+################################
#
# Method: setActionEventHandlers
#
# Description: Allows the documented set of actions to be overwritt+en to hook the Tk::Wizard
# framework into the application. While no handler must +be overwritten, it is
# very useful to overwrite the helpButtionAction and the+ finishButtonAction
# event handlers.
#
# Parameters: None
#
# Exceptions: N/A in this release
#
######################################################################+################################
sub setActionEventHandlers {
my $self = shift;
my %newHandlers = ( @_ );
foreach( keys %newHandlers) {
$self->{"$_"} = $newHandlers{$_};
} # end of foreach
} # end of setActionEventHandlers
######################################################################+################################
#
# Method: setParamaters
#
# Description: Provide useful definitions for the two main paramete+rs - title and filename. title
# refers to the scalar string that will be printed on th+e wizard's title bar. filename
# is the location and name of a JPEG file that should be+ displayed on the left side of
# the wizard.
#
# Parameters: None
#
# Exceptions: N/A in this release
#
######################################################################+################################
sub setParameters {
my $self = shift;
my %newParams = ( @_ );
foreach( keys %newParams) {
$self->{"$_"} = $newParams{"$_"};
} # end of foreach
} # end of setParameters
######################################################################+################################
#
# Method: addWizardPage
#
# Description: Puts the passed in wizard page at the end of the wizard+ page list.
#
# Parameters: $page = The wizard page that should be added to the +end of the wizard list.
#
# Exceptions: N/A in this release
#
######################################################################+################################
sub addWizardPage {
my ($self, $page) = @_;
push @{$self->{wizardPageList}}, $page;
} # end of sub addWizardPage
######################################################################+################################
#
# Method: currentPage
#
# Description: Returns the page number that is currently being disp+layed. Page counting starts with 1.
#
# Parameters: None
#
# Exceptions: N/A in this release
#
######################################################################+################################
sub currentPage {
my($self) = @_;
return ($self->{wizardPagePtr} + 1);
} # end of sub currentPage
1;
######################################################################+##############
# Functions below this level are only loaded on demand. This gives the+ appearance of
# faster load times. See module SelfLoader for details.
######################################################################+##############
__DATA__
sub parent {
my ($self) = @_;
return $self->{hWin};
}
######################################################################+################################
#
# Method: wpFrame
#
# Description: This returns a Tk::Frame object that is a child of t+he Wizard control. It should be
# used when creating wizard pages since some padding parameter+s are applied to it by
# the wizard control.
#
# Parameters: N/A
#
# Exceptions: N/A in this release
#
######################################################################+################################
sub wpFrame {
my ($self) = @_;
my $frame = $self->{hWin}->Frame( -width => 400, -height => 400);
$frame->packPropagate( 0);
return $frame;
} # end of wpFrame
######################################################################+################################
#
# Method: Show
#
# Description: The Show method actually creates all of the necessary c+omponents on the window that is
# set in the new method. It is important that Wizard appl+ications that use MainWindow for
# drawing the Wizard also dispatch MainLoop after the Sho+w method.
#
# Parameters: None
#
# Exceptions: N/A in this release
#
######################################################################+################################
sub Show {
my ($self) = @_;
#
# builds the buttons on the bottom of thw wizard
#
my $buttonPanel = $self->{hWin}->Frame();
$buttonPanel->Button( -text => "Cancel",
-command => [ \&CancelButtonEventCycle, +$self, $self->{hWin}],
-width => 10
) ->pack( -side => "right", -expand => 0);
$self->{nextButtonRef} = $buttonPanel->Button( -text => "Next >",
-command => [ \&NextButtonEventCycle, $self+ ],
-width => 10
)->pack( -side => "right"+, -expand => 0);
$self->{prevButtonRef} = $buttonPanel->Button( -text => "< Previou+s",
-command => [ \&PrevBut+tonEventCycle, $self ],
-width => 10,
-state => "disabled"
)->pack( -side => "right", -expand => 0);
$buttonPanel->Button( -text => "Help",
-command => [ \&HelpButtonEventCycle, $s+elf ],
-width => 10
)->pack( -side => 'left', -anchor => 'w');
$buttonPanel->pack( -side => "bottom", -fill => 'x', -pady => 4, -+padx => 4);
#
# builds the image on the left side of the wizard
#
$self->{hWin}->Photo( "sidebanner", -format => "jpeg", -file => $s+elf->{filename});
$self->{hWin}->Label( -image => "sidebanner")->pack( -side => "lef+t", -anchor => "w");
#
# This populates the wizard page panel on the side of the screen.
#
$self->{wizardFrame} =
$self->{wizardPageList}->[($self->{wizardPagePtr})]->()->p+ack( -side => "top", -expand => 0);
#
# setup the containing window to match the criteria for a wizard w+idget
#
$self->{hWin}->configure( -title => $self->{title});
$self->{hWin}->resizable( 0, 0); # forbid resize
$self->{hWin}->withdraw; # position in screen cente+r
$self->{hWin}->Popup;
$self->{hWin}->transient; # forbid minimize
$self->{hWin}->protocol( WM_DELETE_WINDOW => [ \&CloseWindowEv+entCycle, $self, $self->{hWin}]);
} # end of sub Show
######################################################################+################################
#
# Method: dispatch
#
# Description: Thin wrapper to dispatch event cycles as needed
#
# Parameters: The dispatch function is an internal function used to+ determine if the dispatch back reference
# is undefined or if it should be dispatched. Undefined method+s are used to denote dispatchback
# methods to bypass. This reduces the number of method dispatc+hs made for each handler and also
# increased the usability of the set methods above when trying+ to unregister event handlers.
#
# Exceptions: N/A in this release
#
######################################################################+################################
sub dispatch {
my ($handler) = @_;
if( defined $handler) { return !($handler->());}
return 0;
} # end of sub dispatch
######################################################################+################################
#
# Method: NextButtonEventCycle
#
# Description: Runs the complete view of the action handler cycle for +the "Next>" button on the
# wizard button bar. This includes dispatching the preNex+tButtonAction and
# postNextButtonAction handler at the apporprate times.
#
# Parameters: None
#
# Exceptions: N/A in this release
#
######################################################################+################################
sub NextButtonEventCycle {
my ($self) = @_;
if( dispatch( $self->{preNextButtonAction})) { return;}
# advance the wizard page pointer and then adjust the navigation bu+ttons.
# readraw the frame when finished to get changes to take effect.
$self->{wizardPagePtr}++;
$self->{wizardPagePtr} = $#{$self->{wizardPageList}} if( $self->{wi+zardPagePtr} >= $#{ $self->{wizardPageList}});
if( $self->{nextButtonRef}->cget( -text) eq "Finish") {
if( dispatch( $self->{finishButtonAction})) { return; }
$self->CloseWindowEventCycle();
}
$self->{prevButtonRef}->configure( -state => "normal");
$self->{nextButtonRef}->configure( -text => "Finish") if( $self->{w+izardPagePtr} == $#{ $self->{wizardPageList}});
$self->redrawWizardPage;
if( dispatch( $self->{postNextButtonAction})) { return; }
} # end of sub NextButtonEventCycle
######################################################################+################################
#
# Method: PrevButtonEventCycle
#
# Description: Runs the complete view of the action handler cycle for +the "<Previous" button on the
# wizard button bar. This includes dispatching the prePre+vButtonAction and
# postPrevButtonAction handler at the apporprate times.
#
# Parameters: None
#
# Exceptions: N/A in this release
#
######################################################################+################################
sub PrevButtonEventCycle {
my ($self) = @_;
if( dispatch( $self->{prePrevButtonAction})) { return; }
# move the wizard pointer back one position and then adjust the nav+igation buttons
# to reflect any state changes. Don't fall off end of page pointer
$self->{wizardPagePtr}--;
$self->{wizardPagePtr} = 0 if( $self->{wizardPagePtr} < 0);
$self->{nextButtonRef}->configure( -text => "Next >");
$self->{prevButtonRef}->configure( -state => "disabled") if( $self-+>{wizardPagePtr} == 0);
$self->redrawWizardPage;
if( dispatch( $self->{postPrevButtonAction})) { return; }
} # end of sub PrevButtonEventCycle
######################################################################+################################
#
# Method: HelpButtonEventCycle
#
# Description: This generates all of the events required when the Help+ button is clicked. This runs
# through the pre event handler, the event handler and th+en the post event handler. If
# no event handlers are defined, the method does nothing.
#
# Parameters: None
#
# Exceptions: N/A in this release
#
######################################################################+################################
sub HelpButtonEventCycle {
my ($self) = @_;
if( dispatch( $self->{preHelpButtonAction})) { return; }
if( dispatch( $self->{helpButtonAction})) { return; }
if( dispatch( $self->{postHelpButtonAction})) { return; }
} # end of sub HelpButtonEventCycle
######################################################################+################################
#
# Method: CancelButtonEventCycle
#
# Description: This generates all of the necessary events reqruied for+ a good Wizard control when
# the cancel button is clicked. This involves dispatching+ the preCancelButtonAction handler
# and then activating the CloseWindowEventCycle to run th+rough the process of closing
# the window.
#
# Parameters: None
#
# Exceptions: N/A in this release
#
######################################################################+################################
sub CancelButtonEventCycle {
my ($self, $hGUI) = @_;
if( dispatch( $self->{preCancelButtonAction})) { return;}
$self->CloseWindowEventCycle( $hGUI);
} # end of sub CancelButtonEventCycle
######################################################################+################################
#
# Method: CloseWindowEventCycle
#
# Description: This generates all of the necessary events required for+ a good Wizard control when
# the Window is about to be closed. This involves dispatc+hing the preCloseWindowAction handler
# and then destroying the reference to the Window control+.
#
# Parameters: None
#
# Exceptions: N/A in this release
#
######################################################################+################################
sub CloseWindowEventCycle {
my ($self, $hGUI) = @_;
if( dispatch( $self->{preCloseWindowAction})) { return;}
$hGUI->destroy;
} # end of sub CloseWindowEventCycle
######################################################################+################################
#
# Method: redrawWizardPage
#
# Description: Update the wizard page panel by unpacking the existing +controls and then repacking.
# This allows updates to the page pointer to become visib+le.
#
# Parameters: None
#
# Exceptions: N/A in this release
#
######################################################################+################################
sub redrawWizardPage {
my ( $self) = @_;
$self->{wizardFrame}->packForget;
$self->{wizardFrame} = $self->{wizardPageList}->[$self->{wizardPage+Ptr}]->()->pack( -side => "top");
} # end of sub redrawWizardPagePanel
#############################
# End of Package Tk::Wizard #
#############################
1;
__END__
=head1 NAME
Tk::Wizard - GUI Wizard Framework
=head1 SYNOPSIS
use Tk::Wizard;
my $wizard = new Wizard( new MainWindow);
$wizard->addWizardPage( \&createPage1);
$wizard->addWizardPage( \&createPage2);
$wizard->addWizardPage( \&createPage3);
$wizard->Show();
MainLoop;
=head1 DESCRIPTION
The Tk::Wizard class automates a large part of creating a program wiza+rd to collect information and then
perform some complex task based upon the answers and information gathe+red from the user. The wizard feel
is largly based upon the Microsoft wizard style that appeared in produ+cts like Office 95 and Office 97.
=head1 METHODS
=head2 Tk::Wizard-E<gt>new( @args)
Creates a new instance of the Tk::Wizard object. The @args list is a h+ash list of the different
options that can be specified for the class. These include:
Parameters:
-title => This is the title that will be displayed in the Windows t+itle bar
-filename => This is the path and name of a JPEG file that will be +displayed on the right side
of the screen.
Action Event Handlers:
-preNextButtonAction => This is a reference to a function that will+ be dispatched before the Next
button is processed.
-postNextButtonAction => This is a reference to a function that wil+l be dispatched after the Next
button is processed.
-prePrevButtonAction => This is a reference to a function that will+ be dispatched before the Previous
button is processed.
-postPrevButtonAction => This is a reference to a function that wil+l be dispatched after the Previous
button is processed.
-preHelpButtonAction => This is a reference to a function that will+ be dispatched before the Help
button is processed.
-helpButtonAction => This is a reference to a function that will be+ dispatched to handle the Help
button action.
-postHelpButtonAction => This is a reference to a function that wil+l be dispatched after the Help
button is processed.
-preFinishButtonAction => This is a reference to a function that wi+ll be dispatched before the Finish
button is processed.
-finishButtonAction => This is a reference to a funciton that will +be dispatched to handle the Finish
button action.
-postFinishButtonAction => This is a reference to a function that w+ill be dispatched after the Finish
button is processed.
-preCancelButtonAction => This is a reference to a function that wi+ll be dispatched before the Cancel
button is processed.
-preCloseWindowAction => This is a reference to a funciton that wil+l be dispatched before the window
is issued a close command.
See the section Action Event Handlers for more details.
=head2 Tk::Wizard-E<gt>setActionEventHandlers( @args)
This method can be used to set or change the action event handler func+tions of a Tk::Wizard instance.
The @args list accepts any Action Event Handler value pairs that could+ be passed into the new method.
See the section Action Event Handlers for more details.
=head2 Tk::Wizard-E<gt>setParameters( @args)
This method can be used to set or change the parameters of a Tk::Wizar+d instance. The @args list
accepts any Parameter value pairs that could be passed into the new me+thod.
=head2 Tk::Wizard-E<gt>Show()
This method must be dispatched before the Wizard will be displayed.
=head2 Tk::Wizard-E<gt>addWizardPage( $page)
This method is used to add a Wizard page to the wizard. The $page para+meter must be a Tk::Frame object.
The pages are stored and will be displayed in the order that they were+ added to the Wizard control.
=head2 Tk::Wizard-E<gt>currentPage()
This returns the index of the page that is currently shown. Pages are +indexed starting at 0 with the
first page that is associated with the wizard through the addWizardPag+e method.
=head2 Tk::Wizard-E<gt>wpFrame()
This returns a wizard page frame. This method should be called when th+e user of the wizard wants to
build the page specific controls.
=head2 Tk::Wizard-E<gt>parent
This returns the parent Tk widget that was used to create the wizard a+nd all of the controls. This is
defined as part of the new method.
=head1 Action Event Handlers
The action event handler functions should not accept and parameters an+d return a TRUE or FALSE variable. If
the remainder of the action should continue a TRUE value is returned. +If a FALSE value is returned, execution
of the event handler stops. This can be used to prompt the user if the+y want to quit and take action based upon
the input.
=head1 SUPPORT
A list of the current development efforts can be found at
http://www.uwm.edu/~dthable/perl
=head1 COPYRIGHT
Copyright (c) 2002 Daniel T. Hable
Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated
documentation files (the "Software"), to deal in the Software without +restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicens+e, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so, subj+ect to the following conditions:
The above copyright notice and this permission notice shall be include+d in all copies or substantial portions of
the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRES+S OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE+ AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR O+THER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WIT+H THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
=cut