RubyGTK

 
   

Ruby Way
By Hal Fulton
Slots : 1.0
Table of Contents
 


Ruby/GTK

The GTK+ library is a byproduct of the GIMP (the GNU Image Manipulation Program); the name actually means the GIMP Toolkit. Like BSD and LSD, GTK+ comes to us from the University of California at Berkeley.

For those familiar with X/Motif, GTK+ has a similar look and feel but is more lightweight. GTK+ originates in the Unix world and forms the underlying basis for GNOME (increasingly familiar to Linux users), but it is relatively cross-platform. There is an ongoing port to the Windows family of operating systems, although at the time of this writing it is not completely stable.

Because GTK+ is relatively new technology, it is arguably easier to use than some older, less object-oriented systems. It also offers a reasonably intuitive paradigm for programming and a comfortable rich set of widgets. However, these assessments are highly subjective, and we'll leave the final judgment to you, the reader.

Overview

Ruby/GTK is a library that allows Ruby applications to use the GTK+ library. GTK+ is open source and is released under the GNU LGPL license, so it may be used freely in commercial applications.

Like most GUI toolkits, GTK+ has such concepts as frames, windows, dialog boxes, and layout managers. It has a rich set of widgets and includes all the most basic ones, such as labels, buttons, and text edit boxes, as well as advanced widgets, such as tree controls and multicolumn lists.

Although GTK+ was written in C, it was designed with a strong object-oriented flavor. Ruby/GTK thus presents a very clean, object-oriented API, while also staying very close to the underlying C language. This enables Ruby/GTK developers to take advantage of the large number of "native C" GTK+ tutorials and reference documents available.

GTK+ is actually built on top of a library named GDK. In Ruby/GTK, this means that some of the lower-level graphical objects are in the Gdk module, rather than the Gtk module. The native GTK+ documentation makes it clear which objects are in each library. Native GTK+ also includes a library named GLIB, which contains an assortment of nongraphical classes that handle items such as strings and lists. Because Ruby already has nice string and list classes, Ruby/GTK programs do not have to deal with GLIB concepts much.

At the time of this writing, Ruby/GTK is at version 0.25 and is compatible with the current stable versions of Ruby and GTK+ (1.2). However, Ruby/GTK is not yet at version 1 and is described by the author as "usable, but not completed." Furthermore, the MS Windows port of GTK+ is "not really targeted at end-users yet." So although simple Ruby/GTK applications seem to work fine under MS Windows, it's probably not the best choice for projects that require Windows compatibility.

GTK+ is very object oriented and has a logical widget hierarchy. The concepts of Bin and Container are powerful, and the combination of Box and Table layout managers is simple yet flexible. The Ruby/GTK mechanism for setting up signal handlers is extremely convenient.

Some of the GTK+ widgets include menus, toolbars, tooltips, trees, progress bars, sliders, and calendars. However, one current weakness of GTK+ is that it does not yet provide a good selection of standard dialog boxes, and it is difficult to set them up modally. In addition, the standard multiline text editor widget has some weaknesses.

A Trivial Windowed Application

Any program using Ruby/GTK must do a require of the GTK library. Ruby/GTK provides its functionality through the Gtk and Gdk modules, meaning that GTK+ classes are typically prefixed with Gtk:: (or Gdk::).

Normally we create a top-level window and a handler for the destroy signal (which results when a window is closed by the user). A call to show_all makes the window (and its children) visible, and a call to Gtk.Main initiates the event loop.

We'll expand on this a little after looking at an example. Here is a code fragment similar to the one for Tk that displays the current date:

 

 require "gtk" mainWindow = Gtk::Window.new mainWindow.signal_connect("destroy") {  Gtk::main_quit } str = Time.now.strftime("Today is \n%B %d, %Y") mainWindow.add(Gtk::Label.new(str)) mainWindow.set_default_size(200, 100) mainWindow.show_all Gtk::main 

The main window (of type Gtk::Window) is created as a "top-level" window. Top-level windows have a standard title bar and generally behave as you would expect the main window of an application to behave.

Next, a handler is created for the destroy signal, which is generated after the main window is closed. This handler (here, a single block) simply exits the main event loop. The GTK+ documentation lists all the signals that each widget might receive. (Be sure to look at superclasses, too). These are typically triggered by mouse or keyboard input, timers, changes in window state, and so on.

The next line of code adds a text label widget directly to the main window. The default size of the label will be calculated automatically based on the size of the text.

By default, GTK+ parent widgets are automatically sized according to the sizes of their children. In this case, the size of the string in the default font will determine the size of the label widget, and the main window would become just large enough to hold the label. That's pretty small, so set_default_size is used to indicate that the initial size of the main window is 200 pixels wide and 100 pixels tall.

After that, show_all is used to make the main window and all its children visible. By default, the main window is hidden, so it is necessary to invoke this method for the main window of most applications.

The call to Gtk::main starts the GTK+ event loop. This method will not return until GTK+ is terminated. In this application, the destroy event handler will cause Gtk::main to exit, at which point the app will terminate.

Working with Buttons

To create a pushbutton in Ruby/GTK, we define it using the Button class. In the simple case, we set up a handler for the clicked event that is generated when a user clicks the button.

This code fragment shown in Listing 6.5 will accept a simple line of text in a text-entry field and (when the button is clicked) will convert the string to uppercase. Figure 6.4 shows a screenshot of this example.

Figure 6.4. A simple GTK pushbutton example.

graphics/06fig04.gif


Listing 6.5 GTK Buttons
 require "gtk"     class SampleWindow < Gtk::Window       def initialize         super         set_title("Ruby/GTK Sample")         signal_connect("destroy") {  Gtk::main_quit }         entry = Gtk::Entry.new         button = Gtk::Button.new("All Caps!")         button.signal_connect("clicked") {  cmdAllCaps(entry) }         box = Gtk::HBox.new         box.add(Gtk::Label.new("Text:"))         box.add(entry)         box.add(button)         add(box)         show_all       end       def cmdAllCaps(textField)         textField.set_text(textField.get_text.upcase)       end     end     SampleWindow.new     Gtk::main 

In this example, a SampleWindow class is defined; this is a cleaner approach because it allows the class to control its own look and behavior (rather than requiring the caller to configure the window). This main window is derived from Gtk::Window.

The call to set_title configures the text that will appear in the title bar of the application. As with the first example, a signal handler for destroy exits the GTK+ event loop when the main window is closed.

This class creates a single-line text-entry field using the Entry class, and it creates a button with the text label All Caps!. The signal handler for the button's clicked event calls the cmdUpperCase method, which is defined afterward. (The clicked event is generated after the user presses and releases the button.)

The Gtk::Window class is a Bin, so it can only contain a single child widget. In order to put our two child widgets in the window, we place those widgets in a box and add the box to the main window. As widgets are added to an HBox, they are positioned at the right edge of the box (by default). There is a corresponding Gtk::VBox widget that can stack multiple widgets vertically.

As with the earlier example, show_all is necessary to make the main window (and all its children) visible.

The cmdAllCaps method is invoked by the button's signal handler whenever the button is clicked. It gets the current text out of the entry field, converts it to uppercase, and sets it back into the entry field.

The actual application code is below the SampleWindow class definition. It simply creates the main window and runs the GTK+ event loop.

Working with Text Fields

GTK+ provides the Entry class for single-line input, as shown in the previous example. It also has the Text class, which is a multiline editor that we will describe here. Both Entry and Text are derived from the base class Editable, so they have several text-manipulation methods in common.

The code fragment shown in Listing 6.6 creates a multiline edit box and inserts some text into it. As the contents change, the current length of the text is reflected in a label at the bottom of the window.

Listing 6.6 GTK Text Editor
 require "gtk" class TextWindow < Gtk::Window   def initialize     super     set_title("Ruby/GTK Text Sample")     signal_connect("destroy") {  Gtk::main_quit }     @text = Gtk::Text.new     @text.signal_connect("changed") {  onChanged }     @text.set_word_wrap(true)     @text.set_editable(true)     font = Gdk::Font::font_load("times")     style = @text.get_style     style.set_font(font)     @status = Gtk::Label.new("")     scroller = Gtk::ScrolledWindow.new     scroller.set_policy(Gtk::POLICY_NEVER,           Gtk::POLICY_AUTOMATIC)     scroller.add(@text)     box = Gtk::VBox.new     box.add(scroller)     box.add(@status)     add(box)     @text.insert_text("This is an editor", 0)     @text.insert_text("really ", 5)     show_all   end   def onChanged     text = "Length: " + @text.get_length.to_s     @status.set_text(text)   end end TextWindow.new Gtk::main 

The code in Listing 6.6 gives us a simple text editor. Figure 6.5 shows a screenshot of this application.

Figure 6.5. A small GTK text editor.

graphics/06fig05.gif


The basic structure of the code is very similar to the buttons example: A window class is defined, with an event handler to terminate the app cleanly. At the end of initialize, show_all is used to make the window visible. The last two lines actually create the window and run the GTK+ event loop.

We create an editor widget named @text and set up a signal handler for the changed event. Any time text is inserted, deleted, or modified, this signal will fire, and we will execute the onChanged method. Word wrapping is enabled (the default is to wrap lines regardless of word breaks), and set_editable allows the user to change the contents (by default it is read-only).

Next, we want to configure the @text widget to display its text in a different font. Unfortunately, this is difficult to do in a way that works on all platforms.

GTK+ actually discourages programmers from configuring specific fonts and colors in most cases. Instead, it is often better to allow the users to customize their own system using themes. If you do wish to override the defaults, however, you must do so through the Style class.

In this case, we attempt to load a font from the "Times" family, which on a Windows platform is likely to bring up some variant of Times Roman. On a Linux/Unix platform, the parameter would be a standard X Window System font string. The system will return whatever font is the closest match available.

Each widget has a Style object, which you can access using the get_style method. In this case, we update the font for that style using set_font. You could also configure the text color of this style.

The @status label is initially empty. We will change its text later.

GTK+ provides two ways to add scrollbars to an application. You can directly create ScrollBar objects and use signals to synchronize them with the content widget(s). However, in most cases, it is simpler to use the ScrolledWindow widget instead.

The ScrolledWindow widget is a Bin, meaning it can only contain a single child widget. Of course, that child widget could be a Box or other container that allows multiple children. Several GTK+ widgets, including Text, automatically interact with a ScrolledWindow widget, requiring almost no additional code.

In this example, we create a ScrolledWindow widget named scroller and configure it using set_policy. We choose never to display a horizontal scrollbar and to automatically display a vertical scrollbar only when the editor has more lines than can be seen at once. We add the text editor directly to scroller.

We now set up a Vbox that will stack our widgets vertically. The scrolling window that contains the text field is added first, so it will appear at the top of the main window. The @status text will appear at the bottom. The box is then added to our main window.

The next two lines insert text into the text editor. The first line inserts a string at offset 0 (at the beginning of the text). Because there was no text, 0 is the only reasonable place to insert. We then insert some additional text at offset 5. The result is a text editor containing the string This really is an editor.

Because we already configured the handler for the changed event, it will be triggered by our calls to insert_text. This means the status will already display correctly, even before the user makes any changes to the text.

The onChanged method handles the changed event. It uses get_length to determine the length of the text currently in the text editor and creates a message string. That message is displayed by calling @status.set_text(text).

Working with Other Widgets

Even a relatively simple GUI may need more than text fields and buttons. Often we find a need for radio buttons, checkboxes, and similar widgets. This next example illustrates a few of these.

Here, we assume the user is making an airline reservation. The Gtk::CList class (representing a multicolumn list) is used for the destination city. A checkbox (actually called a checkbutton) determines whether the ticket is roundtrip, and a set of radio buttons (class RadioButton) is used for the seating. A Purchase button completes the interface.

The code is shown in Listing 6.7. A screenshot of the application can be seen in Figure 6.6.

Figure 6.6. A sample app illustrating various GTK widgets.

graphics/06fig06.gif


Listing 6.7 GTK Buttons
 require "gtk"     class TicketWindow < Gtk::Window       def initialize         super         set_title("Purchase Ticket")         signal_connect("destroy") {  Gtk::main_quit }         @destination = Gtk::CList.new(["Destination", "Country"])         @destination.append(["Cairo", "Egypt"]);         @destination.append(["New York", "USA"]);         @destination.append(["Tokyo", "Japan"]);         @destination.signal_connect("select_row") do           |list,row,col,event|           @city = @destination.get_text(row, 0)         end         @destination.select_row(0, 0)         @roundTrip = Gtk::CheckButton.new("Round Trip")         purchase = Gtk::Button.new("Purchase")         purchase.signal_connect("clicked") {  cmdPurchase }         @result = Gtk::Label.new("")         @coach = Gtk::RadioButton.new(nil, "Coach class")         @business = Gtk::RadioButton.new(@coach, "Business class")         @first = Gtk::RadioButton.new(@business, "First class")         flightBox = Gtk::VBox.new         flightBox.add(@destination)         flightBox.add(@roundTrip)         seatBox = Gtk::VBox.new         seatBox.add(@coach)         seatBox.add(@business)         seatBox.add(@first)         topBox = Gtk::HBox.new         topBox.add(flightBox)         topBox.add(seatBox)         mainBox = Gtk::VBox.new         mainBox.add(topBox)         mainBox.add(purchase)         mainBox.add(@result)         add(mainBox)         show_all       end       def cmdPurchase         text = @city         if(@first.active?)           text += ": first class"         elsif(@business.active?)           text += ": business class"         elsif(@coach.active?)           text += ": coach"         end         if(@roundTrip.active?) then text += ", round trip " end         @result.set_text(text)       end     end     TicketWindow.new     Gtk::main 

This application creates a main window with a signal handler, as before. Next, a multicolumn list box (Gtk::CList) widget is created with two columns. Three rows of data are added to the list, and a signal handler is created for the "select_row" event. This will be invoked whenever the user selects a different row. The handler will update the @city member variable to contain the text from the first column (column number 0) of the newly selected row.

A simple checkbox (Gtk::CheckButton) and pushbutton (Gtk::Button) are created. The signal handler for the pushbutton will execute the cmdPurchase method whenever the button is clicked. The label named @result is initially blank, but later will be set to a string indicating what type of ticket was purchased.

Three radio buttons are created as a group, meaning that only one of them can be selected at a time. When the user clicks any of these radio buttons, any previously selected button will automatically be deselected. The first parameter to the radio button constructor is the previous radio button within the same group. Therefore, the first radio button in a group passes nil, and the rest of the buttons pass the earlier button.

The widgets need to be arranged in a way that will make sense to the user, so a combination of HBox and VBox widgets is used. The list box will appear above the checkbox. The three radio buttons will appear in a vertical stack to the right of the list box. Finally, the Purchase pushbutton will appear below all the other widgets.

The cmdPurchase method is straightforward: It builds a string that reflects all the current widget states when the Purchase button is clicked. Radio buttons and checkboxes have a method named active? that returns true if the button is selected. The text is then placed in the @result label so it will appear on the screen.

Most applications use menus as a key part of their user interface. This next example demonstrates how to set up menus using Ruby/GTK. It also shows how easy it is to add tooltipsa nice touch for any program.

The code in Listing 6.8 creates a main window that has a File menu, along with two other dummy items on the menu bar. The File menu contains an Exit item that exits the application. Both the File and Exit items have tooltips. Figure 6.7 shows a screenshot of this example.

Figure 6.7. A GTK menu with tooltips.

graphics/06fig07.gif


Listing 6.8 GTK Menu
 require "gtk" class MenuWindow < Gtk::Window   def initialize     super     set_title("Ruby/GTK Menu Sample")     signal_connect("destroy") {  Gtk::main_quit }     fileExitItem = Gtk::MenuItem.new("Exit")     fileExitItem.signal_connect("activate") {  Gtk::main_quit }     fileMenu = Gtk::Menu.new     fileMenu.add(fileExitItem)     fileMenuItem = Gtk::MenuItem.new("File")     fileMenuItem.set_submenu(fileMenu)     menuBar = Gtk::MenuBar.new     menuBar.append(fileMenuItem)     menuBar.append(Gtk::MenuItem.new("Nothing"))     menuBar.append(Gtk::MenuItem.new("Useless"))     tooltips = Gtk::Tooltips.new     tooltips.set_tip(fileMenuItem, "File Menu", "")     tooltips.set_tip(fileExitItem, "Exit the app", "")     box = Gtk::VBox.new     box.pack_start(menuBar, false, false, 0)     box.add(Gtk::Label.new("Try the menu and tooltips!"))     add(box)     set_default_size(300, 100)     show_all   end end MenuWindow.new Gtk::main 

Again, the basic structure is like the other examples. In this case, we create a MenuItem widget named Exit and a signal handler so it will actually exit the program. The signal is activate, and it will be generated when a user actually invokes this item on the menu.

The File menu is created, and the Exit item is added to it. This is all that is required to create a pop-up menu. Next, the File menu item is createdthis is what will actually appear on the menu bar. We call set_submenu to connect the File menu item with the File menu itself.

We create the menu bar and add its three items: File, Nothing, and Useless. Only the first item is actually functionalthe other two are just for show.

A single Tooltips object manages all the actual tooltips. To create a tooltip for any widget, such as a menu item, call set_tip, passing the widget, the tooltip text, and another string that contains additional "private" text. This private text is not shown as part of the tooltip; it could be used by a help system, for example.

A Vbox is used to place the menu bar at the top of the main window, above any other widgets. In this case, instead of using add to place the menu bar in the box, we use pack_start to gain more control over the exact look and placement of the widget.

The first parameter to pack_start is the widget we are placing. The second parameter is a Boolean indicating whether this widget should take up all the available space. Note that it won't make the widget actually grow; instead, it will typically center the widget. In this case, we want the menu bar at the very top of the screen, so we pass false.

The third parameter is a Boolean for whether this widget should grow to fill all the available space. Because we just want a small menu bar, we pass false for this as well. The last parameter to pack_start is for padding. This would be used to create additional space all around the widget. We don't want any, so we pass zero.

A text label will take up most of the main window. Finally, we force the initial size of the window to be 300 pixels wide by 100 pixels tall.

Other Notes

We should mention GNOME at least briefly. This is a higher-level package that depends on GTK+, and some GNOME-Ruby bindings are available. However, it is not part of the core GTK+ functionality and is not available for MS Windows.

At the time of this writing, we are anticipating the next major release of GTK+, which will be version 2.0. A preview release (known as "unstable version 1.3") shipped in September 2000, and the official release is expected soon. We should note that existing GTK+ 1.2 applications will likely not be compatible with the newer version.

Among the improvements expected in GTK+ 2.0 are the following:

  • Stronger support for internationalization

  • Better font support (via a new library named Pango)

  • New and improved widgets and dialog boxes

  • Built-in portability to the MS Windows, BeOS, and Mac platforms

  • A simplified API with unneeded methods removed

The official Ruby/GTK home page is at http://www.ruby-lang.org/gtk/en/, and the official GTK+ home page, including a comprehensive API reference, is at www.gtk.org.


   

 

 



The Ruby Way
The Ruby Way, Second Edition: Solutions and Techniques in Ruby Programming (2nd Edition)
ISBN: 0672328844
EAN: 2147483647
Year: 2000
Pages: 119
Authors: Hal Fulton

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net