// Load the GTK extension
dl('php_gtk.'. (((strtoupper(substr(PHP_OS,0,3))) == 'WIN')?'dll':'so'));
// Load the SOAP client class
require 'SOAP/Client.php';
// Create the main window and set its title and size
$window = &new GtkWindow();
$window->set_title('PHP Cookbook GTK Demo');
$window->set_default_size(500,200);
// The main layout container for the window is a VBox
$vbox = &new GtkVBox();
$window->add($vbox);
// Create a GtkAccelGroup to hold keyboard accelerators
$accelgroup = &new GtkAccelGroup();
$window->add_accel_group($accelgroup);
// Build the menu, starting with the GtkMenuBar. The arguments to
// pack_start() prevent the menu bar from expanding if the window does.
$menubar = &new GtkMenuBar();
$vbox->pack_start($menubar, false, false);
// Create the "File" menu and its keyboard accelerator
$menu_file_item = &new GtkMenuItem('_File');
$menu_file_item_label = $menu_file_item->child;
$menu_file_item->add_accelerator('activate',$accelgroup,
$menu_file_item_label->parse_uline('_File'),
GDK_MOD1_MASK,0);
// Add the "File" menu to the menu bar
$menubar->add($menu_file_item);
// Create the submenu for the options under "File"
$menu_file_submenu = &new GtkMenu();
$menu_file_item->set_submenu($menu_file_submenu);
// Create the "Quit" option under "File" and its accelerator
// GDK_MOD1_MASK means that the accelerator is Alt-Q, not Q
// GTK_ACCEL_VISIBLE means that the accelerator is displayed in the menu
$menu_file_choices_quit = &new GtkMenuItem('_Quit');
$menu_file_choices_quit_label = $menu_file_choices_quit->child;
$menu_file_choices_quit->add_accelerator('activate',$accelgroup,
$menu_file_choices_quit_label->parse_uline('_Quit'),GDK_MOD1_MASK,
GTK_ACCEL_VISIBLE);
// Add the "File | Quit" option to the "File" submenu
$menu_file_submenu->append($menu_file_choices_quit);
// Create the "Help" menu and its keyboard accelerator
$menu_help_item = &new GtkMenuItem('_Help');
$menu_help_item_label = $menu_help_item->child;
$menu_help_item->add_accelerator('activate',$accelgroup,
$menu_help_item_label->parse_uline('_Help'),
GDK_MOD1_MASK,0);
// Add the "Help" menu to the menu bar
$menubar->add($menu_help_item);
// Create the submenu for the options under "Help"
$menu_help_submenu = &new GtkMenu();
$menu_help_item->set_submenu($menu_help_submenu);
// Create the "About" option under "Help" and its accelerator
$menu_help_choices_about = &new GtkMenuItem('_About');
$menu_help_choices_about_label = $menu_help_choices_about->child;
$menu_help_choices_about->add_accelerator('activate',$accelgroup,
$menu_help_choices_about_label->parse_uline('_About'),GDK_MOD1_MASK,
GTK_ACCEL_VISIBLE);
// Add the "Help | About" option to the "Help" submenu
$menu_help_submenu->append($menu_help_choices_about);
// Layout the weather station searching widgets in a GtkTable
$table_1 = &new GtkTable(2,4);
$vbox->pack_start($table_1);
// Put a label on the left in the first row
$label_sn = &new GtkLabel('Station Name: ');
$label_sn->set_alignment(1,0.5);
$table_1->attach($label_sn,0,1,0,1, GTK_FILL);
// Put a text entry field in the middle of the first row
// The accelerator allows you to hit "Return" in the field to submit
$entry_sn = &new GtkEntry();
$entry_sn->add_accelerator('activate',$accelgroup,GDK_KEY_Return,0,0);
$table_1->attach($entry_sn,1,2,0,1, GTK_FILL);
// Put a scrolled window in the second row of the table
$scrolledwindow_1 = &new GtkScrolledWindow();
$scrolledwindow_1->set_policy(GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
$table_1->attach($scrolledwindow_1,0,4,1,2, GTK_EXPAND | GTK_SHRINK | GTK_FILL,
GTK_EXPAND | GTK_SHRINK | GTK_FILL);
// Put a columned list in the scrolled window. By putting the list inside
// the scrolled window instead of directly in the GtkTable, the window doesn't
// have to grow to a huge size to let you see everything in the list
$clist_sn = &new GtkCList(4,array('Code','Name','Region','Country'));
$scrolledwindow_1->add($clist_sn);
// Set the columns in the list to resize automatically
for ($i = 0; $i < 4; $i++) { $clist_sn->set_column_auto_resize($i,true); }
// Add a "Search" button to the first row
$button_search =&new GtkButton('Search');
$table_1->attach($button_search,2,3,0,1, GTK_FILL);
// Add an "Add" button to the first row
$button_add = &new GtkButton('Add');
$table_1->attach($button_add,3,4,0,1, GTK_FILL);
// Layout the weather conditions display widgets in another GtkTable
$table_2 = &new GtkTable(2,3);
$vbox->pack_start($table_2);
// Add a label displaying how many stations are shown
$label_st = &new GtkLabel('Stations: 0');
$label_st->set_alignment(0,0.5);
$table_2->attach($label_st,0,1,0,1, GTK_FILL);
// Add a button to update a single station
$button_update_sel = &new GtkButton('Update Selected');
$table_2->attach($button_update_sel,1,2,0,1, GTK_FILL);
// Add a button to update all stations
$button_update_all = &new GtkButton('Update All');
$table_2->attach($button_update_all,2,3,0,1, GTK_FILL);
// Add a columned list to hold the weather conditions at the stations
// This columned list also goes inside a scrolled window
$scrolledwindow_2 = &new GtkScrolledWindow();
$scrolledwindow_2->set_policy(GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
$table_2->attach($scrolledwindow_2,0,3,1,2, GTK_EXPAND | GTK_SHRINK | GTK_FILL,
GTK_EXPAND | GTK_SHRINK | GTK_FILL);
$clist_st = &new GtkCList(5,array('Code','Temp','Precipitation','Wind','Updated'));
$scrolledwindow_2->add($clist_st);
// Set the columns in the list to resize automatically
for ($i = 0; $i < 5; $i++) { $clist_st->set_column_auto_resize($i,true); }
// Connect signals to callbacks
// Clicking on the "Search" button or hitting Return in the text entry field
// searches for weather stations whose name match the entered text
$button_search->connect('clicked','wx_searchByName',$entry_sn,$clist_sn,$window);
$entry_sn->connect('activate','wx_searchByName',$entry_sn,$clist_sn,$window);
// Clicking on the "Add" button adds the weather station to the bottom
// columned list
$button_add->connect('clicked','cb_add_station',$clist_sn,$clist_st,$label_st);
// Clicking on the "Update Selected" button updates the bottom columned list
// for a single station
$button_update_sel->connect('clicked','wx_update_report',$clist_st,$label_st,
'selected');
// Clicking on the "Update All" button updates all stations in the bottom
// columned list
$button_update_all->connect('clicked','wx_update_report',$clist_st,$label_st,
'all');
// Closing the window or selecting the "File | Quit" menu item exits the program
$window->connect('destroy','cb_shutdown');
$menu_file_choices_quit->connect('activate','cb_shutdown');
// Selecting the "Help | About" menu item shows an about box
$menu_help_choices_about->connect('activate','cb_about_box',$window);
// These callbacks keep track of the currently selected row (if any)
// in each columned list
$clist_sn->connect('select-row','cb_clist_select_row');
$clist_sn->connect('unselect-row','cb_clist_unselect_row');
$clist_st->connect('select-row','cb_clist_select_row');
$clist_st->connect('unselect-row','cb_clist_unselect_row');
// The interface has been set up and the signals we want to pay attention
// to have been connected to callbacks. Time to display the window and start
// the GTK signal handling loop.
$window->show_all();
gtk::main();
/*
* CALLBACKS AND OTHER SUPPORT FUNCTIONS
*/
// use the searchByName() function over SOAP to get a list of stations
// whose names match the given search term
function wx_searchByName($button,$entry,$clist,$window) {
// instantiate a new SOAP client
$sc = new SOAP_Client('http://live.capescience.com/ccx/GlobalWeather');
$search_term = trim($entry->get_text());
if ($search_term) {
// call the remote function if a search term is provided
$res = $sc->call('searchByName',
array(new SOAP_Value('name','string',$search_term)),
'capeconnect:GlobalWeather:StationInfo',
'capeconnect:GlobalWeather:StationInfo#searchByName');
// pop up an error dialog if the SOAP function fails
if (PEAR::isError($res)) {
error_dialog($res->getMessage(),$window);
return false;
}
// pop up an error dialog if there are no matches
if (! is_array($res)) {
error_dialog('No weather stations found.',$window);
return false;
}
// add each station and its info to the columned list
// wrapping the calls to append() with freeze() and thaw()
// make all of the data appear at once
$clist->freeze();
$clist->clear();
foreach ($res as $station) {
$clist->append(array($station->icao,$station->name,
$station->region,$station->country));
}
$clist->thaw();
}
}
// use the getWeatherReport function over SOAP to get the weather conditions
// at a particular station
function wx_getWeatherReport($code) {
$sc = new SOAP_Client('http://live.capescience.com/ccx/GlobalWeather');
$res = $sc->call('getWeatherReport',
array(new SOAP_Value('code','string',$code)),
'capeconnect:GlobalWeather:GlobalWeather',
'capeconnect:GlobalWeather:GlobalWeather#getWeatherReport');
if (PEAR::isError($res)) {
error_dialog($res->getMessage());
return false;
} else {
return $res;
}
}
// add the weather report in $res to the columned list $clist
// if $row is null, the report is appended to the list
// if $row is not null, the report replaces row $row in the list
function wx_add_report($clist,$label,$res,$row = null) {
// format the timestamp
$timestamp = str_replace('T',' ',$res->timestamp);
$timestamp = str_replace('Z',' GMT',$timestamp);
$timestamp = strftime('%H:%M:%S %m/%d/%Y',strtotime($timestamp));
// format the wind information
$wind = sprintf("%.2f m/s from %s",
$res->wind->prevailing_speed,
$res->wind->prevailing_direction->compass);
$clist->freeze();
if (! is_null($row)) {
// replace the information in row number $row
$clist->set_text($row,1,$res->temperature->string);
$clist->set_text($row,2,$res->precipitation->string);
$clist->set_text($row,3,$wind);
$clist->set_text($row,4,$timestamp);
} else {
// add the information to the end of the columned list
$clist->append(array($res->station->icao,
$res->temperature->string,
$res->precipitation->string,
$wind,
$timestamp));
// update the columned list's internal row count
$rows = 1 + $clist->get_data('rows');
$clist->set_data('rows',$rows);
// update the label that displays a station count
$label->set_text("Stations: $rows");
}
$clist->thaw();
}
// update conditions for one station or all stations, depending on $mode
function wx_update_report($button,$clist,$label,$mode) {
switch ($mode) {
case 'selected':
// if there is a row selected
$selected_row = $clist->get_data('selected_row');
if (($selected_row >= 0) && (! is_null($selected_row))) {
$code = $clist->get_text($selected_row,0);
// get the report and update the columned list
if ($res = wx_getWeatherReport($code)) {
wx_add_report($clist,$label,$res,$selected_row);
}
}
break;
case 'all':
// for each row in the columned list
for ($i = 0, $j = $clist->get_data('rows'); $i < $j; $i++) {
// get the report and update the list
if ($res = wx_getWeatherReport($clist->get_text($i,0))) {
wx_add_report($clist,$label,$res,$i);
}
}
break;
}
}
// add a station to the bottom list of weather reports
function cb_add_station($button,$clist,$clist_2,$label) {
$selected_row = $clist->get_data('selected_row');
// if there's a selected row in the top list of stations
if ($selected_row >= 0) {
$code = $clist->get_text($selected_row,0);
// get the weather report for that station
if ($res = wx_getWeatherReport($code)) {
// find the row if this code is already in the list
$row = null;
for ($i = 0, $j = $clist_2->get_data('rows'); $i < $j; $i++) {
if ($clist_2->get_text($i,0) == $code) {
$row = $i;
}
}
// add the station and its report to the bottom list of
// reports (or update the existing row)
wx_add_report($clist_2,$label,$res,$row);
}
}
}
// update a columned list's internal selected row value when a row is selected
function cb_clist_select_row($clist,$row,$col,$e) {
$clist->set_data('selected_row',$row);
}
// clear a columned list's internal selected row value when a row is unselected
function cb_clist_unselect_row($clist) {
$clist->set_data('selected_row',-1);
}
// display the "About Box"
function cb_about_box($menu_item,$window) {
$about_box = &new GtkDialog();
$vbox = $about_box->vbox;
$action_area = $about_box->action_area;
$about_box->set_title('About');
$label = &new GtkLabel("This is the PHP Cookbook PHP-GTK Demo.");
$button = &new GtkButton('OK');
$button->connect('clicked','cb_dialog_destroy',$about_box);
$vbox->pack_start($label);
$action_area->pack_start($button);
$about_box->set_modal(true);
$about_box->set_transient_for($window);
$about_box->show_all();
}
// display an error dialog box
function error_dialog($msg,$window) {
$dialog = &new GtkDialog();
$vbox = $dialog->vbox;
$action_area = $dialog->action_area;
$dialog->set_title('Error');
$label = &new GtkLabel("Error: $msg");
$button = &new GtkButton('OK');
$button->connect('clicked','cb_dialog_destroy',$dialog);
$vbox->pack_start($label);
$action_area->pack_start($button);
$dialog->set_modal(true);
$dialog->set_transient_for($window);
$dialog->show_all();
}
// close a dialog box
function cb_dialog_destroy($button,$dialog) {
$dialog->destroy();
}
// quit the main program
function cb_shutdown() { gtk::main_quit(); }