Section 12.3. FXRuby (FOX)


12.2. Ruby/GTK2

The GTK+ library is a by-product of the GIMP (the GNU Image Manipulation Program); the name actually means the GIMP Toolkit. Like UNIX 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. Since GTK+ 2.0, it supports not only UNIX-like systems but also the MS Windows family and Mac OS X with the X Window System. There is an ongoing port to the Mac OS X natively, though at the time of this writing it is not stable.

Ruby/GTK2 is a port of GTK+ 2.0. Don't confuse it with Ruby/GTK (based on GTK+ 1.2), which is incompatible and even obsolete. This section deals only with Ruby/GTK2.

12.2.1. Overview

Ruby/GTK2 is a library that allows Ruby applications to use the GTK+ 2.x 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; it 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/GTK2 thus presents a clean, object-oriented API, while also staying close to the underlying C. In addition, Ruby/GTK2 is implemented carefully by hand, not by using a code generator such as SWIG. As a result, the API is very Ruby-like, using blocks, omittable arguments, and so on. The API reference is available at http://ruby-gnome2.sourceforge.jp/.

GTK+ is actually built on top of libraries named GLib, Pango, ATK, Cairo, and GDK. It supports nongraphical functions (GLib), layout and rendering of internationalized text using UTF-8 (Pango), accessibility (Atk), Graphic rendering (Cairo), lower-level graphical objects (Gdk), and a lot of widgets and high-level graphic objects (Gtk).

At the time of this writing, Ruby/GTK2 is at version 0.14.1 and is compatible with the current stable versions of Ruby and GTK+ (2.0). Besides Linux, it supports the Windows family of operating systems and Mac OS X (with the X Window System). There is an ongoing port to native Mac OS X, though it is currently not stable.

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

Some of the GTK+ widgets include menus, toolbars, tooltips, trees, progress bars, sliders, and calendars. But 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.

All strings you pass to Ruby/GTK2 methods must be in UTF-8. You cannot just use non-ASCII characters from some Windows single- or multibyte codepage. Take care to edit your Ruby script in UTF-8 mode and add $KCODE="U" at the top of your Ruby script.

12.2.2. A Simple Windowed Application

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

Normally we call Gtk.init to initialize Ruby/GTK2 and then 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. The following code fragment is similar to the one for Tk, which displays the current date:

$KCODE = "U" require "gtk2" Gtk.init window = Gtk::Window.new("Today's Date") window.signal_connect("destroy") { Gtk.main_quit } str = Time.now.strftime("Today is \n%B %d, %Y") window.add(Gtk::Label.new(str)) window.set_default_size(200, 100) window.show_all Gtk.main


The $KCODE variable was discussed in Chapter 4, "Internationalization in Ruby." The Gtk.init call initializes Ruby/GTK2.

The main window (of type Gtk::Window) is created as a "top level" window with the text that will appear in the title bar. 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 Ruby/GTK2 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.

12.2.3. Working with Buttons

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

Listing 12.5 will accept a simple line of text in a text entry field and (when the All Caps! button is clicked) will convert the string to uppercase. Figure 12.4 shows the text entry field before the button is clicked.

Listing 12.5. Buttons in GTK

$KCODE = "U" require "gtk2" class SampleWindow < Gtk::Window   def initialize     super("Ruby/GTK2 Sample")     signal_connect("destroy") { Gtk.main_quit }     entry = Gtk::Entry.new     button = Gtk::Button.new("All Caps!")     button.signal_connect("clicked") {       entry.text = entry.text.upcase     }     box = Gtk::HBox.new     box.add(Gtk::Label.new("Text:"))     box.add(entry)     box.add(button)     add(box)     show_all   end end Gtk.init SampleWindow.new Gtk.main

Figure 12.4. A simple GTK pushbutton example.


In Listing 12.5, 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.

As with the "Today's Date" 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 Gtk::EnTRy class and a Gtk::Button with the text label All Caps!. The signal handler for the button's clicked event calls the signal handler. (The clicked event is generated after the user clicks and releases the button.)

The Gtk::Window class is a Gtk::Bin, so it can only contain a single child widget. 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 a Gtk::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 signal handler of clicked is invoked 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.

12.2.4. Working with Text Fields

GTK+ provides the Gtk::Entry class for single-line input, as shown in the previous example. It also has a Gtk::TextView class, which is a powerful multiline editor that we will describe here.

Listing 12.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 (see Figure 12.5).

Listing 12.6. A GTK Text Editor

$KCODE = "U" require "gtk2" class TextWindow < Gtk::Window   def initialize     super("Ruby/GTK2 Text Sample")     signal_connect("destroy") { Gtk.main_quit }     set_default_size(200, 100)     @text = Gtk::TextView.new     @text.wrap_mode = Gtk::TextTag::WRAP_WORD     @buffer = @text.buffer     @buffer.signal_connect("changed") {       @status.text = "Length: " + @buffer.char_count.to_s     }     @buffer.create_tag('notice',                        'font' => "Times Bold Italic 18",                        'foreground' => "red")     @status = Gtk::Label.new     scroller = Gtk::ScrolledWindow.new     scroller.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_NEVER)     scroller.add(@text)     box = Gtk::VBox.new     box.add(scroller)     box.add(@status)     add(box)     iter = @buffer.start_iter     @buffer.insert(iter, "This is an editor")     iter.offset = 5     @buffer.insert(iter, "really ", "notice")     show_all   end end Gtk.init TextWindow.new Gtk.main

Figure 12.5. A small GTK text editor.


The basic structure of the code is similar to the button example: Initialize Ruby/GTK2, define a window class with an event handler to terminate the app cleanly, and set the initial size of the main window. 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. Word wrap is enabled here; the default is to wrap lines regardless of word breaks.

The variable @buffer is the text buffer of @text. We give it a signal handler for the changed event; any time text is inserted, deleted, or modified, this signal will fire, and the signal handler will be called. The signal handler uses char_count to determine the length of the current text in the text editor and creates a message string; that message is displayed by setting @status.text = text.

Next we want to configure the @text widget to display its text in a different style. Create a "notice" tag using create_tag. This tag has the font "Times Bold Italic 18" and a foreground color of red. In a similar way, you can define tags with various other properties using Gtk::TextTag.

In this case, we attempt to use 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.

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 Gtk::ScrollBar objects and use signals to synchronize them with the content widget(s). However, in most cases, it is simpler to use the Gtk::ScrolledWindow widget instead.

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

In this sample, we create a Gtk::ScrolledWindow 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 Gtk::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 four lines insert text into the text editor. The first line gets the Gtk::TextIter of the beginning of the text (offset = 0) and then inserts a string there. Because there was no text, zero is the only reasonable place to insert. We then insert some additional text at offset five. 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. This means the status will already display correctly, even before the user makes any changes to the text.

12.2.5. 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, check boxes, and similar widgets. This next example illustrates a few of these.

Listing 12.7 assumes that the user is making an airline reservation. The Gtk::treeView, Gtk::ListStore, and Gtk::treeViewColumn classes (representing a multicolumn list) are used for the destination city. A check box (actually called a Gtk::CheckButton) determines whether the ticket is round-trip, and a set of radio buttons (class Gtk::RadioButton) is used for the seating. A Purchase button completes the interface (see Figure 12.6).

Listing 12.7. Airline Ticket Example

$KCODE = "U" require "gtk2" class TicketWindow < Gtk::Window   def initialize     super("Purchase Ticket")     signal_connect("destroy") { Gtk.main_quit }     dest_model = Gtk::ListStore.new(String, String)     dest_view = Gtk::TreeView.new(dest_model)     dest_column = Gtk::TreeViewColumn.new("Destination",                       Gtk::CellRendererText.new,                       :text => 0)     dest_view.append_column(dest_column)     country_column = Gtk::TreeViewColumn.new("Country",                          Gtk::CellRendererText.new,                          :text => 1)     dest_view.append_column(country_column)     dest_view.selection.set_mode(Gtk::SELECTION_SINGLE)     [["Cairo", "Egypt"], ["New York", "USA"],      ["Tokyo", "Japan"]].each do |destination, country|       iter = dest_model.append       iter[0] = destination       iter[1] = country     end     dest_view.selection.signal_connect("changed") do       @city = dest_view.selection.selected[0]     end     @round_trip = Gtk::CheckButton.new("Round Trip")     purchase = Gtk::Button.new("Purchase")     purchase.signal_connect("clicked") { cmd_purchase }     @result = Gtk::Label.new     @coach = Gtk::RadioButton.new("Coach class")     @business = Gtk::RadioButton.new(@coach, "Business class")     @first = Gtk::RadioButton.new(@coach, "First class")     flight_box = Gtk::VBox.new     flight_box.add(dest_view).add(@round_trip)     seat_box = Gtk::VBox.new     seat_box.add(@coach).add(@business).add(@first)     top_box = Gtk::HBox.new     top_box.add(flight_box).add(seat_box)     main_box = Gtk::VBox.new     main_box.add(top_box).add(purchase).add(@result)     add(main_box)     show_all   end   def cmd_purchase     text = @city     if @first.active?       text += ": first class"     elsif @business.active?       text += ": business class"     elsif @coach.active?       text += ": coach"     end     text += ", round trip " if @round_trip.active?     @result.text = text   end end Gtk.init TicketWindow.new Gtk.main

Figure 12.6. Various GTK widgets.


This application creates a main window with a signal handler as before. Next, a multicolumn list box widget is created with two columns. This list box is designed around a Model-View-Controller (MVC) design; Gtk::ListStore (the model class) has two String columns.

Then Gtk::TReeView is created. Gtk::treeViewColumn configures the column. The title of first column is "Destination", and the cell renderer is Gtk::CellRendererText. The first column (column number zero) of the model (Gtk::ListStore) is used as the text property value. In this way, cell renderers are used to draw the data in the tree model. Several cell renderers come with GTK+ 2.x, including the Gtk::CellRendererText, Gtk::CellRendererPixbuf, and the Gtk::CellRendererToggle. Then three rows of data are added to the list, and a signal handler is created for the "changed" 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 zero) of the newly selected row.

A simple check box (Gtk::CheckButton) and pushbutton (Gtk::Button) are created. The signal handler for the pushbutton will execute the cmd_purchase 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 on 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. So the first radio button doesn't have group as an argument, and the rest of the buttons pass the first radio button.

The widgets need to be arranged in a way that will make sense to the user, so a combination of Gtk::HBoxes and Gtk::VBoxes is used. The list box will appear above the check box. 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 cmd_purchase method is straightforward: It builds a string that reflects all the current widget states when the purchase button was pressed. Radio buttons and check boxes 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 sample demonstrates how to set up menus using Ruby/GTK2. It also shows how easy it is to add tooltips, a nice touch for any program.

Listing 12.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.

Listing 12.8. GTK Menu Example

$KCODE = "U" require "gtk2" class MenuWindow < Gtk::Window   def initialize     super("Ruby/GTK2 Menu Sample")     signal_connect("destroy") { Gtk.main_quit }     file_exit_item = Gtk::MenuItem.new("_Exit")     file_exit_item.signal_connect("activate") { Gtk.main_quit }     file_menu = Gtk::Menu.new     file_menu.add(file_exit_item)     file_menu_item = Gtk::MenuItem.new("_File")     file_menu_item.submenu = file_menu     menubar = Gtk::MenuBar.new     menubar.append(file_menu_item)     menubar.append(Gtk::MenuItem.new("_Nothing"))     menubar.append(Gtk::MenuItem.new("_Useless"))     tooltips = Gtk::Tooltips.new     tooltips.set_tip(file_exit_item, "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 Gtk.init MenuWindow.new Gtk.main

Again, the basic structure is like the other samples. In this case, we create a Gtk::MenuItem named Exit and create 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 submenu= to connect the File menu item with the File menu itself.

We create the Gtk::MenuBar and add its three items: File, Nothing, and Useless. Only the first item is actually functional&dash; the other two are just for show.

A single Gtk::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 Gtk::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 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.

12.2.6. Other Notes

Ruby/GTK2 is a part of the Ruby-GNOME2 project. GNOME is a higher-level package that depends on GTK+, and Ruby-GNOME2 is the set of bindings of GNOME libraries.

Ruby-GNOME2 includes these libraries:

  • Core libraries These libraries are included in ruby-gtk2 packages. Sometimes the term "Ruby/GTK2" is used to mean all of these libraries. All of these work on UNIX-like systems, MS Windows, Mac OS X (under X11), and Cygwin (under X11). These are required from other Ruby-GNOME2 libraries.

  • Ruby/GLib2 GLib is the low-level core library that forms the lowest-level infrastructure. It provides data structure handling for C, portability wrappers, Unicode support, and interfaces for such runtime functionality as an event loop, threads, dynamic loading, and an object system. Ruby/GLib2 is a wrapper for the GLib library. Because Ruby already has good string and list classes, some GLib functions are not implemented. On the other hand, it does provide some important functions to convert C and Ruby objects. This library is required from all other Ruby/GTK2 libraries.

  • Ruby/ATK This provides a set of interfaces for accessibility. By supporting the ATK interfaces, an application or toolkit can be used with such tools as screen readers, magnifiers, and alternative input devices.

  • Ruby/Pango A library for layout and rendering of text, with an emphasis on internationalization using UTF-8. It forms the core of text and font handling for GTK+ (2.0).

  • Ruby/GdkPixbuf2 An image loading and manipulation library. It supports numerous image formats such as JPEG, PNG, GIF, and others.

  • Ruby/GDK2 An intermediate layer that isolates GTK+ from the details of the windowing system.

  • Ruby/GTK2 This comprises the main GUI widgets.

  • Extra libraries These libraries are included in ruby-gnome2 packages with the core libraries. All of them work on UNIX-like systems. Some libraries (Ruby/GtkGLExt, Ruby/Libglade2) work on MS Windows, and Mac OS X. Also some libraries should work on Mac OS X (under X11) and Cygwin (under X11), though these are not well tested.

  • Ruby/GNOME2 Contains extra widgets for the GNOME environment.

  • Ruby/GnomeCanvas2 A widget for creating interactive structured graphics.

  • Ruby/GConf2 A process-transparent configuration database (similar to the Windows Registry).

  • Ruby/GnomeVFS Lets applications seamlessly access remote and local files.

  • Ruby/Gstreamer A multimedia framework for audio and video.

  • Ruby/GtkHtml2 An HTML widget.

  • Ruby/GtkGLExt Offers 3D rendering using OpenGL.

  • Ruby/GtkSourceView A Text widget with syntax highlighting and other features typical of a source code editor.

  • Ruby/GtkMozEmbed A widget embedding a Mozilla Gecko renderer.

  • Ruby/Libart2 Handles the basic drawing capabilities.

  • Ruby/Libgda An interface to the GDA (GNU Data Access) architecture to access data sources such as databases or LDAP.

  • Ruby/Libglade2 Gives applications the capability to load user interfaces from XML files at runtime. The XML files are created with GLADE, a powerful user interface builder that eases the creation of internationalized GUIs.

  • Ruby/PanelApplet A panel applet library for the GNOME panel.

  • Ruby/GnomePrint and Ruby/GnomePrintUI Offer widgets for printing.

  • Ruby/RSVG Enables rendering of SVG vector graphics.

  • External libraries These libraries are required from the Ruby-GNOME2 libraries.

  • Ruby/Cairo A 2D graphics library with support for multiple output devices. Currently supported output targets include the X Window System, win32, and image buffers. Experimental backends include OpenGL (through glitz), Quartz, XCB, PostScript, and PDF file output. This library is required from the core libraries. Ruby/Cairo also requires Ruby/GLib2. The official home page is at http://cairographics.org/.

  • Ruby/OpenGL is an interface to the OpenGL 3D graphics library. This library is required from Ruby/GtkGLExt2. It also works on many platforms. The official home page is at http://www2.giganet.net/~yoshi/.

  • Ruby-GetText-Package provides features to manage translated message catalogs for localization. (Refer to Chapter 4.) Ruby/Libglade2 is localized with this package (though it is optional), and other libraries also can localize with this library. The official home page is at http://gettext.rubyforge.org/.

The official Ruby-GNOME2 home page is at http://ruby-gnome2.sourceforge.jp/. You can find released files, the install guide, API references, tutorials, and sample code. The official GNOME home page is at http://www.gnome.org/, and the GTK+ home page is at http://www.gtk.org/.




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

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