Ruby TK – Events, Timers

Widget Events

Existing widgets will change as a result of an event, such as mouse-clicks, cursor movements and user input. When an event is fired, a callback function is invoked.

To bind an event to a widget, you would use the bind function of the widget using the function:


bind <event>, proc {command}

For example:

bind "1", proc {puts "Hello, world"}

Time Events

There is another sort of event to be used when a duration of time passes. To bind such an event, you should use a Timer object defined in ‘tk/timer’.

The syntax for creation of a timer event is:

my_timer = Timer.new(duration, nTimes, proc {command})

To start the timer, use the method ‘start’ as follows:
my_timer.start
Timer object start as the method ‘start’ is invoked. Commands following the timer start are performed simultaneously with the command passed to the object. If you don’t want other actions taking place while the timer perform its task, you can use the method:

my_timer.wait

You can see an example of using two timers in the following code from the function ‘show_solution”  in my Klotski puzzle solver

  my_timer=TkTimer.new(1, 1500, proc {move_piece 'move'=>[3,1,1,0]})
  my_timer.start
  my_timer.wait
  my_timer=TkTimer.new(1, 1000, proc {move_piece 'move'=>[3,1,0,1]})
  my_timer.start
  my_timer.wait

The code above getw the big square out of the frame. The square moves down 3 postions, and the 2 positions to the right.

The full can be downloaded from here.

Advertisement

Ruby TK – The Canvas

If you want to use Ruby TK for painting – and not just displaying pictures from the disk – you can use the widget TkCanvas. To this widget you can add lines, polygons, elipses and circles etc.

Now, to add a shape to a canvas in Perl you would use the syntax:

$canvas->createLine(x1, y1..., xn, yn, ?option, value, option, value, ...?)

In Ruby:
line=TkcLine.new(canvas,x1, y1..., xn, yn, ?option=> value, option=>value, ...?)

x1,y1,…,xn,yn are the coordinates of the shape. They can be changed in response to events.
It also has options such as ‘fill’, the color with which to fill the shape.
The options can be changed in response to events.

What Are Coordinates

Every shape has its coordinates, points that define the shape. You can get or configure the shape’s coordinates using the function ‘coords’.

TkcLine – points along the shape consisting of one or more lines. Each point is where the previous line ends and the next starts.

TkcPolygom – Like TkcLine, but with a line connecting the last point to the first.

TkcRectangle – x1,y1,x2,y2 are the coordinates of two opposite vertexes of a horizontal or vertical rectangle.

TkcOval – x1,y1,x2,y2 of the rectangle enclosing the ellipse or circle.

TkcArc – like TkcOval, but in addition has the options ‘start’ and ‘extent’. ‘start’ is the number of degrees from the 3 o’clock position of the oval. ‘extent’ is the size of the angular range occupied by the arc counterclockwise from the arc’s beginning point.

Learn more at http://perlhelp.web.cern.ch/PerlHelp/ (search for tk/canvas on the right side of the window) or feel free to ask your questions in a comment.

 

Ruby TK – Arranging Widgets With Grids

What would you do if you wanted your widgets arranged in a tabular structures? For example: a list of labels followed by input fields.

Well, you would divide your window and other widgets into grid, and would justify the contents of grids right, left, up, down, etc.

You can choose the location of your widget in its parent widget, by adding the ‘grid’ method in the block following the ‘new’ method.

For example:

  TkLabel.new(root) do
    background 'white'
    text 'username:'
    grid('row'=>8, 'column'=>0, 'sticky'=>'e', 'padx'=>5)
  end

Explanation:
'sticky'=>'e' means justifying right. e=east, w=west, ne=northeast, etc.
'padx' is the number of pixels following the elements. In the picture, you can see 5
      pixels between the text 'username' and the input field.

When you add a grid to a widget, don't use 'pack'!! This will confuse Tk,
and will result in a program that does not respond!

Creating Windows With Ruby Tk – Widgets

In the previous chapter, you saw how to create a simple window. This window is an object of class TkRoot, which is implicitly created when running a Tk application. You better create it explicitly, so you don’t have a default window with a default title. e.g.

root=TkRoot.new {title 'My Title'}

TkRoot is a singleton object, and its method ‘new’ does not instantiate an object, but returns an existing one. The method ‘new’ only reads the attached block and performs it to configure the default window. If you want similar windows, use ‘TkToplevel’.

Now, you can add widgets to your windows using

myWidget=<widgetname>.new(<parent>) {<block>}

When ‘widgetname’ is the widget’s class, the class name is ‘Tk’ followed by the widget name as defined in Active Perl.

‘parent’ is the parent widget in which you want to put your new widget. If parent is not specified, your widget will be added to the ‘TkRoot’ window.

‘block’ is a block of commands to be performed, mainly methods to configure your widget. For example

mywidget=TkFrame.new(root) {width 90; height 36; background 'blue'}

Because widgets are used for input and output we don’t just put it in windows. For example, a button should response to clicks, so we make it respond to the left click by:

button.bind '1', proc {puts 'button clicked!'}

Or, a common input text field is the widget TkEntry. To read the entered text, we use the expression:
entry.value

Something I don’t like is that to get the value from a TkCheckbutton widget, you use its get_value method` they can use some consistency.

To set a default value:
entry=TkEntry.new {insert 0,'some text'}

We can use TkEntry as a password field by invoking the method ‘show’:
entry=TkEntry.new {show '*'}

To learn more about widgets read ActivePerl.

Creating Windows With Ruby Tk

The Ruby language supports creating windows for GUI (Graphic User Interface). GUI applications are programs that respond to user input, which are usually mouse events, such as clicking buttons, clicking menus, etc.

A GUI application may consist of the following types of objects:

  • Widgets – basic GUI objects that can be put directly in the window. Most of them generate events as a response to user actions. A label is a widget, too, but, usually does not generate events.
  • Shapes – lines, arcs, circles, polygons and other that belong on a canvas.
  • Timers – threads that perform an action the number of times specified and sleep for the  specified duration. From the definition “thread” you can understand that they run in parallel.

The simplest Ruby Tk program is:


require 'tk'
Tk.mainloop

This program displays the following window:

This is the default window. It is displayed on the screen when the line ‘Tk.mainloop’ is performed. Until this window is closed, no commands that are not responses to events will be executed.

“Programing Ruby – The Pragmatic Programmers Guide” suggests that you look at Perl/Tk guides to learn how to use Tk. A good place to look for Perl’s objects and their methods is Active Perl. I’m not going to write here the complete guide to Ruby Tk, but I hope the following chapters will help you understand how it works.

To be continued.

JSON, The New XML

When you write for the web, you may want to send data to another server or to a client. A common way to transfer that data is in XML format. The data will then be processed using SAX, DOM or XPath. Every language support it.

If what you want is to define a variable, an object or an array in Javascript, you can use the JSON extension. JSON is an acronym for ‘Java Script Object Notation’. In Javascript you can use it as follows:

var myObject=<?php echo json_encode($php_object); >;

Here no parsers are required.

Here’s an example of using it in PHP:

<?
class my_class {
  public $prop1;
  public $prop2;

  function __construct(){
    $this->prop1='a';
    $this->prop2=400;
  }
}

$obj=new my_class();
echo json_encode($obj);
?>

The output looks like:
{“prop1″:”a”,”prop2″:400}

In addition to encoding, a JSON string can be decoded into an object in a language other than Javascript. Thus, you can pass data in the JSON format to any program supporting JSON, and, as you can see in www.json.org, most languages used today support it.

The ability to encode varibles into JSON and decode it back in any language is not the only reason why JSON can replace XML. If you go to www.json.org, you can see links in the bottom referring to other sites. For example, JSONPath, that allows you to access a member just like XPath. JSONPath is available in PHP and Javascript.

Making C Programmer-Friendly with GNU Libraries

Does anybody remember how to program in C? Well, its syntax is similar to that of languages such as Java, PHP and others, but C uses a data type named pointer, and an array is a pointer, too. When you define an array, the system allocates enough space for it, but when you use the array nobody keeps you from accessing elements out of that array. You will not get a “Subscript out of range” error with the file’s name and line number. What you’ll get instead is a segmentation fault or another variable’s value will change. Use a debugger to find where it happens.
The library GLib has functions that perform operations on data structures like in modern object oriented languages. For example:
Use GArray, and you can enjoy the function ‘g_array_sort’ that sorts using a user-defined comparison function. This structure also allows you to increase the size of your array without calling ‘realloc’ implicitly.
GSList and GList are the data types of singly-linked and doubly-linked respectively, and a ‘foreach’ functions are defined to perform the same operation on each of their elements.
Other data structures are hash-tables, balanced binary trees, N-ary trees, etc.

An interesting utility is the command line option parser, that will check the validity of values passed via the command line and pass the values to function and global variables defined by the user. In addition, this utility will define the ‘–help’ flag, which will print your program’s command line flags and their use.

Of course, this is not everything you can do with GLib, GLib also supports threads and processes, events, etc.

Read more here.

If you want to distribute products you developed using GLib, you might be interested in the license. GLib is distributed under the Lesser GPL license, which means that you can distribute proprietary and open-source software developed with this library.

 

ImageMagick Workaround for SVG Support

I have installed the PECL package IMagick in my computer. Its current version 7:6.6.2.6 -1 does a great job converting SVG files into raster images. In my hosting account the Imagemagick package is not the most recent: I have an SVG file that contained elemants repeated using the Inkscape’s spray can, but when I convert it using a test program in my host it only print one of the repeated stars.

How can I resolve this?

Well, as you probably know, SVG is an XML format, so I can read it using a text editor. Doing this I found that there’s an element named ‘use’ with an attribute named ‘href’. “href” we know from HTML, and the element’s named “use” makes sense when using an existing element. So I commented out all the “use” elements, and viewing the SVG file as an image I saw the star at the exact location as in the  erroneous raster image, but with other elements not printed there. Probably the SAX engine stopped after the first unknown element. Commented out all elements from the first “use” elements until the end of file, and BINGO! This reproduced the problem. Well, the “use” elements should be replaced by the original ones with changes to their attributes. The following code does it:

/**
* I found that the "use" tags are not supported, so I'm replacing them
* by something else.
*
* The function returns the content of the file, the returned value can be the 1st argument of 
* Imagick::readImageBlob
*/
function svg_workaround($filename){
  $mimeType = mime_content_type($filename);

  if ($mimeType != 'text/xml' && mimeType!='image/svg+xml'){
  // At home the mime type is "Image/svg+xml" in the hosting account "text/xml"
    return file_get_contents($filename);
  }

  $contents = file_get_contents($filename);
  $docRoot = new DOMDocument(); // For random access and fast location of referenced objects
                                                     // you would use 'DOM'.

  if (!$docRoot->loadXML($contents))
    return FALSE;

  $xlinkNs = getXLinkNs($docRoot); // For the "href" attribute.

  $svgElems = $docRoot->getElementsByTagName('svg');
  if (!$svgElems->length)
    return $contents;

  $nodeList = $docRoot->getElementsByTagName('use');

  // Because the node lists changes when elements are replaces, we keep the elements in an array.
  $nodes = array();
  for ($i=0; $ilength; $i++)
    $nodes[]=$nodeList->item($i);

  foreach ($nodes as $node)
    if (!replace_use_tag($node, $docRoot, $xlinkNs))
      return false;

  return $docRoot->saveXML();
}

// The function that replaces the "use" elements
function replace_use_tag($node, $doc, $xlinkNs){
  $href=$node->getAttribute($xlinkNs . ':href');
  if (!$href)
    $href=$node->getAttribute('href');

  if (!$href)
    return FALSE;

  $id=substr($href, 1); // Removing the '#' from the href.
  if (!$id)
    return FALSE;
  // We use 'xpath' to locate a node with a given id because, unlike HTML, the use of getElementById
  // in XML DOM is not trivial.
  $xpath=new DOMXPath($doc);
  $elems=$xpath->query('//*[@id=\'' . $id . '\']');
  $orig=$elems->item(0);

  if (!$orig)
    return FALSE;

  $newNode=$orig->cloneNode(TRUE); // TRUE for deep cloning; When cloning a composite such as "g",
                                                             // its child elements should be copied as well.

  // Copy attributes from the 'use' node to the new node, not including the 'href'
  $attributes=$node->attributes;

  $length=$attributes->length;

  for ($i=0; $iitem($i); $i++){
    $attrName=$item->name;
    $attrValue=$item->value;
    if ($attrName=='href' || $attrName==$xlinkNs . ':href')
      continue;
    $newNode->setAttribute($attrName, $attrValue);
  }

  $parent=$node->parentNode;
  $parent->replaceChild($newNode, $node);
  return TRUE;
}

// Get the namespace for XLink.
function getXLinkNs($doc){
  return $doc->lookupPrefix('http://www.w3.org/1999/xlink');
}

Extending PHP

Extending PHP does not mean just adding classes and functions. It also means adding functionality not previously supported by PHP. This can be done by writing functions in C that can be called from PHP. These functions should be able to receive parameters passed from PHP. The difference between a variable in a C source and a variable in PHP is that in PHP the variable in PHP is loosely typed. That is, in PHP a variable can be used as an integer, but later as a string or a floating point number, so its equivalent in C is zVal. “zval” is a structure containing the variable’s type and a union made up of members of different types sharing the same memory address.

The PHP extension is a dynamically linked library (‘dll’ in Windows, ‘so’ in Linux) containing functions that can be called from PHP.

The process of creating an extension is described in the chapter “PHP at the Core: A Hacker’s Guide to the Zend Engine” in the famous PHP manual.

Building the extension starts with tunning the ‘ext_skel’ script, which creates a directory for your extension including a skeleton of the extension’s C code and a header file.

The next step is to add functions and global variables using macros.

The macro used for defining a  function is PHP_FUNCTION(function_name). Returning a value is done using the macros RETURN_TRUE, RETURN_FALSE, RETVAL_* . These macros are in /path/to/php_include_dir/Zend/zend_API.h

Arguments are passed to local C variables using the function ‘zend_parse_parameters’.

The next step is to edit config.w4(Linux) or config.w32(windows), then run ‘phpize’ and ‘configure’ to create a Makefile.

Finally, run make.

The dynamically loaded library will be created in the ‘modules’ directory. Use ‘make install’ with root permissions to copy your extension to the PHP extension directory.

Unfortunately, the guide is far from being complete, so to look for examples, browse ‘pecl.php.net‘ for source codes.