Creating a GUI Application with Ruby/GTK

Problem

You want to write a GUI application that uses the GTK widget library, perhaps so you can integrate it with the Gnome desktop environment.

Solution

Use the Ruby bindings to Gnomes GTK widget library, available as a third-party download. Heres a simple Ruby/GTK application (Figure 21-5).

	#!/usr/bin/ruby -w
	# 
gtktrout.rb
	require gtk2

	Gtk.init
	window = Gtk::Window.new Tiny Ruby/GTK Application
	label = Gtk::Label.new You are a trout!
	window.add label
	window.signal_connect(destroy) { Gtk.main_quit }
	window.show_all
	Gtk.main

Figure 21-5. You are a GTK trout


Discussion

Gnome is one of the two most popular Unix desktop suites. The Ruby-Gnome2 project provides and documents Ruby bindings to Gnomes vast array of C libraries. You can write Ruby applications that fully integrate with the Gnome desktop, but in this recipe Im going to focus on the basics of the Gnome GUI library GTK.

Although the details are different, the sample program above is basically the same as it would be with Tk (Recipe 21.12) or the wxRuby library (Recipe 21.13). You create two widgets (a window and a label), attach the label to the window, and tell the GUI library to display the window. As with Tk and wxRuby, the application goes into a display loop, capturing user events like mouse clicks.

The sample program won actually respond to any user events, though, so lets create a Ruby/GTK version of the stopwatch program seen in previous GUI recipes.

The core methods, the ones that actually implement the stopwatch, are basically the same as the corresponding methods in the Tk and wxRuby recipes. Since GTK doesn have a timer widget, Ive implemented a simple timer as a separate thread. The other point of interest is the HTML-like markup that GTK uses to customize the font size and weight of the stopwatch text.

	#!/usr/bin/ruby -w
	# gtk_stopwatch.rb
	require gtk2

	class Stopwatch

	 LABEL_MARKUP = %s

	 def start
	 @accumulated ||= 0
	 @elapsed = 0
	 @start = Time.now

	 @mybutton.label = Stop
	 set_button_handler(clicked) { stop }
	 @timer_stopped = false
	 @timer = Thread.new do
	 until @timer_stopped do
	 sleep(0.1)
	 tick unless @timer_stopped
	 end
	 end
	 end

	 def stop
	 @mybutton.label = Start
	 set_button_handler(clicked) { start }
	 @timer_stopped = true
	 @accumulated += @elapsed
	 end

	 def reset
	 stop
	 @accumulated, @elapsed = 0, 0
	 @mylabel.set_markup(LABEL_MARKUP % 0:00:00.0)
	 end

	 def tick
	 @elapsed = Time.now - @start
	 time = @accumulated + @elapsed
	 h = sprintf(\%02i, (time.to_i / 3600))
	 m = sprintf(\%02i, ((time.to_i % 3600) / 60))
	 s = sprintf(\%02i, (time.to_i % 60))
	 mt = sprintf(\%1i, ((time - time.to_i)*10).to_i)
	 @mylabel.set_markup(LABEL_MARKUP % "#{h}:#{m}:#{s}:#{mt}")
	 end

Now begins the GUI setup. Ruby uses VBox and HBox objects to pack widgets into the display area. The stopwatch application will give its main window a single VBox containing three widgets arranged from top to bottom: a menu bar, a label (displaying the stopwatch time), and a button (to start and stop the stopwatch):

	 def initialize
	 
Gtk.init
	 root = Gtk::Window.new(GTK Stopwatch)

	 accel_group = Gtk::AccelGroup.new
	 root.add_accel_group(accel_group)
	 root.set_border_width 0

	 box = Gtk::VBox.new(false, 0)
	 root.add(box)

The programs menu bar consists of many nested MenuBar, Menu, and MenuItem objects. Rather than create these objects ourselves, we define the parameters of our menu bar in a nested array, and pass it into an ItemFactory object:

	 menu_factory = 
Gtk::ItemFactory.new(Gtk::ItemFactory::TYPE_MENU_BAR,
	 
, nil) menu_spec = [ [/_Program], [/Program/_Start, , nil, nil, lambda { start } ], [/Program/S_top, , nil, nil, lambda { stop } ], [/Program/_Exit, , nil, nil, lambda { Gtk.main_quit } ], [/_Reset], [/Reset/_Reset Stopwatch, , nil, nil, lambda { reset } ] ] menu_factory.create_items(menu_spec) menu_root = menu_factory.get_widget(
) box.pack_start(menu_root)

The label and the button are pretty simple: just define them and pack them into the VBox:

	 @mylabel = Gtk::Label.new
	 @mylabel.set_markup(LABEL_MARKUP % 0:00:00.0)
	 box.pack_start(@mylabel)

	 @mybutton = Gtk::Button.new(Start)
	 set_button_handler(clicked) { start }
	 box.pack_start(@mybutton)

	 root.signal_connect(destroy) { Gtk.main_quit }
	 root.show_all

	 Gtk.main
	 end

Ive been calling a nonexistent method Stopwatch#set_button_handler whenever I want to modify the code that runs when the user clicks the button. I close out the Stopwatch class by defining that method (Figure 21-6):

	 def set_button_handler(event, &block)
	 @mybutton.signal_handler_disconnect(@mybutton_handler) if @mybutton_handler
	 @mybutton_handler = @mybutton.signal_connect(event, &block)
	 end
	end

	Stopwatch.new

In the Tk recipe, I simply called a buttons command method whenever I needed to change the code block that runs when the user clicks the button. So why this set_ button_handler code? Why not just call signal_connect whenever I need to change what the button does here? I can do that because GTK lets you associate multiple code blocks with a single event. This doesn usually come up, but its a problem here because Im changing the function of a button.

Figure 21-6. The GTK stopwatch


If the button is set up to call start when you click it, and you call signal_ connect(clicked,proc { stop }), then clicking on the button will call start and then call stop. Youve added a second code block to the "clicked" event, when what you want is to replace the old "clicked" code with the new code. To avoid this problem, set_button_handler removes any old handler from the button before installing the new handler. The set_button_handler method tracks the internal ID of the newly installed handler, so that it can be removed if the user clicks the button yet again.

See Also

  • You can download the Ruby bindings to GTK from the project homepage (http://ruby-gnome2.sourceforge.jp/); the GTK homepage itself is at http://www.gtk.org; Debian GNU/Linux users can install the libgtk2-ruby package
  • The Ruby GTK bindings are documented on the Ruby-GNOME2 Wiki at http://ruby-gnome2.sourceforge.jp/hiki.cgi?Ruby%2FGTK; theres also a tutorial at http://ruby-gnome2.sourceforge.jp/hiki.cgi?tut-gtk
  • Don confuse the Ruby-GNOME2 project with its predecessor, Ruby-GNOME; the documentation for the older project is still online and will mislead you if you go to the wrong web site


Strings

Numbers

Date and Time

Arrays

Hashes

Files and Directories

Code Blocks and Iteration

Objects and Classes8

Modules and Namespaces

Reflection and Metaprogramming

XML and HTML

Graphics and Other File Formats

Databases and Persistence

Internet Services

Web Development Ruby on Rails

Web Services and Distributed Programming

Testing, Debugging, Optimizing, and Documenting

Packaging and Distributing Software

Automating Tasks with Rake

Multitasking and Multithreading

User Interface

Extending Ruby with Other Languages

System Administration



Ruby Cookbook
Ruby Cookbook (Cookbooks (OReilly))
ISBN: 0596523696
EAN: 2147483647
Year: N/A
Pages: 399

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