Basics of Timeout Functions
Making an application run a function at regular intervals.
Topic: General PHP-GTK (View All Tutorials)
Keywords: timeout_add interval timer
Updated: 351 Days Ago, 2007/12/16 15:38
Another useful feature of the GTK main loop is to have it automatically execute a function or method at regular intervals. This is done with timeouts, with the static methods of Gtk::timeout_add(int ms, callback func[, mixed args[, ...]]) and Gtk::timeout_remove(int timer). For example, to run a function roughly¹ once every second: Gtk::timeout_add(1000, 'my_function');

This demo application will run two different timers at the same time. The first one will update the time/date once every second. The second timeout will run the progress bar which will fill up to the right and then empty to the right. The progress bar itself means nothing, it is just there to provide a means of animation as a physical manifestation of the GTK timeout.
Timeout Demo Application
#!/usr/bin/php -c/etc/gtk/php.ini
<?php
class bobWindow extends GtkWindow {
// note 1
private $tid1,$tid2; // timeout identifiers.
private $clocktick; // clock tick sentinel.
private $piter,$pdir; // progress bar maintenance.
public function __construct() {
parent::__construct();
$this->clock = new GtkLabel;
$this->bar = new GtkProgressBar;
$this->vbox = new GtkVBox;
$this->hbox = new GtkHBox;
$this->connect_simple('delete-event',array($this,'on_quit'));
$this->piter = 0;
$this->pdir = false;
$this->clocktick = true;
// timeout function for the clock just re-renders it, so might as well
// use it to initalize the clock display too.
$this->on_timeout_one();
// note 2
$this->tid1 = Gtk::timeout_add(1000,array($this,'on_timeout_one'));
$this->tid2 = Gtk::timeout_add(50,array($this,'on_timeout_two'));
$this->set_size_request(300,-1);
$this->set_resizable(false);
$this->set_position(Gtk::WIN_POS_CENTER);
$this->set_title('GTK Timeout Demo');
$this->bar->set_pulse_step(0.01);
$this->clock->set_justify(Gtk::JUSTIFY_CENTER);
$this->vbox->pack_start($this->clock,true,true,3);
$this->vbox->pack_start($this->bar,true,true,3);
$this->hbox->pack_start($this->vbox,true,true,3);
$this->add($this->hbox);
$this->show_all();
return;
}
public function on_timeout_one() {
// run the clock.
$this->clock->set_markup(sprintf(
"<span size='x-large' weight='bold'><tt>%s%s%s</tt></span>\n".
"<span size='large' style='italic'>%s</span>",
date('h'),
(($this->clocktick)?(':'):(' ')),
date('i A'),
date('l F jS, Y')
));
$this->clocktick = !$this->clocktick;
// note 3
return true;
}
public function on_timeout_two() {
// run the progress bar.
// (which is really the progress of nothing)
if($this->pdir) { --$this->piter; }
else { ++$this->piter; }
$this->bar->set_fraction(($this->piter / 100));
if($this->piter == 100) {
// empty bar.
$this->bar->set_orientation(Gtk::PROGRESS_RIGHT_TO_LEFT);
$this->pdir = true;
} else if($this->piter == 0) {
// fill bar.
$this->bar->set_orientation(Gtk::PROGRESS_LEFT_TO_RIGHT);
$this->pdir = false;
}
return true;
}
public function on_quit() {
$this->hide();
Gtk::main_quit();
return;
}
}
$window = new bobWindow;
Gtk::main();
$window->destroy();
unset($window);
?>
Note 1
This class has some private properties which are just used by the functions that are to do the GUI work, like render the clock and the progress bar. I track every other second for the clock to make the colon blink, and I track the current direction the progress bar is filling so that it can be switched which it reaches the end.
Note 2
Here is where the timeouts are created. The Gtk::timeout_add() method returns a unique ID which you can later use on Gtk::timeout_remove() to manually kill off the timeout if needed. The second parametre (the callback) works just like the signal callbacks do, you pass a string of the function name, or an array with an instance of the object and the string of the method name.
Note 3
Both of the functions used in these timeouts always return true. This is because as long as they return true, they will continue to happen every interval that you specified. Returning void (not returning anything) or returning false will halt and destroy the timer. This is useful if your timeout is checking the status of something, it can automatically terminate after the conditions have been met.
End Game
Footnote 1
It is worth nothing the use of the word 'roughly' because as it takes time to things. Let's say we have a 1 second timer and we let it execute 60 times to update a progress bar. After 60 times, it might have taken 60.01 seconds because we wasted time updating the GUI. Also if you get your program caught in a processing loop, unless you manually force pending updates the timeouts will not happen until your processing loop is done.