280. How to set up wizards using GtkDialog - Part 1?

Problem

As I've mentioned in my blog article titled "Some problems with GtkAssistant", currently the 'apply' signal and the method set_forward_page_func() are not working properly yet. This means that it's difficult for you to set up anything useful yet using GtkAssistant.

After playing with GtkAssistant for a while, I thought all the functionalities provided by this new widget can be reproduced exactly using GtkDialog.

So here it is — the same wizard reproduced using just plain old GtkDialog!

Since this uses only GtkDialog and not GtkAssistant, you can run the sample code below using any php-gtk2 without gtk+2.10 (yes, the Gnope version too).

Note that using this version, we're now able to grab the information entered in Step 1 (the name and email) to be used in the final step as shown below.

How to set up wizards using GtkDialog - Part 1?


Solution

The code looks long, but it's actually very straightforward.

  • The rectangle frame is constructed using a GtkFrame.
  • The title comprises a GtkLabel stuffed inside a GtkEventBox with a blue background.
  • Maybe the only "tricky" part is the swapping in and out of the pages.
    1. Each page is just a GtkVBox stored in the array $page[$page_num].
    2. When the user clicks the Next button, we remove the old page from the container ($page_vbox), and swap in the vbox containing the new page.
    3. Note that the method GtkContainer::remove() just remove the link from the container. The contents of all the pages remain intact in the memory. It's just hidden.
  • The same goes with the three GtkButtons at the bottom. We hide and show them according to which page the user is currently on.
  • The global variable $current_page keeps track of the current page the user is on.
  • In the event handler on_button(), we can also perform actions depending on which page the user is on. For example, in this example, we retrieve the name and email entered by the user when the user clicks on the Next button on page 1.

Sample Code

1   
2   
3   
4   
7   
8   
9   
10   
11   
12   
13   
15   
16   
17   
18   
20   
21   
22   
23   
24   
25   
26   
27   
28   
29   
30   
31   
32   
33   
34   
35   
36   
37   
38   
42   
43   
44   
45   
47   
48   
49   
50   
54   
55   
56   
57   
59   
60   
61   
62   
66   
68   
69   
70   
71   
72   
73   
78   
79   
86   
87   
88   
89   
90   
91   
92   
93   
95   
97   
98   
99   
100   
101   
102   
103   
104   
105   
107   
108   
109   
110   
111   
112   
113   
114   
115   
116   
117   
118   
119   
120   
121   
122   
123   
124   
125   
126   
127   
128   
129   
130   
131   
132   
133   
134   
135   
136   
137   
138   
139   
140   
141   
142   
143   
144   
145   
148   
149   
153   
154   
155   
156   
157   
158   
159   
160   
161   
162   
163   
164   
165   
166   
167   
168   
169   
171   
172   
173   
174   
175   
176   
177   
178   
179   
180   
181   
182   
183   
184   
185   
186   
187   
188   
189   
190   
191   
192   
193   
194   
195   
196   
197   
198   
199   
200   
201   
202   
203   
204   
205   
206   
207   
208   
209   
210   
211   
<?php
// create a new dialog
$dialog = new GtkDialog('Alert', null, Gtk::DIALOG_MODAL); // create a new dialog
$dialog->set_title($argv[0]);
$dialog->set_size_request(400,300);

// create a frame with blue border
$frame = new GtkFrame();
$frame->modify_bg(Gtk::STATE_NORMAL, GdkColor::parse("#08216B"));
$frame->modify_fg(Gtk::STATE_NORMAL, GdkColor::parse("#08216B"));
$frame->modify_base(Gtk::STATE_NORMAL, GdkColor::parse("#08216B"));
$dialog->vbox->pack_start($frame);

// create the title box with blue background
$eventbox = new GtkEventBox();
$eventbox->modify_bg(Gtk::STATE_NORMAL, GdkColor::parse("#08216B"));
$title = new GtkLabel('Create New Account');
$title->modify_fg(Gtk::STATE_NORMAL, GdkColor::parse("#ffffff"));
$title->modify_font(new PangoFontDescription("Bold 14"));
$title->set_size_request(-1, 40);
$alignment = new GtkAlignment(0, 0.5, 0.04, 0.04);
$alignment->add($title);
$eventbox->add($alignment);
$top_area = new GtkVBox();
$frame->add($top_area);
$top_area->pack_start($eventbox, 0);

// create the content area for each page
$page_vbox = new GtkVBox();
$top_area->pack_start($page_vbox);
$top_area->pack_start(new GtkLabel());

//intro
$page[0] = new GtkVBox();
$page[0]->pack_start(new GtkLabel(
"This wizard will help you create a new account.\n\n".
"Creating a new account is just a two-step process.\n\n".
"Click the Next button to start getting a free email account."));
$page[0]->show_all();

//Step 1
$page[1] = new GtkVBox();
$page[1]->pack_start(new GtkLabel("Please enter your name and email, \n".
"and click Next"));
$name_entry = setup_textentry($page[1], 'Name: ');
$email_entry = setup_textentry($page[1], 'Email: ');
$page[1]->show_all();

//Step 2
$page[2] = new GtkVBox();
$page[2]->pack_start(new GtkLabel("This is Step 2."));
$page[2]->show_all();

//Done
$page[3] = new GtkVBox();
$page3_text = new GtkLabel();
$page[3]->pack_start($page3_text);
$page[3]->show_all();

// start with the first page
$page_vbox->pack_start($page[0], 1);
$current_page = 0;

// set up the buttons
$hbox = new GtkHBox();
$hbox->pack_start(new GtkLabel());
$button_back = create_button($hbox, Gtk::STOCK_GO_BACK, 'Back');
$button_next = create_button($hbox, Gtk::STOCK_GO_FORWARD, 'Next');
$button_close = create_button($hbox, Gtk::STOCK_CLOSE, 'Close');
$dialog->vbox->pack_start(new GtkVBox(), 0);
$dialog->vbox->pack_start($hbox, 0);

$dialog->set_has_separator(false);
$dialog->action_area->set_size_request(-1, 1);
$dialog->show_all();
$button_back->hide();
$button_close->hide();
$button_next->grab_focus();

// all set! let's go!
$dialog->run();
$dialog->destroy();

function setup_textentry($container, $label) {
    $hbox = new GtkHBox();
    $hbox->pack_start(new GtkLabel($label), 0);
    $entry = new GtkEntry();
    $hbox->pack_start($entry, 0);
    $container->pack_start($hbox, 0);
    return $entry;
}

function on_button($button, $label) {
    global $page_vbox, $current_page, $page;
    global $button_back, $button_next, $button_close;

    if ($label=='Close') {
        global $dialog;
        $dialog->destroy();
        return;
    }

    global $name_entry, $email_entry;
    global $name, $email;
    if ($current_page==1) {
        $name = $name_entry->get_text(); // note 5
        $email = $email_entry->get_text(); // note 5
        echo "name = $name\n";
        echo "email = $email\n";
    }

    $button_back->hide();
    $button_next->hide();
    $button_close->hide();

    if ($label=='Next') {
        $page_vbox->remove($page[$current_page]); // note 1
        if ($current_page<3) ++$current_page; // note 2
    } elseif ($label=='Back') {
        $page_vbox->remove($page[$current_page]);
        if ($current_page>0) --$current_page;
    }

    echo "new current_page = $current_page\n";
    $page_vbox->pack_start($page[$current_page], 0); // note 3
    $page[$current_page]->show_all();

    global $title;
    switch($current_page) { // note 4
        case 0: $title->set_text('Create New Account'); break;
        case 1: $title->set_text('Step 1 of 2'); break;
        case 2: $title->set_text('Step 2 of 2'); break;
        case 3: $title->set_text('Done'); break;
    }

    if ($current_page==count($page)-1) {
        $button_close->show();
        $button_close->grab_focus();
        global $page3_text;
        $page3_text->set_text("Congratulations, $name!\n\n". // note 6
        "Your new account has been successfully created.\n\n".
        "A confirmation email has been sent to: $email");
    } else {
        if ($current_page<(count($page)-1)) $button_next->show();
        if ($current_page>0) $button_back->show();
    }

}

function create_button($container, $stock_id, $label='', $width=82, $height=32) {
    if ($label=='') {
        return GtkButton::new_from_stock($stock_id);
    }

    $button = new GtkButton();
    $hbox = new GtkHBox();
    $button->add($hbox);
    $hbox->pack_start(new GtkLabel());

    $img=GtkImage::new_from_stock($stock_id,
        Gtk::ICON_SIZE_SMALL_TOOLBAR);
    $hbox->pack_start($img, 0, 0);

    $hbox->pack_start(new GtkLabel(), 0, 0);
    $hbox->pack_start(new GtkLabel($label), 0, 0);

    $button->connect('clicked', 'on_button', $label);
    $button->set_size_request($width, $height);
    $container->pack_start($button, 0);
    $hbox->pack_start(new GtkLabel());

    return $button;
}

?>

Output

As shown above.
 

Explanation

Note that we make use of the technique as outlined in the article How to use stock images for buttons but with different labels - Part 1? to display buttons that use stock images but with different labels.

What's new here:

  1. Swap out the old page.
  2. If the user clicks the Next button, we increment the page counter.
  3. Swap in the new page.
  4. Set the page title depending on which page the user is on.
  5. For Step 1, we grab the name and email from the user input.
  6. Note how we displayed the name and email on this page using the user inputs the user entered in Step 1.

Note

You'll probably find this version much more "powerful" and flexible than using GtkAssistant as you have now control over every single widget in the wizard. For example, note that we have changed the label of the Forward button to "Next". At least for now, it's not possible for you to change the button label if you are using GtkAssistant.

More details in the blog article titled Implementing the GtkAssistant using GtkDialog.

Related Links

Add comment


Security code
Refresh