Understanding the how to deal with pending events is an important part of programming with GTK. While not technically accurate (in any regards), it is useful to think of a PHP-GTK application as two threads or processes working together for one program. In one hand you have PHP running the program, on the other you have the GTK library running an idle loop to process its UI. If you hard lock PHP into a processing (for, while, foreach) loop GTK will be unable to process its updates until PHP finishes, and visa versa.

Here is an example where you might run into this issue. Lets say you have a directory full of files you need to read, and a window displaying a progress bar. These are two symptoms you might encounter:

  1. the appearance that your program has hardlocked and must be killed, and then at the last minute your progress bar jumps to 100%.
  2. your progress bar updates sporadically jumping large amounts instead of smoothly across.

… and both of these, generally, can be solved very easily. Most of the time just telling GTK to process events is enough to do it. And how do we do that?

PHP Code:
<?php

while(Gtk::events_pending() || Gdk::events_pending()) {
    
Gtk::main_iteration_do();
}

?>

This while loop when inserted into your code after you update a UI element will force PHP to wait on GTK while it updates its UI. Basically the loop reads as, “while GTK has stuff to do, do it so we can move on.”

If you do lots of updates, pasting that all over your code might get messy looking. There is a solution for that too.

PHP Code:
<?php

function while_events_pending() {
    while(
Gtk::events_pending() || Gdk::events_pending()) {
        
Gtk::main_iteration_do();
    }
}

?>

Armed with that function in our code, now we would just call while_events_pending() every time we want to make sure the UI updates.

The Use Case

Referring back to my original example about a program that is reading a directory of files, and updating a progress bar, here is that application. It has two run modes, with and without events-pending enabled. Below the code is a zip file containing all the files this needs to run, including the dummy demo text files that it reads to waste time.

If you run this program as is, more often than not after clicking the Scan button you should see the progress bar jump from empty to full in a blink. However if you run this program (from the command line) with the argument –enable-pending, you should see the progress bar flow smooth from 0 to 100% and even display what file it is reading in the bar.

PHP Code:
<?php

//. run without enabling the pending events update.
//$ php events-pending-demo.phpg
//. expected result, progress bar goes from 0 to 100% in a blink.

//. run with the pending events enabled.
//$ php events-pending-demo.phpg --enable-pending
//. expected result, progress bar moves smoothly from 0 to 100%.

function bob_events_pending() {
    while(
Gtk::events_pending() or Gdk::events_pending()) {
        
Gtk::main_iteration_do();
    }
}

class 
bobWindow extends GtkWindow {
    public function 
__construct() {
        
parent::__construct();
        
        
$this->set_title('Events Pending Demo');
        
$this->set_size_request(300,-1);
        
$this->set_position(Gtk::WIN_POS_CENTER);
        
$this->set_border_width(10);
        
$this->connect_simple('delete-event',array($this,'onQuit'));
        
        
$this->vbox = new GtkVBox;
        
$this->bar = new GtkProgressBar;
        
        
$this->button = new GtkButton('Scan');
        
$this->button->connect_simple('clicked',array($this,'beginScan'));
        
        
$this->vbox->pack_start($this->bar,true,true,4);
        
$this->vbox->pack_start($this->button,true,true,4);
        
$this->add($this->vbox);
        
$this->show_all();
        
        return;
    }
    
    public function 
onQuit() {
        
Gtk::main_quit();
        return;
    }
    
    public function 
beginScan() {        
        
$list glob(sprintf('%s/lolectory/*.txt',dirname(__FILE__)));
        
$filecount count($list);
        
$charcount 0;
                
        
sort($list);

        
//. process each file now.        
        
foreach($list as $iter => $filename) {
            
            
//. update UI with current status.
            
$this->bar->set_fraction(($iter+1)/$filecount);
            
$this->bar->set_text(sprintf('Reading %s...',basename($filename)));
            if(
defined('BOB_ENABLE_PENDING')) bob_events_pending();
        
            
//. do actual processing.
            
$fp fopen($filename,'r');
            while(
$line trim(fgets($fp))) {
                foreach(
str_split($line,1) as $char) {
                    
// lollin the time away.
                    // pretend we be processin
                    
++$charcount;
                }
            } 
fclose($fp);
        }
        
        
//. all done scanning!
        
$this->bar->set_text("Done! Processed {$charcount} characters.");
    
        return;
    }
}

//. checking for my quick cli option.
if(array_key_exists(1,$_SERVER['argv'])) {
    if(
$_SERVER['argv'][1] == '--enable-pending') {
        
define('BOB_ENABLE_PENDING',true);
    }
}

new 
bobWindow;
Gtk::main();

?>