Code hints

This "Code hints" section contains useful examples on how to manipulate certain combination of widgets and/or calls to achieve a given result.

Simpler, one-widget / one-scenario examples are located in the Single widget examples section.

Largers examples with extension classes are located in the Code snippets section.

On to the hints !

Activate menu bar with ALT key

This enables a user to activate the menu without using the mouse or combining keys, just by pressing ALT. This can be important for accessibility.

<?php

if (!extension_loaded('gtk')) {
 
dl( 'php_gtk.' . PHP_SHLIB_SUFFIX);
  }
 
/**
* test functions
*/
function func1() {
 
$label = &new GtkLabel( 'Go selected');
 
$label->show();
 
$GLOBALS['vbox']->add($label);
  }

function
func2(){
 
$label = &new GtkLabel( 'Clear selected');
 
$label->show();
 
$GLOBALS['vbox']->add($label);
  }

/**
* Body of code
*/

//simple menu items
$menu_info = array(
 
'_Test'=>array(
   
'_Go'=>'func1',
   
'_Clear'=>'func2',
   
'seperator'=>'',
   
'E_xit'=>array('gtk','main_quit'),
  )
);

$window = &new GtkWindow();
$window->set_default_size(350, 450);
$window->connect_object('destroy', array('gtk', 'main_quit'));

//create accegroup for main window
$accelgroup = &new GtkAccelGroup();
$window->add_accel_group($accelgroup);

//add vbox
$vbox = &new GtkVBox(FALSE, 0);
$vbox->show();
$vbox->set_spacing(1);
$window->add($vbox);

$menubar = &new GtkMenuBar();
$menubar->set_shadow_type(GTK_SHADOW_ETCHED_IN);

//make menu

foreach ($menu_info as $title=>$items) {
 
$menu = &new GtkMenuItem($title);
 
$mlabel = $menu->child;
 
$mlabel_key = $mlabel->parse_uline($title);
  if(
$mlabel_key) {
   
//add accel with Alt key
   
$menu->add_accelerator('activate_item', $accelgroup,
     
$mlabel_key, GDK_MOD1_MASK, 0);
   
$menu->lock_accelerators();
  }
 
$menubar->append($menu);
 
$menu->show();
 
$submenu = &new GtkMenu();
 
 
//create a new accelgroup for submenus
 
$accel = $submenu->ensure_uline_accel_group();
  foreach (
$items as $sub_title=>$function) {
    if(
$sub_title == 'seperator') {
     
$item =& new GtkMenuItem();
     
$item->set_sensitive(false);
    } else {
     
$item =& new GtkMenuItem($sub_title);
     
$label = $item->child;
     
$label_key = $label->parse_uline($sub_title);
      if (!empty(
$function)) {
       
$item->connect_object('activate',$function);
        if(
$label_key){
         
$item->add_accelerator('activate_item', $accel, $label_key, 0, 0);
         
$item->lock_accelerators();
        }
      }
    }
   
$submenu->append($item);
   
$item->show();
  }
 
$menu->set_submenu($submenu);

}

$menubar->show();
$vbox->pack_start($menubar,FALSE,FALSE,0);
$window->show_all(); gtk::main();
?>

Capture GtkCList selection

You have to connect the button-release-event after all other functions have worked.

<?php

// Connect signal
$this->arWidgets["lstExcludes"]->connect_after(
 
"button-release-event",
  array(&
$this, "OnClickList"));

// Get the current selection:
<?php
function OnClickList() {
 
$nSelRow = $this->arWidgets["lstExcludes"]->selection[0];
  if(
is_int( $nSelRow)) {
    echo
get_text( $nSelRow, 0);
    }
  }
//function OnClickList()
?>

The text in col 1 of the selected row is echoed now.

Extending widgets with PHP

primitive widget

  • you need to extend GtkWidget
  • you need to write many methods
  • your constructor MUST NOT call parent::__construct, or you will have an error (Fatal error: Cannot instantiate abstract class) for primitive widgets (abstract ?) (GtkWidget),
<?php
# simple code fragment (need to be completed)
class PrimitiveWidget extends GtkWidget {
  function
__construct() {
   
# Fatal error: Cannot instantiate abstract class
    # PrimitiveWidget in line ... (__construct() line call)
    # parent::__construct();
 
}
}

$w = new PrimitiveWidget();
?>

composite widget

  • you can extend from GtkVbox for example
  • constructor will have to create all needed objects,
  • no need to write many methods,
  • here is a working sample code :
<?php
class CompositeWidget extends GtkVbox {
  function
__construct() {
   
parent::__construct();
   
$this->label = new GtkLabel('test composite widget');
   
$this->add($this->label);
   
$this->add(new GtkLabel('an other label'));
   
$this->add(new GtkButton('Test me'));
   
$this->set_border_width(10);
  }
}

$win = new GtkWindow();
$win->set_title('Composite Widget extension example');
$win->connect_simple('destroy', array('gtk', 'main_quit'));

$composite_widget = new CompositeWidget();

$vbox = new GtkVBox();
$win->add($vbox);

$vbox->pack_start($composite_widget, true);

$win->show_all();
Gtk::main();
?>

links

Grabbing a screenshot with Gdk

From Widget to Pixbuf

Need to grab a screenshot in your program? Or maybe you just need to create an image from an existing GtkWidget. There's an easy way to accomplish both tasks using GdkPixbuf::get_from_drawable();

To use the method you'll need two things - an empty GdkPixbuf and the "drawable" (GdkDrawable) associated with your widget. Most widgets will have a GdkWindow located in $widget->window. You can get the entire screen, however, by using Gdk::get_default_root_window();. Now for the code examples...

Creating a screenshot

<?php
/**
* This comes first because we need to make sure all
* widgets are shown on the screen before we
* take a picture of them
*/
while (Gtk::events_pending())
  {
 
Gtk::main_iteration();
  }

// get the root gdkwindow (entire screen)
$root = Gdk::get_default_root_window();

// get the size of the screen
list($width, $height) = $root->get_size();

// create an empty pixbuf the same size
$pixbuf = new GdkPixbuf(Gdk::COLORSPACE_RGB, TRUE, 8,
 
$width, $height);

// grab a pixbuf from the screen
$pixbuf->get_from_drawable ($root, $root->get_colormap(), 0, 0,
 
0, 0, $width, $height);
?>

Creating a pixbuf of a specific widget

<?php
/**
* remember the widget MUST BE SHOWN AND DRAWN
* and not offscreen or you'll get weird black areas
*/
$widget->show();
while (
Gtk::events_pending())
  {
 
Gtk::main_iteration();
  }

/**
* we'll use allocation information: not every widget has
* its own gdkwindow
*/
$alloc = $widget->allocation;
$pixbuf = new GdkPixbuf(Gdk::COLORSPACE_RGB, TRUE, 8,
 
$alloc->width, $alloc>height);
$pixbuf->get_from_drawable ($widget->window,
 
$widget->window->get_colormap(),
 
$alloc->x, $alloc->y,
 
0, 0,
 
$alloc->width, $alloc->height);
?>

After your pixbuf is created you can save it to a file, or display it in a GtkImage or manipulate it with GdkPixbuf methods- have fun.

GtkHtml and Glade

Hi, there is a way to use the gtkhtml component with glade / phpgtk.

In glade, create a GtkScrolledWindow and add a Custom component with gtk_html_new as creation function name.

The following php code produced a segmentation fault, on my Debian :(

<?php
// $html is some text variable that
// contains html code
$html = "<html><body>foo</body></html>";

// generate a glade layout from
// the glade xml file
$layout = &new GladeXml("test.glade");

// retrieve the Custom component named "htmlcomponent"
$gtkhtml = &$layout->get_widget("htmlcomponent");

// gtkhtml component MUST have a content
// or the program will generate a
// segmentation fault (another one yes)
$stream = &$gtkhtml->begin();

$gtkhtml->write ($stream, $html, strlen($html) );
$gtkhtml->end ($stream, GTK_HTML_STREAM_OK);
?>

To get rid of this segfault, I removed the GtkViewPort widget from the produced glade xml (I found no way to remove it from the glade interface).

The glade file is alway usuable within glade so you can modify the interface...

I hope this will help some coders :)

For a full list of gtkhtml callbacks and functions, refer to gtkhtml.h file in gtkhtml-dev packages.


NOTE : This comment was taken directly from http://gtk.php.net/manual/en/glade.gladexml.php

Non-blocking I/O with Gtk::io_add_watch()

This is a sample code to demonstrate the use of Gtk::io_add_watch(). The main goal of this function is to handle socket IO without blocking. Socket polling is done in Gtk::main() loop. You don't need to care about socket polling. This task is done by Gtk and Glib.

In this example, the function is used for building an simple IRC client that connect to an IRC server, identifying itself, connecting to #php-gtk, and doing some boring things.

Since this is just a use case for the API, there is no user interface.

There is a package on Gnope server based on this documentation and class.

Function interface description

Function call parameters

io_id Gtk::io_add_watch($stream, $conditions, $callback)

$conditions parameter description : this is a mask of the following values :

  • Gtk::IO_IN : stream is ready for reading,
  • Gtk::IO_OUT : stream is ready for writing,
  • Gtk::IO_PRI : this is the high priority channel for stream
  • Gtk::IO_ERR : stream is in error state,
  • Gtk::IO_HUP : stream has been disconnected (sighup)
  • Gtk::IO_NVAL : (invalid command to socket ?)

You can build mask like this : $conditions = Gtk::IO_IN|Gtk::IO_OUT.

Callback parameters

function io_callback($gio_channel, $conditions)

  • $gio_channel is not usable with this current API implementation ; should be used with g_io_ functions not implemented. But IO can be done with standard php functions.
  • $conditions : this is effective conditions that triggered actual event call.

Source code example

<?php
error_reporting
(E_ALL);

/*
$conditions : can be a mask of :
  Gtk::IO_IN,  Gtk::IO_OUT, 
  Gtk::IO_PRI, Gtk::IO_ERR,
  Gtk::IO_HUP, Gtk::IO_NVAL
*/

/**
* Available syntaxes for io_add_watch:
*
$id = io_add_watch($stream, $condition, $callback);
$id = io_add_watch_priority($stream, $condition, $callback,
   $priority);
*
*/

class GtkIO {
  protected
$gio_ids;
  protected
$fd;

  protected
$hostname;
  protected
$port;

  protected
$errno;    # fsocketopen error status
 
protected $errstr;

  function
__construct() {
   
$this->gio_ids = array();
  }

  function
connect($hostname, $port) {
   
$this->hostname = $hostname;
   
$this->port = $port;
   
$this->fd = fsockopen($hostname, $port,
     
$this->errno, $this->errstr);
    if(!
$this->fd)
     
trigger_error("GtkIO::connect : connexion error : "
       
. "{$this->errstr} ({$this->errno})");
    else {
     
$this->io_setup();
    }
    return
$this->fd;
  }

  function
io_setup() {
   
$this->gio_ids[Gtk::IO_IN]   =
     
Gtk::io_add_watch($this->fd, Gtk::IO_IN,
        array(
$this, 'io_in'));
   
$this->gio_ids[Gtk::IO_OUT]  =
     
Gtk::io_add_watch($this->fd, Gtk::IO_OUT,  
        array(
$this, 'io_out'));
   
$this->gio_ids[Gtk::IO_HUP]  =
     
Gtk::io_add_watch($this->fd, Gtk::IO_HUP,  
        array(
$this, 'io_hup'));
   
$this->gio_ids[Gtk::IO_PRI]  =
     
Gtk::io_add_watch($this->fd, Gtk::IO_PRI,  
        array(
$this, 'io_pri'));
   
$this->gio_ids[Gtk::IO_ERR]  =
     
Gtk::io_add_watch($this->fd, Gtk::IO_ERR,  
        array(
$this, 'io_err'));
   
$this->gio_ids[Gtk::IO_NVAL] =
     
Gtk::io_add_watch($this->fd, Gtk::IO_NVAL
        array(
$this, 'ion_val'));
  }
 
  function
write($data) {
    return
fwrite($this->fd, $data, strlen($data));
  }
 
 
# very incomplete : need to handle errors ...
 
function read($size = 2048) {
    return
fread($this->fd, $size);
  }
 
  function
eof() {
    return
feof($this->fd);
  }
 
  function
io_in($gio_channel, $conditions) {
    echo
"GtkIO::io_in()\n";
  }

  function
io_out($gio_channel, $conditions) {
    echo
"GtkIO::io_out()\n";
  }

  function
io_hup($gio_channel, $conditions) {
    echo
"GtkIO::io_hup()\n";
  }

  function
io_pri($gio_channel, $conditions) {
    echo
"GtkIO::io_pri()\n";
  }

  function
io_err($gio_channel, $conditions) {
    echo
"GtkIO::io_err()\n";
  }
}

define('GTK_IRC_CONNECTED',       1);
define('GTK_IRC_DISCONNECTED',    2);

class
IrcProtocol extends GtkIO {
  protected
$nick;
  protected
$user;
  protected
$channels;

  protected
$timeout_id;
  protected
$irc_status;

  function
__construct() {
   
$this->timeout = 1000;
   
$this->timeout_id = Gtk::timeout_add($this->timeout,
      array(
$this, 'timeout'));
   
$this->irc_status = GTK_IRC_DISCONNECTED;
  }

  function
connect($hostname, $port,
   
$nick, $user, $channels) {
   
$status = parent::connect($hostname, $port);
    if (
$status === false)
      die(
"can't connect to irc server");

   
$this->irc_status = GTK_IRC_CONNECTED;
   
$this->nick = $nick;
   
$this->user = $user;

   
# initial channels to join;
   
$this->channels = $channels;

   
$this->nick($nick);
   
$this->user($user);

   
$this->join($channels);
  }

  function
send($str) {
    echo
"Irc::send($str)\n";
     
$this->write($str . "\n\r");
  }

  function
timeout() {
    static
$count = 0;
    echo
"timeout ...\n";
   
$count++;

   
# debug - for gtk_io function monitor
 
if ($count > 3) {
   
$this->send_chan('#php-gtk', "I'm going out; bye ...");
   
$this->disconnect();
    return;
    }

  if  (
$this->irc_status == GTK_IRC_CONNECTED
   
&& $this->fd != null)
   
$this->timeout_id = Gtk::timeout_add($this->timeout,
      array(
$this, 'timeout'));
   
$this->send_chan('#php-gtk', "hello ; I'm php-gtk-irc bot");
  }

  function
io_in($gio_channel, $conditions) {
   
$data = $this->read();
    echo
"$data\n";
  }

  function
io_hup($gio_channel, $conditions) {
    echo
"irc : disconnected\n";
   
$this->fd = null;
   
Gtk::main_quit();
  }

  function
disconnect() {
   
$this->send('QUIT');
   
$this->irc_status = GTK_IRC_DISCONNECTED;
   
Gtk::main_quit();
  }

  function
send_chan($chan, $msg) {
   
$this->send("PRIVMSG $chan :$msg");
  }

  function
nick($nick) {
   
$this->send("NICK $nick");
  }

  function
user($user) {
   
$this->send("USER $user $user $user localhost");
    }

  function
join($channels) {
    foreach(
$channels as $chan)
     
$this->send("JOIN $chan");
    }
  }

$irc = new IrcProtocol();

/**
* You'll need to change these parameters
* maybe to connect to chat.freenode.net
*/
$irc->connect(
 
$server   = 'irc.freenode.net',
 
$port     = '6667',
 
$nick     = 'mq-boot',
 
$user     = 'Marc'# put you name here ...
 
$channels = array('#php-gtk', '#php'));

Gtk::main();
?>

Notes :

  • Irc class is incomplete,
  • GtkIO::read() is very incomplete to.

Links

You can refer to thoses links for a complete documentation. Gtk::io_add_watch() is based on Glib function io_add_watch().

Note that, although the function is available on all platforms, this small example doesn't completely work on Windows.

Simulating wait loops

How to use a loop, for you might want to make an IRC client :)

I had trouble finding this out, so here it is. If you want to manage a network connection (i.e. an IRC connection) you need a while (1) {} loop.

This cannot be done in PHP-GTK, because of the gtk::main() function. So, there is the gtk::timeout_add($time, $function) that executes a function every $time milliseconds. This solves our problem.

Important

Your function must return TRUE else it will not be called again. Return FALSE to cancel the timeout.