QWizard(3)
๐ QWizard ๐
okay so real talk: this is the manual for QWizard(3), straight out of the User Contributed Perl Documentation section, and it's genuinely iconic. bestie, you're gonna wanna sit down for this one. ๐ซถ
๐ธ NAME ๐ธ
QWizard โ it shows a series of questions, grabs the answers, and then acts on those answers. that's the whole thing. slay.
โจ SYNOPSIS โจ
okay so here's the entire vibe in one go. this code works as an application or as a CGI script both, which is the most main-character energy a snippet can possibly have:
#
# The following code works as a application *or* as a CGI script both:
#
use QWizard;
my %primaries =
(
starting_node =>
{ title => "starting here",
introduction => "foo bar",
questions =>
[{ type => 'text',
name => 'mytext',
text => 'enter something:',
default => "hello world" },
{ type => 'checkbox',
text => 'yes or no:',
values => ['yes','no'],
name => 'mycheck'} ],
actions =>
[sub { return [
"msg: text = " . qwparam('mytext'),
"msg: checkbox = " . qwparam('mycheck')
];}]
}
);
my $qw = new QWizard(primaries => \%primaries,
title => "window title");
$qw->magic('starting_node');
#
# PLEASE see the examples in the examples directory.
#
and like, genuinely, PLEASE go look at the examples in the examples directory. it's not a suggestion, it's a lifestyle. ๐
๐ฆ DESCRIPTION ๐ฆ
QWizard displays a list of grouped questions, then retrieves and processes the answers your user gives. you can show multiple question/answer sets before any of those answers actually get dealt with โ it just keeps stacking. and then the moment a "commit" action happens (instigated by the user, not by you, it's their moment โจ), a whole series of actions fires off to handle the answers. those actions run in the order the QWizard programmer said they should. order matters, we don't freelance here.
here's where the real power lives, and it's lowkey magical: QWizard inherently keeps track of all the state information between one wizard screen and the next, even in normally stateless transaction environments like HTTP and HTML. that's the whole trick. so a QWizard programmer can collect a huge body of data using a bunch of simple displays. once everything's been gathered and verified, then it can be handled however makes sense โ written to a database, used for system configuration, used to generate a graph, whatever. go off. ๐
the user interfaces that currently exist are HTML, Gtk2, Tk, and (minimally) ReadLine. and here's the slay: a single QWizard script implementation can make use of any of those output formats without touching the code. not one line changed. so it's extremely easy to write portable wizard scripts that work without modification in graphical window environments (Gtk2 and Tk), in HTML-based web environments (e.g., CGI scripts.), and in interactive command line environments (ReadLine). one script, many vibes. ๐
back-end interfaces โ they're child classes of the QWizard::Generator module โ are the ones responsible for displaying the information to the user. right now HTML, Gtk2, Tk and ReadLine are the output mechanisms that work the best, in that order. some others are planned (namely a curses version), but those are not far along in development, so don't hold your breath. ๐ developing new generator back-ends is fairly simple and doesn't take a lot of code โ assuming the graphic interface is fairly powerful and contains a widget library. easy peasy.
QWizard operates by displaying a series of "screens" to the user. each screen is defined inside a QWizard construct called a primary, which describes the attributes of a given screen, including the list of questions to be presented to the user. primaries can contain questions, things to do immediately after the questions are answered (post_answers), and things to do once the entire series of screens have been answered (actions). you can also attach other info to a primary, like a title and an introduction. cute, right?
an example of a very minimal primary definition containing one question:
my %primaries = (
myprimary =>
{
title => "my screen title",
introduction => "optional introduction to the screen",
questions =>
[
{
type => 'checkbox',
text => 'Should the chicken cross the road?',
}
],
}
after defining a set of primaries, you have to create a new QWizard object. the new() constructor gets handed a set of options โ things like the window title and a reference to a hash table containing the primaries. (the complete set of options lives in the "QWizard new() Options" section, don't worry, we're getting there.) the question display and data collection starts when you call the magic() routine on your new QWizard object:
my $qw = new QWizard(primaries => \%primaries,
title => 'my title');
$qw->magic('myprimary');
there are examples distributed with the QWizard module sources that may help you understand the whole system and what it's capable of โ see the examples directory of the QWizard source code tree for details. also, QWizard was written mostly because of the requirements of the Net-Policy project. Net-Policy makes very extensive use of QWizard and is another good place to look for examples. in fact, the QWizard CVS code is located inside the Net-Policy CVS tree. see http://net-policy.sourceforge.net/ for details on the Net-Policy project. and there are a number of screen shots showing all the interfaces as well on the main net-policy web site. ๐ธ
๐ช MAGIC() PSEUDO-CODE ๐ช
here's a pseudo-code walk-through of the essential results of the magic() routine up above. in a CGI script, for example, magic() will be called multiple times (once per screen) โ but the results will be the same in the end. it's all taken care of magically ;-). that's just how it is. ๐ง
################
## WARNING: pseudo-code describing a process! Not real code!
################
# Loop through each primary and display the primary's questions.
while(primaries to process) {
display_primary_questions();
get_user_input();
check_results();
run_primary_post_answers();
}
# Displays a "will be doing these things" screen,
# and has a commit button.
display_commit_screen();
# Loop through each primary and run its actions.
# Note: see action documentation about execution order!
foreach (primary that was displayed) {
results = run_primary_actions();
display(results);
}
# If magic() is called again, it restarts from
# the top primary again.
๐ QWIZARD NEW() OPTIONS ๐
the options you pass to the QWizard new() operator define how the QWizard instance will behave. you pass them like this:
new QWizard (option => value, ...)
and these are the valid options, bestie:
title => "document title"โ the document title to be printed in the title bar of the window. ๐ชgenerator => GENERATORโGENERATORis a reference to a valid QWizard generator. current generator classes are:QWizard::Generator::Best(default: picks the best available)QWizard::Generator::HTMLQWizard::Generator::Gtk2QWizard::Generator::TkQWizard::Generator::ReadLine(limited in functionality)
the QWizard::Generator::Best generator is used if no specific generator is specified. the Best generator will create an HTML generator if used in a web context (i.e., a CGI script), or else pick the best of the available other generators (Gtk2, then Tk, then ReadLine).
this example forces a Gtk2 generator to be used:
my $wiz = new QWizard(generator => new QWizard::QWizard::Gtk2(),
# ...
);
generator_args => [ARGS],โ if you want the default generator that QWizard will provide you, but would still like to provide that generator with some arguments, you can use this token to pass an array reference of arguments. these arguments will be passed to thenew()method of the Generator that is created.top_location => "webaddress"โ this should be the top location of a web page where the questions will be displayed. this is needed for "go to top" buttons and the like to work. this is not needed if the QWizard-based script is not going to be used in a CGI or other web-based environment (eg, if it's going to be used in mod_perl).primaries => \%my_primariesโmy_primarieswill define the list of questions to be given to the user.my_primariesjust defines the questions, but does not mean the user will be prompted with each question. the questions in this series that will be displayed for the user to answer is determined by themagic()function's starting arguments, described below. the format of the primaries hash is described in the Primaries Definition section below, and the recognized values in the primaries hash are described in the Primaries Options section.no_confirm => 1โ if set, the final confirmation screen will not be displayed, but instead the resulting actions will be automatically run. this can also be achieved inside the wizard tokens primaries by setting a question name tono_confirmwith a value of1(using a hidden question type.) ๐begin_section_hook => \&subroutineโ this function will be called just before each screen is displayed. it can be used to perform such functions as printing preliminary information and initializing data.end_section_hook => \&subroutineโ this function will be called just after a set of questions is displayed.topbarโ a place to add extra widgets to a primary at the very top. see the bar documentation in the QUESTION DEFINITIONS section below for details on this field.leftside => [WIDGETS]rightside => [WIDGETS]โ adds a left or right side frame to the main screen where theWIDGETSare always shown for all primaries. basically, these should be "axillary" widgets that augment the widgets is the main set of questions. they can be used for just about anything, but the look and feel will likely be better if they're suplimental.
the WIDGETS are normal question widgets, just as can appear in the questions section of the primaries definition as described below.
in addition, however, there can be subgroupings with a title as well. these are then in a sub-array and are displayed with a title above them. EG:
leftside => [
{ type => 'button',
# ... normal button widget definition; see below
},
[
"Special Grouped-together Buttons",
{ type => 'button',
# ...
},
{ type => 'button',
# ...
},
],
],
the above grouped set of buttons will appear slightly differently and grouped together under the title "Special Grouped-together Buttons". ๐
The widget-test-screen.pl in the examples directory shows examples of using this. go look at it bestie it's literally a live demo ๐
Important note: not all backends support this yet ๐ญ HTML and Gtk2 do, though. so if you're on one of those, slay โจ
๐ธ PRIMARIES DEFINITION ๐ธ
okay so the primaries argument of the new() function is where you define the whole list of questions that might get posed to a user. each primary in the hash holds a list of questions, answers, etc., and they all get grouped together under a name (that name is the key in the hash) ๐ซถ
so a typical primary set definition looks something like this:
%my_primaries =
(
# The name of the primary.
'question_set_1' =>
# its definition
{
title => 'My question set',
questions =>
# questions are defined in an array of hashes.
[{type => 'checkbox',
text => 'Is this fun?',
name => is_fun,
default => 1,
values => [1, 0] },
{type => 'text',
text => 'Enter your name:',
name => 'their_name'}],
post_answers =>
# post_answers is a list of things to do immediately after
# this set of questions has been asked.
[ sub { print "my question set answered" } ],
actions =>
# actions is a list of actions run when all is said and done.
[ sub {
return "msg: %s thinks this %s fun.\n",
qwparam('their_name'),
(qwparam('is_fun')) ? "is" : "isn't"
}],
actions_descr =>
# An array of strings displayed to the user before they agree
# to commit to their answers.
[ 'I\'m going to process stuff from @their_name@)' ]
});
lowkey check out the QWizard::API module if you want an alternative, less verbose form of API for creating primaries โ it can produce more compact-looking code โจ๐
๐ VALUE conventions ๐
in the docs that follow, any time the keyword VALUE shows up, you can swap in any of these types of "values":
- "a string"
- 10
- \&sub_to_call
- sub { return "a calculated string or value" }
- [\&sub_to_call, arguments, to, sub, ...]
subroutines get called and they're expected to return a single value or an array reference of multiple values, no cap ๐ง
a lot of the time the VALUE keyword shows up inside array brackets: []. so bestie you can often specify multiple values in all kinds of ways. e.g. a values clause in a question may be given like this:
sub my_examp1 { return 3; }
sub my_examp2 { return [$_[0]..$_[1]]; }
values => [1, 2, \&my_examp1, [\&my_examp2, 4, 10]],
after everything gets evaluated, the end result of this (very extra) example will be an array passed of digits from 1 to 10 passed to the values clause. iconic behaviour honestly ๐
and at any function, at any point in time during processing, the qwparam() function can be called to return the results of a particular question as it was answered by the user ๐ so like, if a question named their_name was answered with "John Doe" at any point in the past series of wizard screens, then qwparam('their_name') would return "John Doe". since most VALUE functions are designed to process previous user input, understanding this is the key to using the QWizard Perl module fr fr. more info and examples follow in the sections below ๐
๐ PRIMARY OPTIONS ๐
these are the tokens you can specify in a primary:
title => VALUEโ the title name for the set of questions. it gets displayed at the top of the screen ๐introduction => VALUEโ introductory text printed above the list of questions for a given primary. super useful as a starting piece of text to help the user with this particular wizard screen. whether it shows is controlled by the Net-Policypref_introuser preference. the default is to display introductory text, but the user can turn this setting off and on ๐ฆquestions => [{ QUESTION_DEFINITION }, { QUESTION_DEFINITION }, ...]โ this is the list of questions to pose to the user for this screen. the Question Definitions section describes valid question formatting โจpost_answers => [ VALUES ]โ a list of actions to run after the questions on the screen have been answered. even though this is a VALUES clause like described above, these should normally be subroutines and not hard-coded values ngl. the first argument to the VALUE functions will be a reference to the wizard. this is particularly useful for conditionally adding future screens/primaries that need to be shown to the user ๐ you do that with theadd_todos()function call in the action section:
if (some_condition()) {
$_[0]->add_todos('primary1', ...);
}
peep the QWizard Object Functions section for more on the add_todos() function, but the above adds the 'primary1' screen to the list of screens to display for the user before the wizard is finished ๐ซ
a post_answers subroutine should return the word "OK" for it to be successful (right now this isn't checked, but it may be (again) in the future ๐). it may also return "REDISPLAY", which causes the screen to be displayed again.
for HTML output, these get run just before the next screen is printed after the user has submitted the answers back to the web server. for window-based output (Gtk2, Tk, etc.) the results are similar and these subroutines are evaluated before the next window is drawn ๐ธ
check_value => sub { ... }check_value => [sub { ... }, arg1, arg2, ...]โ the primary may have acheck_valueclause assigned to do high level consistency checks. returning anything but"OK"means the returned text gets displayed as an error to the user, and they have to fix it before they're allowed to continue past the screen ๐actions => [ VALUES ]โ the action functions run after the entire wizard series of questions has been displayed and answered and after the user has hit the"commit"button. it's assumed that the actions of the earlier screens depend on the actions of the later screens, so the action functions get executed in reverse order from the way the screens were displayed. see theadd_todos()function description in the QWizard Object Functions section for how to change the order of execution away from the default ๐ซถ
the collected values returned from the VALUES evaluation get displayed to the user. any message starting with a 'msg:' prefix is displayed as a normal output line. any value not prefixed with 'msg:' gets displayed as an error (typically bold and red by most generators, oop) ๐ญ
actions_descr => [ VALUES ]โ just before the actions are run, a change-summary screen is shown to the user, and a"commit"button is given on this screen too. VALUE strings, function results, etc., get displayed as a list on this commit screen. strings may have embedded special@TAG@keywords which get replaced by the value for the question with a name ofTAG. these strings should tell the user what the commit button will do for any actions to be run by this set of questions ๐ if any question was defined whose name wasno_confirmand whose value was1, this screen gets skipped and the actions run directly โญsub_modules => [ 'subname1', ... ]โ this hash value adds the specified sub-modules to the list of screens to display after this one. it's equivalent to having apost_answersclause that includes the function:
sub { $_[0]->add_todos('subname1', ...); }
doif => sub { LOGIC }โ lets primaries be optional and only displayed under certain conditions ๐ if specified, it should be a CODE reference which, when executed, should return a1if the primary is to be displayed or a0if not. the primary gets entirely skipped if the CODE reference returns a0๐topbarโ a place to add extra widgets to a primary at the very top. see the bar documentation in the QUESTION DEFINITIONS section below for details on this field ๐leftside => [WIDGETS]rightside => [WIDGETS]โ adds a left or right side frame to the main screen where the WIDGETS are shown for this primary. Important note: see theleftside/rightsidedocumentation for QWizard for more details and the important support notes over there ๐take_over => \&subroutineโ this hash value lets a subroutine completely take control of processing beyond this point. the wizard methodology functionally stops here and control for anything in the future is entirely passed to this subroutine. this should be rarely (if ever) used, and it's really a way of breaking out of the wizard completely ๐ช๐จ
๐ฆ QUESTION DEFINITIONS ๐ฆ
questions are implemented as a collection of hash references. a question generally has the following format:
{
type => QUESTION_TYPE
text => QUESTION_TEXT,
name => NAME_FOR_ANSWER,
default => VALUE,
# for menus, checkboxes, multichecks, ... :
values => [ VALUE1, VALUE2, ... ], # i.e., [VALUES]
# for menus, checkboxes, multichecks, ... :
labels => { value1 => label1, value2 => label2 } # i.e., [VALUES]
}
other than this sort of hash reference, the only other type of question allowed in the question array is a single "" empty string. the empty string acts as a vertical spatial separator, meaning a space should occur between the previous question and the next question ๐ธ
the fields available to question types are given below. unless otherwise stated, the fields are available to all question types โจ
name => 'NAME'โ names the answer to the question. this name can then be used later in other sections (action, post_answers, etc.) to retrieve the value of the answer using theqwparam()function. for example,qwparam('NAME')at any point in future executed code should return the value provided by the user for the question named'NAME'๐
the namespace for these names is shared among all primaries (except 'remapped' primaries, which are described later). a warning gets issued if different questions from two different primaries use the same name. this warning won't be given if the question contains an override flag set to 1 ๐ซถ
text => 'QUESTION TEXT'โ the words your user actually reads for that question. Picture the screen, bestie: this text usually chills on the left, and the widget they're meant to poke at sits over on its right. (Okay so technically this is all up to how the back-end question Generator implements things โ but the standard QWizard generators? They use this exact layout scheme. ๐ )type => 'TYPE'โ the moment of truth: this decides what kind of question you're building.TYPEcan be one of these absolute icons:labelโ just displays info on the screen and asks for literally nothing back. The question's text prints on the left, then the values portion follows on the right. Omit the values portion and the text portion stretches across the entire width of the screen. Slay.paragraphโ labels' cousin, but made for those spots where the text needs to wrap and is probably gonna be quite long. Like, essay-long.textโ text input, periodt. Shows an entry box where you can type a standard single line of text.textboxโ also text input, but it's giving big box energy โ big enough for multi-line entries.hidetextโ obscured text input โจ. It shows a text entry box but echoes whatever's typed as asterisks. Perfect for prompting users to enter passwords, because the actual text is not shown on the screen. Iconic and secure.checkboxโ a checkbox, obviously. The values clause should have only 2 values in it: one for its "on" value and one for its "off" value (and those default to1and0respectively).
The button_label clause can specify text to put right next to the checkbox itself.
If a backend supports key accelerators (GTk2): checkbox labels can be bound to Alt-key accelerators. See QUESTION KEY-ACCELERATORS below for more info, bestie.
multi_checkboxโ multiple checkboxes, one for each label/value pair ๐. Thenamequestion field is a prefix, and all the values and/or label keywords become the second half of the name.
So like, these clauses:
{
type => 'multi_checkbox',
name => 'something',
values => ['end1','end2'],
...
}
will give you parameters of 'somethingend1' and 'somethingend2'. Cute, right?
If a backend supports key accelerators (GTk2): checkbox labels can be bound to Alt-key accelerators. See QUESTION KEY-ACCELERATORS below for more.
radioโ radio buttons, where only one can be selected at a time. And here's the fun part: if two questions have the same name and are both type'radio', they get "linked" together โ clicking a radio button on one question affects the other one too. They're besties fr fr.
If a backend supports key accelerators (GTk2): radio button labels can be bound to Alt-key accelerators. See QUESTION KEY-ACCELERATORS below.
menuโ a pull-down menu, where each label shows up as a menu item. If you use just the values clause (see below), the labels on screen will match the values. If thedefaultclause is set, that menu entry becomes the menu's initial selection. And if thelabelsclause is used, the values shown to the user get converted into screen representations that differ from theqwparam()values you'll have available later. Super useful for showing human-friendly versions of programmatic values. E.g.:
{
type => 'menu',
name => 'mymenu',
labels => [ 1 => 'my label1',
2 => 'my label2']
}
In this example, the user sees a menu with 2 entries, "my label1" and "my label2" โ but qwparam() will return 1 or 2 for qwparam('mymenu'). The vibes are fake, the data is real. ๐
tableโ a table to display. The values section should return a reference to an array, where each element of the array is a row containing the columns to display for that row. The top-most table must actually be returned in an array itself โ yeah, it's an oddity of internal QWizard processing, don't fight it. E.g.:
{
type => 'table',
text => 'The table:',
values => sub {
my $table = [['row1:col1', 'row1:col2'],
['row2:col1', 'row2:col2']];
return [$table];
}
}
Which would show up graphically on the screen like this:
row1:col1 row1:col2
row2:col1 row2:col2
Also! A column value inside the table may itself be a sub-table (another double-array reference set) or a hash reference, which becomes a sub-widget displaying any of the other types listed in this section. Tables within tables within tables. Inception core.
Finally, a headers clause can be added to the question definition, which adds column headers to the table. E.g.:
headers => [['col1 header','col2 header']]
fileuploadโ a dialog box so the user can upload a file into the application ๐ฆ. When a user submits a file, the question name can be used later to retrieve a read file handle on that file with the functionqw_upload_fh('NAME'). Now,qwparam('NAME')will return the original name of the file submitted โ but because of the sheer variability in how web-browsers submit file names along with the data, you should generally not use this field. Instead, get at the data through theqw_upload_fh()function. Second best alternative is theqw_upload_file('NAME')function, which returns a safe path to access under web-environments (something like/tmp/qwHTMLXXXXXX...). Safety first, bestie.filedownloadโ a button that lets the user download something the application generated โจ. The data that'll be stored in this file should be defined in the'data'field or the'datafn'field. The name displayed inside the button gets taken from the default question parameter.
Heads up: the data field is processed early during display of the screen, so if you're generating large sets of data that won't always be downloaded โ or that take a lot of memory โ you shouldn't use the data field. The data field is processed like any other value field, where raw data or a coderef can be passed in that'll be called to return the data.
The datafn field should contain a CODE reference that will be called with five arguments:
- a
IO::Filefilehandle to print to - the file name (if appropriate) it's going to print to.
- a QWizard object reference
- a reference to the primary hash
- a reference to the question hash
Example usage:
{ type => 'filedownload',
text => 'Download a file:',
datafn =>
sub {
my $fh = shift;
print $fh "hello world: val=" . qwparam('someparam') . "\n";
}
},
Currently only Gtk2 supports this button, but others will in the future. Patience, bestie ๐ซถ
imageโ an image file ๐ธ. The image name is specified by theimagehash keyword. Several optional hash keys are recognized to control display of the image:imagealtspecifies a string to display if the image file isn't found;heightspecifies the height of the image;widthspecifies the width of the image. (heightandwidthare currently only implemented for HTML.)graphโ a graph of passed data ๐. This is only available if theGD::Graphmodule is installed. Data gets passed in from the values clause and is expected to be an array of arrays of data, where the first row is the x-axis data and the rest are y values (one line gets drawn for each y value).
Also, the GD::Graph options can be specified with a graph_options tag on the question, which lets you create things like axis labels and legends. Gorgeous.
treeโ hierarchical tree ๐ณ. Displays a selectable hierarchical tree set, and the user picks a single item from it. Two references to subroutines must be passed in via theparentandchildrenquestion tags. Also, aroottag should specify the starting point.
Widget Options: ๐
parent => CODEREFโ the parent function gets called with a wizard reference and a node name. It's expected to return the name of the node's parent.
The function should return undef when no parent exists above the current node.
children => CODEREFโ the children function gets passed a wizard reference and a node name. It's expected to return an array reference to all the children names. It may also return a hash reference for some names instead โ that hash contains an internalnametag along with alabeltag, for displaying something to the user that's different than what's internally passed around as the resulting selected value. Sneaky, but make it fashion.
An example return array structure could look like:
[
'simple string 1',
'simple string 2',
{
name => 'myanswer:A',
label => 'Answer #A'
},
{
name => 'myanswer:B',
label => 'Answer #B'
},
]
and when there's literally nothing below the current node? your function should return undef. no children, no answer, no drama ๐
expand_all => NUM
You can pass the expand_all tag, and what it does is declare that all initial option trees should be expanded up to the number you give it. Three levels deep of open-ness, bestie, go off โจ
button
Button widget, periodt. When the button is clicked, the QWizard parameter name (which you grab by calling qwparam('name')) gets assigned the value indicated by the default clause. If the button is not clicked, that parameter value does not get set. ๐ The button's label text is set to the value of the values clause.
The button widget is equivalent to pressing the next button. So once your user presses it, the next primary will be shown. Slay, that's the whole point ๐
If a backend supports key accelerators (GTk2, looking at you ๐): button labels can be bound to Alt-key accelerators. See QUESTION KEY-ACCELERATORS below for more.
bar
A bar widget is functionally a separator in the middle of the list of questions. It's giving "let me split this set in two" energy โ it's useful for breaking a set of questions in two, and also for providing button-containers or menu containers within widget sets without having them tied to the normal QWizard left/right feel. One intentional artifact of this is that they can be used to provide a visual difference between the flow of the questions. EG, if the QWizard primary showed a screen which had two questions in it, it would look something like the following when displayed by most of the generators that exist today:
+-------------------+-----------------+
| Question 1 | Answer Widget 1 |
| Longer Question 2 | Answer Widget 2 |
+-------------------+-----------------+
Adding a bar in the middle of these questions, however, would break the forced columns above into separate pieces:
+------------+------------------------+
| Question 1 | Answer Widget 1 |
+------------+------------------------+
| BAR |
+-------------------+-----------------+
| Longer Question 2 | Answer Widget 2 |
+-------------------+-----------------+
Finally, there is an implicit top bar in every primary and the QWizard object as a whole. You can push objects onto this bar by adding objects to the $qwizard->{'topbar'} array or by adding objects to a primary's 'topbar' tag. E.G.
my $qw = new QWizard(primaries => \%primaries,
topbar => [
{
type => 'menu', name => 'menuname',
values => [qw(1 2 3 4)],
# ...
}]);
The widgets shown in the topbar will be a merge of those from the QWizard object and the primary currently being displayed.
TODO: make it work better with merged primaries
TODO: make a bottom bar containing the next/prev/cancel buttons
hidden
This clause is used to set internal parameters (name => value), but these values are not shown to the user.
Note: This is not a secure way to hide information from the user. The data set using hidden are contained, for example, in the HTML text sent to the user.
So yeah โ hidden from the screen, not from the source. Do not put your secrets in there bestie, I'm begging ๐ญ
dynamic
A dynamic question is one where the values field is evaluated and is expected to return an array of question definitions which are in turn each evaluated as a question. It is useful primarily when doing things like creating a user-defined number of input fields, or interacting with an external data source where the number of questions and their nature is directly related to the external data source. Basically: the questions build themselves ๐ iconic.
raw
Raw data. The values portion is displayed straight to the screen. Use of this is strongly discouraged. Obviously, the values portion should be a subroutine that understands how to interact with the generator.
Really, don't use this. It's for emergencies only. It only works with HTML output. ๐จ
values => [ VALUES ]
An array of values that may be assigned to question types that need choices (eg: menu, checkbox, multi_checkbox.) It should be a reference to an array containing a list of strings, functions to execute, and possibly sub-arrays containing a function and arguments, as described by the VALUE conventions section above. Any function listed in a values clause should return a list of strings.
Heads up though: the values clause is not needed if the labels clause is present. ๐ซถ
labels => [ VALUE1 => 'LABEL1', VALUE2 => 'LABEL2', ... ]
Assigns labels to the question's values. Labels are displayed to the user instead of the raw values. This is useful for converting human-readable text strings into real-world values for use in code. So the human sees the cute text, the code gets the real value โ everyone wins ๐
If the values clause is not specified and the labels clause is, the values to display are extracted from this labels clause directly. If a value from the values clause does not have a corresponding label, the raw value is presented and used instead. Generally, only the labels clause should be used with radio buttons, menus, or check boxes; but either or both in combination work.
The labels clause subscribes to all the properties of the VALUES convention previously discussed. Thus, it may be a function, an array of functions, or any other type of data that a VALUE may be. The final results should be an array, especially if the values clause is not present, as the order displayed to the user can be specified. It can also be a hash as well but the displayed order is subject to Perl keys() conventions and thus an array is preferred when no values clause has been defined. Order matters, bestie โ hash order is chaos ๐
default => VALUE
The default value to use for this question. It may be a subroutine reference which will be called to calculate and return the value. Lazy-computed defaults, we love to see it โจ
check_value => \&subroutine
A script to check the answer submitted by the user for legality. The script should return 'OK' to indicate no error found. In the event an error is detected, it should return an error string. The string will be shown to the user as an error message that the user must fix before being allowed to proceed further in the wizard screens. Alternately, the script may return 'REDISPLAY' to indicate no error but that screen should be redisplyed (perhaps with new values set with qwparam() from within the script). In the case of error or 'REDISPLAY', the current primary screen will be repeated until the function returns 'OK'.
So it's a little bouncer at the door of your wizard: 'OK' and you're in, an error string and the user has to fix it, 'REDISPLAY' and we just... run the screen back. Until you get that 'OK', nobody's going anywhere ๐
The arguments passed to the function are the reference to the wizard, a reference to the question definition (the hash), and a reference to the primary containing the question (also a hash.) The function should use the qwparam() function to obtain the value to check. An array can be passed in which the first argument should be the subroutine reference, and the remaining arguments will be passed back to the subroutine after the already mentioned default arguments.
There are a set of standard functions that can be used for checking values. These are:
\&qw_required_field
Ensures that a value is supplied or else a "This is a required field" error message is returned. The function only checks that the value is non-zero in length.
\&qw_integer, \&qw_optional_integer
Ensures that the value is an integer value (required or not, respectively.)
\&qw_hex, \&qw_optional_hex
Ensures that the value is a hex string (required or not, respectively.)
[\&qw_check_hex_and_length, length]
Ensures that a value is supplied and is a hex string sufficiently long for length bytes. This means that the hex string must be "length * 2" ASCII characters (two hex characters per byte.) Math is math, no cap ๐งฎ
[\&qw_check_int_ranges, low1, high1, low2, high2, ...]
Ensures that the value specified falls within one of the lowX - highX ranges. The value must be between (low1 and high1) or (low2 and high2).
[\&qw_check_length_ranges, low1, high1, low2, high2, ...]
qw_check_length_ranges is similar to qw_check_int_ranges(), but it checks that the length of the data string specified by the user falls within the given ranges.
doif => sub { LOGIC }โ this one lets questions be optional and only shows them under certain conditions. If you set it, it should be a CODE reference that returns1when the question should be displayed or0when it shouldn't. If it returns0, the question gets skipped entirely, no cap. ๐helptext,helpdescrโ if you specify these, they define the help text for a question.helpdescrshould be short descriptions printed on screen when the wizard screen is displayed, andhelptextshould be a full length description of help that will be displayed only when the user clicks on the help button.helpdescris optional, and a button will be shown linking tohelptextregardless. ๐indent => 1โ slightly indents the question for some generators. โจsubmit => 1โ when this is specified as a question argument, if the user changes the value then it will also be the equivalent of pressing the 'Next' button at the same time. With the HTML generator, this requires javascript and thus you shouldn't absolutely depend on it working. ๐ฅrefresh_on_change => 1โ if the contents of a screen are generated based on data extracted from dynamically changing sources (e.g., a database), then setting this parameter to1will make the current question force a refresh if the value changes (ie, when they pull down a menu and change the value or click on a checkbox or ...) and the screen be redrawn (possibly changing its contents). As an example, Net-Policy uses this functionality to allow users to redisplay generated data tables and changes the column that is used for sorting depending on a menu widget. ๐ฆhandle_results => sub { ... }โ thehandle_resultstag can specify a CODE reference to be run when the questions are answered so each question can perform its own processing. This is sort of like a per-questionpost_answershook equivalent. ๐
โจ QUESTION KEY-ACCELERATORS โจ
Some generators (currently only Gtk2 actually) support key accelerators so that you can bind alt-keys to widgets. This is done by including a _ (underscore) character where appropriate to create the binding. EG:
{
type => 'radio',
text => 'select one:',
values => ['_Option1','O_ption2', 'Option3']
}
When Gtk2 gets the above construct it will make Alt-o be equivalent to pressing the first option and Alt-p to the second. It will also display the widget with a underline under the character that is bound to the widget. HTML and other non-accelerator supported interfaces will strip out the _ character before displaying the string in a widget.
In addition, unless a no_auto_accelerators = 1> option is passed to the generator creation arguments, widgets will automatically get accelerators assigned to them. In the above case the 't' in Option3 would automatically get assigned the Alt-t accelerator (the 't' is selected because it hasn't been used yet, unlike the o and p characters). You can also prefix something with a ! character to force a single widget to not receive an auto-accelerator (EG: "!Option4" wouldn't get one). ๐
๐ SPECIAL VARIABLES ๐
A few QWizard parameters are special and help control how QWizard behaves. Most of these should be set in the primaries question sets using a hidden question type.
no_actionsโ if set to1, the actions phase will not be run. ๐no_confirmโ if set to1, the screen which prompts the user to decide if they really want to commit their series of answers won't be shown. Instead, QWizard will jump straight to the actions execution (if appropriate.) This can also be given as a parameter to the QWizardnew()function to make it always true. ๐ซถallow_refreshโ if the contents of a screen are generated based on data extracted from dynamically changing sources (e.g., a database), then setting this parameter to1will add a "Refresh" button beside the "Next" button so that the user can request the screen be redrawn (possibly changing its contents). As an example, Net-Policy uses this functionality to allow users to redisplay generated graphs and maps that will change dynamically as network data are collected. This token can also be set directly in a primary definition to affect just that primary screen. โจQWizard_nextโ the button text to display for the "Next" button. This defaults to"_Next"but can be overridden using this parameter. ๐QWizard_commitโ the button text to display for the "Commit" button. This defaults to"_Commit"but can be overridden using this parameter. The commit button is shown after the questions have been asked and theactions_descr's are being shown to ask the user if they really want to run the actions. ๐QWizard_finishโ the button text to display for the "Finish" button. This defaults to"_Finish"but can be overridden using this parameter. The finish button is shown after the actions have been run and the results are being displayed. ๐ธ
๐ QWIZARD RESERVED VARIABLES ๐
The following parameters are used internally by QWizard. They should not be modified.
pass_varsqw_cancelno_cancelqwizard_treedisplay_help_pdisp_q_numredo_screenupd_timewiz_confirmedwiz_canceledwizard_queue_list
QWizard OBJECT FUNCTIONS ๐ฆ
okay bestie, these are the functions that live in the QWizard class and you can call them whenever you need them. no cap.
$qw->magic(primary_name, ...);
This is the main character function. It tells QWizard to start its magic, beginning at the primary named primary_name. Multiple primaries will be displayed one after the other until the list of primaries to display is empty. The actions clauses of all these primaries will not be run, however, until after all the primaries have been processed.
The magic() routine exits only after all the primaries have been run up through their actions, or unless one of the following conditions occurs:
$qw->{'one_pass'} == 1$qw->{'generator'}{'one_pass'} == 1
By default, some of the stateless generators (HTML) will set their one_pass option automatically since it is expected that the client will exit the magic() loop and return later with the next set of data to process. The magic() routine will automatically restart where it left off if the last set of primaries being displayed was never finished. This is common for stateless generators like HTTP and HTML.
$qw->finished();
Closes the open qwizard window. Useful after your magic() routine has ended and you don't intend to call it again. Calling finished() will remove the QWizard window from visibility.
$qw->set_progress(FRACTION, TEXT);
If available, some generators (Gtk2 can) may be able to display a progress meter. If they are, calling this function inside action clauses, for example, will start the display of this meter with FRACTION complete (0 <= FRACTION <= 1). The TEXT is optional and if left blank will be set to NN% showing the percentage complete.
$qw->add_todos([options], primary_name, ...);
Adds a primary to the list of screens to display to the user. This function should be called during the post_answers section of a primary. Options that can be passed before the first primary name are:
-earlyโ Adds the primaries in question as early as possible in the todo list (next, unless trumped by future calls.) This is the default.-lateโ Adds the primary to the end of the list of primaries to call, such that it is called last, unless another call toadd_todos()appends something even later.-actionslastโ All the actions of subsequent primaries that have been added as the result of a current primary'spost_answersclauses are called before the actions for the current primary. This means that the actions of any childrens are executed prior to the actions of their parents. This is done by default, as the general usage prediction is that parent primaries are likely to be dependent on the actions of their children in order for their own actions to be successful.
However, this flag indicates that the actions of the childrens' primaries listed in this call are to be called before the current primary's actions.
-mergeโ Merges all the specified primaries listed into a single screen. This has the effect of having multiple primaries displayed in one window.
Important note: you can not use both-remap(see below) and-mergeat the same time! This will break the remapping support and you will not get expected results!
-remap => 'NAME'โ If a series of questions must be called repeatedly, you can use this flag to remap the names of the child primary questions to begin with this prefix. The children's clauses (questions, actions, post_answers, etc.) will be called in such a way that they can be oblivious to the fact this is being done behind their backs, allowingqwparam()to work as expected. However, for the current primary (and any parents), the'NAME'prefix will be added to the front of any question name values that the child results in defining.
This is rather complex and is better illustrated through an example. There is an example that illustrates this in the QWizard Perl module source code examples directory, in the file number_adding.pl. This code repeatedly asks for numbers from the user using the same primary.
Important note: you can not use both-remapand-mergeat the same time! This will break the remapping support and you will not get expected results!
$qw->add_primary(key => value, key => value);
Adds a primary definition into the existing primary data set for the QWizard object. One key value pair MUST be a 'name' => 'NAME' pair, where NAME will be the installed primary name for later referral (e.g., in add_todos() calls.) If a name collision takes place (a primary already exists under the given name), the original is kept and the new is not installed.
$qw->merge_primaries(\%new_primaries);
Merges a new set of primaries into the existing set. If a name collision takes place, the original is kept and the new is not installed.
$qw->get_primary('NAME');
Returns a primary definition given its NAME.
๐ซถ qwparam() โ the one that gets your user's answers
$val = $qw->qwparam('NAME')
$val = qwparam('NAME')
$qw->qwparam('NAME', 'VALUE')
qwparam('NAME', 'VALUE')
Retrieves a value specified by NAME that was submitted by a user from a QWizard widget. If a VALUE is specified as a second argument, it replaces the previous value with the new for future calls.
QWizard parameters are accessible until the last screen in which all the actions are run and the results are displayed. Parameters are not retained across primary execution.
The qwparam() function is exported by the QWizard module by default, so the function shouldn't need to be called directly from the QWizard object. Thus, just calling qwparam('NAME') by itself will work.
๐ qwpref() โ the persistent preference bestie
$val = $qw->qwpref('NAME')
$val = qwpref('NAME')
$qw->qwpref('NAME', 'VALUE')
qwpref('NAME', 'VALUE')
qwpref() acts almost identically to qwparam(), except that it is expected to be used for "preferences" -- hence the name. The preferences are stored persistently across primary screens, unlike parameters. Preferences are not erased between multiple passes through the QWizard screens. (In the HTML generator, they are implemented using cookies).
๐ช HOOKS ๐ช
TBD: Document the $qw->add_hook and $qw->run_hooks methods that exist.
(basically $qw->add_hook('start_magic', \&coderef) will run coderef at the start of the magic function. Search the QWizard code for run_hooks for a list of hook spots available.
๐ DEBUGGING ๐
The variable $QWizard::qwdebug controls debugging output from QWizard. If set to 1, it dumps processing information to STDERR. This can be very useful when debugging QWizard scripts as it displays the step-by-step process about how QWizard is processing information.
Additionally, a qwdebug_set_output() function exists which can control the debugging output destination. Its argument should be a reference to a variable where the debugging output will be stored. Thus, debugging information can be stored to a previously opened error log file by doing the following:
our $dvar;
$QWizard::qwdebug = 1;
$qw->qwdebug_set_output(\$dvar);
$qw->magic('stuff');
print LOGFILE $dvar;
โจ EXAMPLES โจ
There are a few usage examples in the examples directory of the source package. These examples can be run from the command line or installed as a CGI script without modification. They will run as a CGI script if run from a web server, or will launch a Gtk2 or Tk window if run from the command line.
๐ EXPORT ๐
qwparam(), qwpref()
qw_required_field(), qw_integer(), qw_optional_integer(), qw_check_int_ranges(), qw_check_length_ranges(), qw_hex(), qw_optional_hex(), qw_check_hex_and_length()
๐ AUTHOR ๐
Wes Hardaker, hardaker@users.sourceforge.net
๐ COPYRIGHT and LICENSE ๐
Copyright (c) 2003-2007, SPARTA, Inc. All rights reserved
Copyright (c) 2006-2007, Wes Hardaker. All rights reserved
QWizard is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
๐ฅ SEE ALSO ๐ฅ
For extra information, consult the following manual pages:
๐ QWizard_Widgets
Manual page for more information about the Various QWizard display generators and the questions and arguments that each one supports. This page is actually generated from what the generator actually advertises that it supports.
๐ซ QWizard::API
If you get tired of typing anonymous hash references, this API set will let you generate some widgets with less typing by using APIs instead.
Example API call:
perl -MQWizard::API -MData::Dumper -e 'print Dumper(qw_checkbox("my ?","it",'A','B', default => 'B'));'
$VAR1 = {
'text' => 'it',
'name' => 'my ?',
'default' => 'B',
'values' => [
'A',
'B'
],
'type' => 'checkbox'
};
๐ Net-Policy: http://net-policy.sourceforge.net/
The entire QWizard system was created to support a multiple-access-point network management system called "Net-Policy" and the SVN repository for Net-Policy actually contains the QWizard development tree.
okay bestie that's the whole QWizard(3) page, and this part came from perl v5.42.0, dated 2025-07-25, so you can cite it as QWizard(3) when you're being iconic. mwah ๐โจ
๐ฆ where this page comes from
- fedora 43: perl-QWizard 3.15-47.fc43 ยท GPL-1.0-or-later OR Artistic-1.0-Perl
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms