Credit: Kevin Marshall
You need to create a program that has a graphical user interface (GUI).
Use the Tk library. Its language-independent, cross-platform, and best of all, it comes standard with most Ruby distributions.
With Tk you create GUI elements, or "widgets", and then bind code blocks to them. When something happens (like the user clicking a widget), Tk runs the appropriate code block.
Ruby provides a class for each type of Tk widget. This simple Tk program creates a "root" widget (the application window), and a "label" widget within the window. The program then waits for events (although it can respond to any).
require k root = TkRoot.new { title "Tiny Tk Application" } label = TkLabel.new(root) { text "You are a trout!" } label.pack Tk.mainloop
When run, it looks like Figure 21-1.
The simple application above shows most of the basic features of GUI programming in Tk and other modern GUI toolkits. Well use the techniques to build a more complex application.
Tk GUI development and layout take a parent/child approach. Most widgets are children of other widgets: depending on the widget, this nesting can go arbitrarily deep. The exception to this rule is the TkRoot widget: its always the top-level widget, and its represented as the application window.
Child widgets are "packed" inside their parents so they can be displayed. A system called the geometry manager controls where on the screen the widgets actually show up. The default geometry manager is the "placer" manager, which lets you place widgets in relation to each other.
Tk applications are event-driven, so the final step is to start a main event loop which tells our program to listen for events to be fired on our widgets.
To further illustrate, lets make a simple stopwatch program to demostrate a realworld use of Tk.
To start, well create four simple methods that will be bound to our widgets. These are the nonGUI core of the program:
#!/usr/bin/ruby # stopwatch.rb require k class Stopwatch def start @accumulated = 0 unless @accumulated @elapsed = 0 @start = Time.now @mybutton.configure( ext => Stop) @mybutton.command { stop } @timer.start end def stop @mybutton.configure( ext => Start) @mybutton.command { start } @timer.stop @accumulated += @elapsed end def reset stop @accumulated, @elapsed = 0, 0 @mylabel.configure( ext => 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) newtime = "#{h}:#{m}:#{s}:#{mt}" @mylabel.configure( ext => newtime) end
Next, we set up our GUI. This consists of six simple widgets. As before, the TkRoot is our application window, and contains all our other widgets:
def initialize root = TkRoot.new { title Tk Stopwatch }
The TkMenuBar corresponds to the menu bar at the top of the screen in most modern GUI programs. Its an easy way to group a set of program features and make them available across our application. The menu layout of a TkMenuBar is defined by a nested array containing the menu items, and the code blocks to run when a menu item is selected:
menu_spec = [ [ [Program], [Start, lambda { start } ], [Stop, lambda { stop } ], [Exit, lambda { exit } ] ], [ [Reset], [Reset Stopwatch, lambda { reset } ] ] ] @menubar = TkMenubar.new(root, menu_spec, earoff => false) @menubar.pack(fill=>x, side=> op)
The TkFont is used only as a configuration option for our TkLabel, which in turn is only used to display the value of our stopwatch:
@myfont = TkFont.new(size => 16, weight => old) @mylabel = TkLabel.new(root) @mylabel.configure( ext => 0:00:00.0, font => @myfont) @mylabel.pack(padx => 10, pady => 10)
Apart from the menu bar, the TKButton is the only part of the GUI that the user can directly manipulate. The code block passed into its command method is run when the user clicks the button. Recall how the start and stop methods call this method to modify the behavior of the button. This makes the button act like the toggle on a physical stopwatch:
@mybutton = TkButton.new(root) @mybutton.configure( ext => Start) @mybutton.command { start } @mybutton.pack(side=>left, fill => oth)
The TkAfter event is an especially interesting widget because it has no direct visual representation in our program. Instead, it runs in the background firing our tick method every millisecond:
@timer = TkAfter.new(1, -1, proc { tick })
Finally, well start up the main Tk event loop. This call loads the GUI and starts listening for events:
Tk.mainloop end end Stopwatch.new
Figure 21-2 shows the final product.
This recipe only scratches the surface of the Tk library, not to mention GUI design in general. The Tk library includes dozens of widgets with lots of options and features. Entire books have been writen about how to use the library. You should refer to the Ruby Tk documentation or other Tk references for complete details.
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