068. How to display progress bar while processing long task - Part 2 using_idle_add?

Solution

  • Just before the long task is started, first set up a dialog that contains a GtkProgressBar.
  • Then begin the task by setting up Gtk::idle_add to call your function (that process the long task) whenever it's idle.
  • Every time php-gtk has nothing to do, it will make a trip to your function. In your function, do whatever subtask you want to do. If there are more tasks to be completed, return a true. If all tasks are completed, return a false.
  • When all tasks is completed, you should also call Gtk::idle_remove to let php-gtk know that you're done. Of course, you may also want to close the progress bar dialog.
  • To trap the event in which the user closes the progress bar dialog, we set up a connect('delete-event').
  • When the user closes the progress bar in the midst of the long task, just like above, we call Gtk::idle_remove then close the progress bar dialog.
  • As explained in Part 1, don't forget to call while (Gtk::events_pending()) {Gtk::main_iteration();} so that the progress bar gets updated.

Sample Code for PHP GTK

1   
2   
3   
4   
5   
6   
7   
8   
9   
10   
11   
12   
13   
14   
15   
16   
17   
18   
19   
20   
21   
22   
23   
24   
25   
26   
27   
28   
29   
30   
31   
32   
33   
34   
35   
36   
37   
38   
39   
40   
41   
42   
43   
44   
45   
47   
48   
49   
50   
51   
52   
53   
54   
55   
56   
57   
58   
61   
62   
63   
64   
65   
66   
67   
68   
69   
70   
71   
72   
73   
74   
75   
76   
77   
78   
79   
80   
81   
82   
83   
84   
85   
86   
87   
88   
89   
90   
91   
93   
94   
95   
96   
97   
98   
99   
100   
<?php
$window = new GtkWindow();
$window->set_size_request(400, 175);
$window->connect_simple('destroy', array('Gtk','main_quit'));
$window->add($vbox = new GtkVBox());

// display title
$title = new GtkLabel("Display progress bar ".
"while processing long task - Part 2");
$title->modify_font(new PangoFontDescription("Times New Roman Italic 10"));
$title->modify_fg(Gtk::STATE_NORMAL, GdkColor::parse("#0000ff"));
$title->set_size_request(-1, 40);
$vbox->pack_start($title, 0, 0);
$vbox->pack_start(new GtkLabel(), 0, 0); // add a small gap

$vbox->pack_start(new GtkLabel('using Gtk::idle_add()'), 0, 0);
$vbox->pack_start(new GtkLabel('you can also interrupt the long task'), 0, 0);
$vbox->pack_start(new GtkLabel(), 0, 0); // add a small gap

$vbox->pack_start($hbox = new GtkHBox(), 0, 0);
$hbox->pack_start($button = new GtkButton('Start processing'), 1, 0);
$vbox->pack_start(new GtkLabel()); // add a small gap
$button->connect('clicked', 'start_processing');

$window->show_all();
Gtk::main();

function start_processing($button) {
    $do_long_task = new DoLongTask(); // note 1
    $do_long_task->idle_ID = Gtk::idle_add(
        array(&$do_long_task, 'process_task')); // note 2
}

class DoLongTask { // note 1

    var $progress_bar;
    var $dialog;
    var $subtask_count = 0;
    var $max_task_count = 10;
    var $idle_ID;

    function DoLongTask() {
        // setup a dialog containing progress bar
        $dialog = new GtkDialog('Work in progress...',
            null, Gtk::DIALOG_MODAL); // create a new dialog
        $top_area = $dialog->vbox;
        $top_area->pack_start(new GtkLabel(
            'Please hold on while processing data...'));
        $this->progress_bar = new GtkProgressBar();
        $this->progress_bar->set_orientation(Gtk::PROGRESS_LEFT_TO_RIGHT);
        $top_area->pack_start($this->progress_bar, 0, 0);
        $dialog->set_has_separator(false);
        $dialog->show_all(); // show the dialog
        $this->dialog = $dialog; // keep a copy of the dialog ID

        $dialog->connect('delete-event',
            array( &$this, "on_delete_event")); // note 3
    }

    // this is where you process your task
    function process_task() {
        echo "processing subtask $this->subtask_count: ";
        for ($i=0; $i<10; ++$i) {
            usleep(50000); // sleep for half a second
            print ".";
        }
        print "\n";

        ++$this->subtask_count;
        $percent_complete = $this->subtask_count/$this->max_task_count;
        $percent_complete2 = number_format(
            $this->subtask_count/$this->max_task_count*100,0);
        $this->progress_bar->set_fraction($percent_complete); // note 4
        $this->progress_bar->set_text(
            $percent_complete2.'% Complete'); // note 5
        while (Gtk::events_pending()) {Gtk::main_iteration();} // note 6

        if ($this->subtask_count < $this->max_task_count) { // task completed?
            return true; // not yet!
        } else {
            $this->dialog->destroy(); // yes, all done. close the dialog
            Gtk::idle_remove($this->idle_ID); // inform php-gtk that we're done
            return false; 
        }
    }

    // function that is called when user closes the progress bar dialog
    function on_delete_event($widget, $event) {
        $this->dialog->destroy(); // close the dialog
        Gtk::idle_remove($this->idle_ID); // inform php-gtk that we're done
        // any other clean-up that you may want to do
        return true;
    }
}

?>

Output for PHP GTK

As shown above.
 

Explanation for PHP GTK

  1. Wrap this in a class to avoid any global variables.
  2. Ask php-gtk to process your long task whenever it's free.
  3. Let us know if user closes the progress bar dialog.
  4. Update the percent value.
  5. Update the displayed text.
  6. Don't forget this so that progress bar gets updated.

Related Links

Add comment


Security code
Refresh