Creating a GUI Application with Tk
Credit: Kevin Marshall
Problem
You need to create a program that has a graphical user interface (GUI).
Solution
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.
Figure 21-1. You are a trout
Discussion
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.
Figure 21-2. The stopwatch in action
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.
See Also
- If your Ruby distribution doesn include Tk, you can obtain the binary or source from http://www.tcl.tk; you may then need to rebuild Ruby from the source distribution once you have the Tk extension; on Debian GNU/Linux, you can just install the libtk-ruby package
- Rubys Tk documentation is not very complete; fortunately, its Tk binding is similar to Perls, so you can get a lot of information from the Perl/Tk documentation; one location for this is http://perlhelp.web.cern.ch/PerlHelp/
- Tcl and Tk by Brent B. Welch and Ken Jones with Jeffrey Hobbs (Prentice Hall)
- Perl/Tk Pocket Reference by Stephen Lidie (OReilly)
- The next few recipes (21.13 and 21.15) reproduce the simple GUI application and the stopwatch with the Ruby bindings to various other GUI libraries
Категории