Creating a GUI Application with wxRuby

Problem

You want to write a portable GUI application that looks better than a Tk application.

Solution

Use the wxRuby library, available as a third-party download. It uses native GUI widgets on Windows, Unix, and Mac OS X. Its got many more features than the Tk library, and even greater complexity.

Heres a very simple wxRuby application (Figure 21-3):

	#!/usr/bin/ruby -w
	# wxtrout.rb

	require wxruby
	class TroutApp < Wx::App
	 def on_init
	 frame = Wx::Frame.new(nil, -1, Tiny wxRuby Application)
	 panel = Wx::StaticText.new(frame, -1, You are a trout!,
	 Wx::Point.new(-1,1), Wx::DEFAULT_SIZE,
	 Wx::ALIGN_CENTER)
	 frame.show
	 end
	end

	TroutApp.new.main_loop

Figure 21-3. You are a wxRuby trout


Discussion

The simple wxRuby application has the same basic structure as its Tk cousin (see Recipe 21.12). A top-level widget is created (here called a Frame) and a label (StaticText) widget is added to it. The application then goes into an event loop, listening for and retrieving events like mouse clicks.

A wxRuby version of the Tk stopwatch program is also similar, although much longer. wxRuby code tends to be more verbose and less idiomatic than Ruby Tk code.

The core methods are nearly unchanged, because they have little to do with the GUI:

	#!/usr/bin/ruby -w
	# wx_stopwatch.rb
	require wxruby

	class StopwatchApp < Wx::App

	 def start
	 @start = Time.now
	 @button.set_label(Stop)
	 @button.refresh
	 @frame.evt_button(@button.get_id) { stop }
	 @timer.start(100) # The timer should tick every 100 milliseconds.
	 end

	 def stop
	 @button.set_label(Start)
	 @button.refresh
	 @frame.evt_button(@button.get_id) { start }
	 @timer.stop
	 @accumulated += @elapsed
	 end

	 def reset
	 stop
	 @accumulated, @elapsed = 0, 0
	 @label.set_label(0:00:00.0)
	 @frame.layout
	 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}"
	 @label.set_label(newtime)
	 @frame.layout
	 end

The menu bar takes a lot more code in wxRuby than in Tk. Every widget in a wxRuby program has a unique ID, which must be passed in when you register an event handler. Ive defined a hardcoded ID for each menu item, so that after I create the "menu item" widget, I can pass its unique ID into the event-handler registration method, evt_menu. You can really sense the underlying C code here:

	 # Constants for the IDs of the menu items.
	 START_MENU = 10
	 STOP_MENU = 11
	 EXIT_MENU = 12
	 RESET_MENU = 13

	 # Constant for the ID of the timer widget, used below.
	 TIMER_ID = 14

	 def on_init
	 @accumulated, @elapsed = 0, 0
	 @frame = Wx::Frame.new(nil, -1,  
wxRuby Stopwatch)

	 menu_bar = Wx::MenuBar.new

	 program_menu = Wx::Menu.new
	 menu_bar.append(program_menu, &Program)
	 program_menu.append(START_MENU, &Start, Start the stopwatch)
	 @frame.evt_menu(START_MENU) { start }
	 program_menu.append(STOP_MENU, S&top, Stop the stopwatch)
	 @frame.evt_menu(STOP_MENU) { stop }
	 menu_exit = program_menu.append(EXIT_MENU, "E&xit	Alt-X",
	 Exit the program)
	 @frame.evt_menu(EXIT_MENU) { exit }

	 reset_menu = Wx::Menu.new
	 menu_bar.append(reset_menu, &Reset)
	 reset_menu.append(RESET_MENU, &Reset, Reset the stopwatch)
	 @frame.evt_menu(RESET_MENU) { reset }
	 @frame.set_menu_bar(menu_bar)

wxRuby uses Sizer objects to pack widgets into their display areas. The BoxSizer object used below arranges widgets within the frame vertically, so that the label will be above the stopwatch button.

	 sizer = Wx::BoxSizer.new(Wx::VERTICAL)

	 @label = Wx::StaticText.new(@frame, -1, 0:00:00.0)
	 font = Wx::FontData.new.get_chosen_font
	 font.set_point_size(16)
	 font.set_weight(Wx::FONTWEIGHT_BOLD)
	 @label.set_font(font)
	 sizer.add(@label, 1, Wx::ALIGN_CENTER)

The button and the timer work more or less like their Tk equivalents. The call to @frame.set_sizer tells the root widget to use our vertical BoxSizer when deciding how to arrange widgets on the screen (Figure 21-4).

	 @button = Wx::Button.new(@frame, -1, Start)
	 @frame.evt_button(@button.get_id) { start }
	 sizer.add(@button, 0, Wx::ALIGN_CENTER, 2)

	 @frame.set_sizer(sizer)
	 @frame.show
	 @timer = Wx::Timer.new(@frame, TIMER_ID)
	 @frame.evt_timer(TIMER_ID) { tick }
	 end
	end

	StopwatchApp.new.main_loop

Figure 21-4. The wxRuby stopwatch looks more like a native application than the Tk one


See Also

  • You need to download (and, on Unix systems, compile) wxRuby as a Ruby extension; you can get it from http://wxruby.rubyforge.org/; the wxRuby developers provide a good installation guide at http://wxruby.rubyforge.org/wiki/wiki.pl?Installation
  • The wxRuby wiki has a lot of useful information, including a simple tutorial at http://wxruby.rubyforge.org/wiki/wiki.pl?Getting_Started; the wxRuby distribution also comes with many good sample applications in its samples/ directory
  • The web site for wxWidgets (the underlying library to which wxRuby is a binding) also has lots of good reference material: http://www.wxwidgets.org/; you just have to be able to translate the C++-style class and method names into Ruby style (for instance, WxLabel::SetLabel becomes Wx::Label#set_label)


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