With PHP’s younger cousin PHP-GTK’s recent step to maturity with the 2.0 stable release it is a good time to give this project some more attention. In this article I will show you how to create a re-usable IPv4 Entry widget using PHP-GTK’s excellent OO structure.
For the people who don’t know what I’ve been talking about in the first paragraph please visit the PHP-GTK site (http://gtk.php.net).
In short:
“PHP-GTK is a PHP extension allowing you to build portable GUI applications for Windows, Linux, and Mac with the ease you’ve come to expect from PHP”
What we are creating today is an IPv4 Entry widget so users can enter their IP address in a way that is similar to the configuration screen found in most current operating systems.
Creating the widget class
What we want to do is create a widget that we can include in our project and instantiate it without the need to rely on other classes. We include a get_text() and a set_text() method so we can retrieve and/or set the necessary information.
The trickiest part of this widget is that we want the cursor to skip to the next subsection of the IP address when we entered a value that demands this (i.e. a value higher then 255). Also we want to block non-numerical values since these have no business in an IPv4 address. Finally we want the cursor to change to the next input field when the dot (.) or the TAB is pressed.
To the code
First of all we subclass GtkFrame to create our own IPv4Entry class. I chose GtkFrame because under the hood this widget is actually a container that holds 4 GtkEntry widgets and 3 GtkLabels (the dots that split the IP address).
Our class needs two attributes. First of all it needs an array to hold our seperate GtkEntry fields and secondly it needs an attribute to keep track of the entry field we are currently in.
In the constructor we create a GtkHBox to hold our widgets and fill this with GtkEntry widgets separated by GtkLabels (the infamous dots). This is where the magic starts. We connect the key-press-event and the key-release-event events to each of the GtkEntry widgets. What this does is that each time a user presses or releases a key while the GtkEntry widget has focus it calls the function we associated with the event.
In these events (onButtonUp and onButtonDown) we check what key the user pressed and act accordingly.
To wrap things up we add the set_text() and get_text() methods that do exactly what they say they do. I named these methods as set_text() and get_text() because that is the way the GtkEntry widget works. Since we are creating another type of entry widget it is appropriate to keep the naming convention.
Beautiful, what’s next?
As you can see creating your own widgets by extending or using existing widgets is easy. A commenter over at www.php-gtk.eu pointed out that the logic I used to keep non-numeric input out of the GtkEntry fields can also be contained within a separate widget that subclasses GtkEntry so we do not need to handle this in the main widget.
It is up to you now to go out and write your own widgets. Good luck!
Posted below is the entire code for the IPv4 widget with associated comments / PHPDoc.
<?php
/**
* IPv4 Entry widget
*
* IPEntry performs some checks to see if userinput is valid and implements the
* get_text() and set_text() methods found in regular GtkEntry widgets for
* compatibility and ease of use.
*
* Small warning note: Needs to be fitted within a GtkAlignment because it will
* take up all available space otherwise.
*
* @author Robert van der Linde <robert@linde002.nl>
* @version 1.0
*/
class IPEntry extends GtkFrame
{
/**
* Contains the four GtkEntry fields.
*
* @var array
*/
protected $aEntries = array();
/**
* The GtkEntry currently selected
*
* @var int
*/
protected $iCurrentEntry = 0;
/**
* Creates a IPEntry widget
*
*/
public function __construct()
{
parent::__construct();
/**
* The container for all the GtkEntries
*/
$oHBox = new GtkHBox();
/**
* Create four GtkEntry fields
*/
for ($x=0; $x<4; $x++)
{
$this->aEntries[$x] = new GtkEntry();
/**
* Align the text in the middle of the GtkEntry. Looks pretty
*/
$this->aEntries[$x]->set_property ('xalign', 0.5);
$this->aEntries[$x]->set_property ('has-frame', 0);
$this->aEntries[$x]->set_size_request (30, -1);
/**
* Default to 0
*/
$this->aEntries[$x]->set_text('0');
/**
* Connect the callback signals used to perform input checking
*/
$this->aEntries[$x]->connect('key-press-event', array($this, 'onButtonDown'), $x);
$this->aEntries[$x]->connect('key-release-event', array($this, 'onButtonUp'), $x);
$oHBox->pack_start($this->aEntries[$x], 0);
/**
* If this is the last entry field don't add the dot.
*/
if($x != 3)
$oHBox->pack_start(new GtkLabel('.'), 0);
}
/**
* Pack it all in a pretty eventbox so we can set the general background color.
*/
$oEventBox = new GtkEventBox();
$oEventBox->add($oHBox);
$oEventBox->modify_bg(Gtk::STATE_NORMAL, GdkColor::parse("#ffffff"));
$this->add($oEventBox);
$this->set_shadow_type(Gtk::SHADOW_ETCHED_OUT);
}
/**
* Called when a user starts inputting a character. If that character is a dot
* we jump to the next entry field and suppress the dot.
*
* @param GtkEntry $oEntry
* @param GdkEvent $oEvent
* @param int $iEntryid
* @return boolean
*/
public function onButtonDown(GtkEntry $oEntry, GdkEvent $oEvent, $iEntryid)
{
if(chr($oEvent->keyval) == '.')
{
$sTxt = $oEntry->get_text();
if($iEntryid == 3)
$this->aEntries[0]->grab_focus();
else
$this->aEntries[$iEntryid+1]->grab_focus();
return true;
}
else
return false;
}
/**
* Called when a user finishes inputting a character. If the character is
* the third digit or by inputting the character makes the value higher then 25
* jump to the next inputfield.
*
* @since 1.0
* If this inputfield is selected by way of using TAB or dot we do not check the
* value of the input fields. This is a workaround to stop the widget from entering
* a possible infinite loop when all input fields are > 25
*
* @param GtkEntry $oEntry
* @param GdkEvent $oEvent
* @param int $iEntryid
*/
public function onButtonUp(GtkEntry $oEntry, GdkEvent $oEvent, $iEntryid)
{
if($oEvent->keyval != Gdk::KEY_Tab && chr($oEvent->keyval) != '.')
{
if(strlen(trim($oEntry->get_text())) == 3 || trim($oEntry->get_text()) > 25)
{
if($iEntryid == 3)
$this->aEntries[0]->grab_focus();
else
$this->aEntries[$iEntryid+1]->grab_focus();
}
/**
* If the inputfield has a value higher then 255 reset it to 255
*/
if(trim($oEntry->get_text()) > 255)
$oEntry->set_text('255');
}
}
/**
* Implementation of the GtkEntry::set_text() function for usability.
* Checks if the string is a valid ip address (does it's best to create
* one if it received an invalid string)
*
* When it fails to do so defaults to 0.0.0.0
*
* @param string $sIP
*/
public function set_text($sIP)
{
$sCleanIP = long2ip(ip2long($sIP));
$aIPSections = explode('.', $sCleanIP);
for($x=0; $x<4; $x++)
$this->aEntries[$x]->set_text($aIPSections[$x]);
}
/**
* Implementation of the GtkEntry::get_text() function for usability.
* Returns a concatenation of the GtkEntry fields and dots.
*
* @return string
*/
public function get_text()
{
$sIP = '';
for($x=0; $x<4; $x++)
{
$sIP .= trim($this->aEntries[$x]->get_text());
if($x != 3)
$sIP .= ".";
}
return $sIP;
}
}
?>




August 18, 2008
Tutorials