You have a long task to execute, and you want to display a progress meter that show the
progress of the task. How can you do it ?
Without Progress callback
Goal of this example is to simulate packages download (user task). Here is a screenshot
of what we will produces :
On left frame we find a form used to select what package the user want to download.
On right frame we show an horizontal progress bar with results of download process.
Code listing 2.2: a package select-download demonstration
- <?xml version="1.0" encoding="utf-8"?>
- <!DOCTYPE html
- PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
- "http://www.w3c.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
-
- <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
- <head>
- <meta name="robots" content="noindex, nofollow" />
- <meta name="keywords" content"="HTML_Progress, PEAR, progress meter" />
- <meta name="description" content"="HTML_Progress " />
- <meta name="author" content="Laurent Laville" />
- <title>Code listing 2.2: a software installation </title>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
- </head>
- <frameset cols="300, *">
- <frame name="files" src="listing-2.2.1.php" />
- <frame name="meter" src="listing-2.2.2.php" scrolling="no" />
- <noframes>
- <body>
- <p>Your browser does not support frames.</p>
- </body>
- </noframes>
- </frameset>
- </html>
Here is the main page: a frameset which contains the two frames :
Left (see code listing 2.2.1) and
Right (see code listing 2.2.2).
Code listing 2.2.1: a package selection
- <?php // listing-2.2.1.php
- require_once 'HTML/QuickForm.php';
-
- $form =& new HTML_QuickForm('installer', 'post', $_SERVER['PHP_SELF'], 'meter');
-
- $form->addElement('header', null, 'Choose PEAR packages to download');
-
- $checkbox[] = &HTML_QuickForm::createElement('checkbox', 'Archive_Tar', null, 'Archive_Tar');
- $checkbox[] = &HTML_QuickForm::createElement('checkbox', 'Config', null, 'Config');
- $checkbox[] = &HTML_QuickForm::createElement('checkbox', 'HTML_QuickForm', null, 'HTML_QuickForm');
- $checkbox[] = &HTML_QuickForm::createElement('checkbox', 'HTML_CSS', null, 'HTML_CSS');
- $checkbox[] = &HTML_QuickForm::createElement('checkbox', 'HTML_Page', null, 'HTML_Page');
- $checkbox[] = &HTML_QuickForm::createElement('checkbox', 'HTML_Template_Sigma', null, 'HTML_Template_Sigma');
- $checkbox[] = &HTML_QuickForm::createElement('checkbox', 'Log', null, 'Log');
- $checkbox[] = &HTML_QuickForm::createElement('checkbox', 'MDB', null, 'MDB');
- $checkbox[] = &HTML_QuickForm::createElement('checkbox', 'PHPUnit', null, 'PHPUnit');
- $form->addGroup($checkbox, 'packages', 'Packages:', '<br />');
-
- $form->addElement('submit', 'submit', 'Download');
-
- if ($form->validate()) {
-
- include_once 'listing-2.2.2.php';
-
- $packages = $form->exportValue('packages');
- $percent = 0;
-
- echo '<p><font face="Courier">';
- foreach ($packages as $pkg => $bool) {
-
- $msg = str_pad("Downloading package: $pkg", max(50,21+strlen($pkg)+4), '.');
- print $msg;
- /* Here you have to the job : download the package */
- sleep(1); // but as it's a tutorial we do nothing else than wait ...
-
- print " OK<br/>\n";
-
- $percent += intval(round(100 / count($packages)));
- $progress->setValue($percent);
- $progress->display();
- }
- echo '</font></p>';
-
- if ($percent < 100) {
- $progress->setValue(100);
- $progress->display();
- }
-
- } else {
- $form->display();
- }
- ?>
The form that allows to select what package to download, was built with help
of PEAR::HTML_QuickForm package.
If you don't know it, i suggest you to read the
QuickForm Manual
on PEAR website.
Let's consider now the important lines in this script:
Line 4, the form target is the right frame
(meter: see line 17 of code listing 2.2).
That will allows results of lines 23 thru 47 to be display in the good place.
Line 21, the form is validate.
Next (line 25) we extract data to know your package-selection.
On lines 29 to 41, we find the master process: download simulation.
If we have a real download operation, it should replace the sleep function at line 34.
Lines 31 and 32 prepare the result of operation which is given at line 36.
Line 38 we compute the next value of the progress bar.
We make changes at line 39
and refresh the progress bar at line 40.
Code listing 2.2.2: slave-progress pattern
- <?php // listing-2.2.2.php
- require_once 'HTML/Progress.php';
-
- $progress = new HTML_Progress();
-
- $ui = & $progress->getUI();
- $ui->setCellCount(20);
- $ui->setBorderAttributes('width=1 color=#000000');
- $ui->setCellAttributes(array(
- 'active-color' => '#970038',
- 'inactive-color' => '#FFDDAA',
- 'width' => 20,
- 'height' => 20
- ));
- ?>
- <!DOCTYPE html
- PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
- "http://www.w3c.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-
- <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
- <head>
- <style type="text/css">
- <!--
- <?php echo $progress->getStyle(); ?>
-
- body {
- background-color: #FFFFFF;
- color: #000000;
- font-family: Verdana, Arial;
- }
- // -->
- </style>
- <script type="text/javascript">
- <!--
- <?php echo $progress->getScript(); ?>
- //-->
- </script>
- </head>
- <body>
-
- <?php echo $progress->toHtml(); ?>
-
- </body>
- </html>
There are not so many changes on this script since Your first Progress Meter.
You may notice at least four important differences.
- No need to slow progress bar animation with HTML_Progress::setAnimSpeed
- The progress bar has 20 cells long rather than 10 (line 7)
- We use W3C doctype lines 16 to 20 to avoid IE box model break rules,
because we have choosen to display a progress bar with a border (line 8).
- The cells got a new layout (lines 9 to 14)
Use a Progress callback
Sometimes you can't immediately determine the length of a long-running task.
Goal of this example is to display a progress meter in indeterminate mode
(while you determines the length of the task), and then switch back in determinate mode
to finish the works.
Code listing 2.3: master-progress pattern
- <?php
- require_once 'HTML/Progress.php';
-
- function myProgressHandler($progressValue, &$bar)
- {
- static $c;
-
- if (!isset($c)) {
- $c = 0;
- }
-
- $bar->sleep(); // wait 0.5 second
-
- if ($bar->isIndeterminate()) {
-
- if ($progressValue == 100) {
- $c++;
- echo "myProgressHandler -> loop #$c <br/>\n";
- }
-
- /* switch back from indeterminate to determinate mode
- after 3 full loops */
- */
- if ($c == 3) {
- $bar->setIndeterminate(false);
- $bar->setValue(0);
- $bar->setString(null); // re-show percent info
- }
- }
- }
-
- $progress = new HTML_Progress();
- $ui = & $progress->getUI();
- $ui->setProgressAttributes(array(
- 'background-color' => '#e0e0e0'
- ));
- $ui->setStringAttributes(array(
- 'color' => '#996',
- 'background-color' => '#CCCC99'
- ));
- $ui->setCellAttributes(array(
- 'active-color' => '#996'
- ));
-
- $progress->setAnimSpeed(500);
- $progress->setIncrement(10);
- $progress->setStringPainted(true); // get space for the string
- $progress->setString(""); // but don't paint it
- $progress->setIndeterminate(true); // Progress start in indeterminate mode
- $progress->setProgressHandler('myProgressHandler');
- ?>
- <html>
- <head>
- <title>Listing 2.3 </title>
- <style type="text/css">
- <!--
- body {
- background-color: #CCCC99;
- color: #996;
- font-family: Verdana, Arial;
- }
-
- <?php echo $progress->getStyle(); ?>
- // -->
- </style>
- <script type="text/javascript">
- <!--
- <?php echo $progress->getScript(); ?>
- //-->
- </script>
- </head>
- <body>
-
- <?php
- echo $progress->toHtml();
- $progress->run();
- ?>
-
- </body>
- </html>
The user callback 'myProgressHandler' defined on lines 4 thru 30,
present the user-task. We informs HTML_Progress at line 50 to use
this user callback.
The progress meter will run in indeterminate mode for 3 full loops (equivalent to 300%).
This rule is given at line 24.
Process go back in determinate mode at line 25.
Next (lines 26 and 27) we finish the works with a standard behavior.