Ruby/Tk Primer, Part 2

This is the second article in a multi-part series in which we use Ruby and
the Tk toolkit to develop a GUI front end for the cron task scheduler. In the
first
article, we learned the basics of programming in Ruby, and in the process
we created the back end for the GUI we're working on. If you have no prior experience
in Ruby, or if you need a refresher, please go back and read over the first
tutorial. Otherwise, if you just need a copy of the code from the previous article,
you can find it here.

Mac OS X Panther comes with a version of Ruby already installed and ready to go. However, the version of Ruby that Panther ships with is a little out of date and needs to be updated before we can proceed. We'll also need to install the Tk toolkit on our system. This site gives you detailed instructions on how to download and install both Ruby 1.8.1 and Tcl/Tk Aqua (Batteries-Included) 8.4.5. Once you get both of these installed, you'll be ready to start the tutorial. So, with your computer set up and ready to go, let's proceed.

What is Tk?

Tk is a GUI library originally created by Dr. John Ousterhout while at the University of California in Berkley as an extension to his Tcl scripting language. The idea was to create a library of common graphical components that would make the creation of complex GUIs simpler, thus allowing the developer to concentrate his/her time on solving the more interesting problems presented by each unique application.

Tk has many advantages that make it a useful GUI library to learn. Among these are its ease of use, language independence, and near infinite portability. Since its inception as an extension to the Tcl scripting language, Tk has been ported to nearly every platform imaginable and has been developed for use with several popular scripting languages, such as Perl and Ruby. Learning Tk gives you instant reusability since you can run your Tk-based applications on nearly any operating system, and since Ruby has also been ported to most platforms, the combination of the two means that you'll be able to run your applications on other platforms with little to no modification.

Convinced that the combination of Ruby and Tk is an important addition to your programming arsenal? Great! Let's get down to it and start learning how to use the Tk toolkit.

Tk Concepts

Tk is a library for creating graphical user interfaces. The basic component of a Tk-based application is called a widget. (A component is also sometimes called a window, since, in Tk, "window" and "widget" are often used interchangeably.) Tk applications follow a widget hierarchy where any number of widgets may be placed within another widget, and those widgets within another widget, ad infinitum. The main widget in a Tk program is referred to as the root widget and can be created by making a new instance of the TkRoot class.

Most Tk-based applications follow the same cycle: create the widgets, place them in the interface, and finally, bind the events associated with each widget to a method. Our application is very simple, and thus, most of the callback methods we bind to our widgets can be done during their creation by setting the command option. As for placing the widgets into the application, this will be done through the use of geometry managers.

In Tk, there are three geometry managers (place, grid, and pack) that are responsible for controlling the size and location of each of the widgets in the interface. In fact, a widgets is not viewable until a geometry manager has knowledge of it. A common mistake made in Tk programming is neglecting to notify a geometry manager of the existence of a widget.

If you have experience with programming GUIs in Java, then you are already acquainted with the way that geometry managers work. Basically, each geometry manager provides a set of rules used in placing and sizing widgets within a container. Several containers are placed inside of each other in order to form more complicated layouts.

One of the things that Tk makes very easy is in interacting with the widgets that make up an application. Many times, all it takes to set up an event handler is to create a method and set the widget's command option to that method during the widget's creation. Even if we need to handle a less-common event, it can be done by simply calling the bind method and passing into it the event we want to listen for and the method that will handle the event.

Getting and receiving the data in widgets such as the TkEntry widget (Tk's version of a text field) is just as easy. To get and set the data within a widget, we simply create a TkVariable object and pass it into the widget at creation. We can then alter the data in a widget and get the data from a widget by interacting with that variable.

These features make Tk a very good library to use for rapid prototyping and for constructing GUIs for commonly used system commands and scripts, such as we are doing in this lesson.

Now that we know the basics of creating a Tk-based application, we need to learn some of the details of the GUI library. The next section provides an overview of the widgets used in our program.

An Overview of the Tk Widgets Used in Our Program

Anyone wanting a good overview of what the Tk library has to offer will find our chosen application is a perfect introduction. It uses a rich subset of the Tk library. During the course of this lesson, we'll cover most of the usual suspects such as buttons, text fields, drop-down lists, and list boxes. We will also cover some of the nice ready-made features that Tk provides, such as the file chooser dialog and a few others.

The list below has summaries of each of the widgets used in this application. Feel free to read through it now, or if you're the impatient type, go ahead onto the next section, where we jump into the code for the GUI, and come back to this one as needed.

TkFrame: Simply a container widget. Its main purpose is to facilitate the creation of complex layouts. The frame widget has only two features: its background and border.

TkRoot: A special type of TkFrame. Every Tk-based application should begin with the creation of this widget and every widget within the application should be able to follow its widget hierarchy back up to an instance of TkRoot.

TkLabel: A widget that displays a text string, bitmap, or image. It is commonly used in forms to describe the fields within the form.

TkButton: A common button widget that can display text, bitmaps, or images. When clicked, the TkButton object will call the method associated with its command option.

TkEntry: This object displays a one-line text string that can be manipulated by the user. This widget is similar to what other languages refer to as a text field.

TkOptionMenubutton: This class is Tk's version of the common drop-down list.

TkListbox: A widget that displays a list of strings (one per line). It's usually used with the TkScrollbar widget to give the user the ability to view items in the list if the number of items exceeds the viewable limit of the listbox.

TkScrollbar: A common scrollbar that can be either vertical or horizontal. It is used with other widgets, such as the TkListbox widget, when scrolling capabilities are needed. In order for the scrollbar object to work, it must be registered with the widget it is to control.

There are a few things you should notice after reading through the list of widgets above. First, all of the widgets in the list begin with "Tk." This is pretty standard for the Ruby version of the Tk library. If you want to try creating more complicated GUIs with Tk, you'll need to find a good reference, and frankly as far as Tk for Ruby is concerned there really aren't any. So, the next best option is to find a good Tcl/Tk reference like this one.

You can create just about any widget in this reference simply by prefacing its name with "Tk" and calling its new method. As for the options available for each widget, you should be able to use any of those listed in the reference. But in Ruby/Tk, they are set by providing a code block to the widget's new method with each of the options listed as methods, and the attributes to those options listed as arguments to the methods. Or you could pass them into the new method as a Ruby hash.

Geometry Managers

If you've used the layout managers that Java provides, you're already familiar with the way that Tk manages the layout of its widgets. Tk uses geometry managers to control the sizing and location of the widgets in its applications. Widgets such as TkFrame can be used to create complex layouts by stacking them several levels deep within each other and using a few simple geometry managers. In this application we will make use of two of the three geometry managers provided by the Tk library -- the packer and the grid.

The packer geometry manager organizes GUI elements by packing them into the parent widget in order from the side specified. It's very similar to Java's FlowLayout layout manager. The grid geometry manager allows us a little more control over where we place our widgets. Working with the grid manager is very much like working with HTML tables, in that the grid forms a table and you determine where a widget resides by specifying its column and row.

You can also perform other layout tricks that a normal HTML table would let you do, such as naming how many columns your widget should span. The third and final geometry manager (which is not used in this article) is the place geometry manager. The place manager allows a user to specify the location and size of the widget, either absolutely or relatively. The place manager is usually only used in situations where you a need a little more control than what the grid and packer managers can provide, or when you need to create a custom geometry manager.

Next Time

The table is set. You have a good understanding of what the Tk library can do. In the next installment, I'll show you a simple Ruby/Tk application. Then we'll wrap up by returning to our cron application and finishing it with this new knowledge. See you in a few days!

Christopher Roach
recently graduated with a master's in computer science and currently works in Florida as a software engineer at a government communications corporation.