Cross-Platform GUIs with Ruby and GTK

Part of a series: Embedding Ruby – Part I

I recently embarked on a quick-turn project with a timeline of 30 days to come up with GUI application that needed to utilize a touchscreen and basic PIN-pad for data entry, and also interface to an RFID reader and a scale. Hardware cost was a main concern for profitability, but the timeline could not budge, due to market pressures. I found some great low-price touch-panel linux machines at Technologic that came with a stripped down version of Linux that ran on a lightweight Freescale ARM processor that fit the bill, so I decided to tap into the power of Linux and Ruby to conquer this task in the short time allotted.

We had developed JRuby applications in the past at Atomic, and that worked out great on a modern-day PC. Although, with the lack of horsepower on this target platform, I didn’t want the extra processing required to run through a Java Virtual Machine to end up resulting in performance issues on this 800MHz 256MB RAM machine.

I initially tried to get Qt rolling with Ruby bindings, but ran into too many installation headaches. I also didn’t require the full power of Qt for the lightweight GUI required for this project, so I decided to give GTK a shot by utilizing the Ruby-GNOME2 framework that provides Ruby bindings for GTK and other GNOME libraries.

Below is a lightweight example that loads a simple prebuilt text editor window from a Glade UI designer file and implements open, save and quit operations via menu items:

Building the window in Glade is pretty trivial. First I set the output format to LibGlade, instead of the default of GtkBuilder, since I had problems getting that format to work with the Ruby bindings.

I started by adding a Window toplevel container, and then added a VBox container with 3 panes. Here is a screenshot:

Window with 3-paned HBox
Window with 3-paned VBox

At this point, I filled in the panes of the VBox container. I added a Menu Bar with default contents in the top slot, followed by a Text View in the middle, and a Status Bar at the bottom. The resultant window, in Glade, looks like:

Simple Text Editor Window
Simple Text Editor Window

To allow me to respond to menu item clicks, I selected the File->Open menu item, and went to the Signals tab in the properties view. I then simply added a name for my handler, on_open_clicked, to the GtkMenuItem->activate handler column. I did likewise for File->Save (on_save_clicked) and File->Quit (on_quit_clicked). My UI design was complete, so I saved it as editor_window.glade and was done!

Now, I simply needed to load the Glade file in Ruby, and make it do something. I simply created a new GladeXML object, and the block passed to the constructor wires up the events to the corresponding handler, which are simply methods in the same class with the same name I entered for each event in Glade. Here is my editor.rb file with all the magic to make the basic features work:

require 'gtk2'
require 'libglade2'

class Editor < Gtk::Window

  def initialize

    super

    # Create the window from the Glade file
    @glade = GladeXML.new('editor_window.glade') do |handler|
      self.method(handler)
    end

    # Grab a reference to the Window, handle detroy, and queue to show
    @window = @glade['window']
    @window.signal_connect('destroy'){self.on_quit_clicked}
    @window.show_all

    # Start GTK processing
    Gtk.main()

  end

  def on_open_clicked

    # Display a file open dialog
    dialog = Gtk::FileChooserDialog.new("Open File",
      @window,
      Gtk::FileChooser::ACTION_OPEN,
      nil,
      [Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
      [Gtk::Stock::OPEN, Gtk::Dialog::RESPONSE_ACCEPT])

    # Dump the text from the file into the text box
    if dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
      buf = @glade.get_widget('textview').buffer
      buf.text = File.read(dialog.filename)
    end

    # Make sure we get rid of the remains
    dialog.destroy

  end

  def on_save_clicked

    # Display a file save dialog
    dialog = Gtk::FileChooserDialog.new("Save File",
      @window,
      Gtk::FileChooser::ACTION_OPEN,
      nil,
      [Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
      [Gtk::Stock::SAVE, Gtk::Dialog::RESPONSE_ACCEPT])

    # Save the text box content to the specified file
    if dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
      buf = @glade.get_widget('textview').buffer
      File.open(dialog.filename, 'w+'){|f| f << buf.text}
    end

    # Make sure we get rid of the remains
    dialog.destroy

  end

  def on_quit_clicked
    # Stop GTK, since we are done
    Gtk.main_quit()
  end

end

# Create an instance of our editor window to start the show
Editor.new

Here is the corresponding ‘editor_window.glade’ file generated fully in Glade. Not really nice to look at, but included it here for completeness.



  
  
  
    True
    Text Editor Demo
    center
    440
    250
    True
    
      
        True
        vertical
        
          
            True
            
              
                True
                _File
                True
                
                  
                    True
                    
                      
                        gtk-new
                        True
                        True
                        True
                      
                    
                    
                      
                        gtk-open
                        True
                        True
                        True
                        
                      
                    
                    
                      
                        gtk-save
                        True
                        True
                        True
                        
                      
                    
                    
                      
                        gtk-save-as
                        True
                        True
                        True
                      
                    
                    
                      
                        True
                      
                    
                    
                      
                        gtk-quit
                        True
                        True
                        True
                        
                      
                    
                  
                
              
            
            
              
                True
                _Edit
                True
                
                  
                    True
                    
                      
                        gtk-cut
                        True
                        True
                        True
                      
                    
                    
                      
                        gtk-copy
                        True
                        True
                        True
                      
                    
                    
                      
                        gtk-paste
                        True
                        True
                        True
                      
                    
                    
                      
                        gtk-delete
                        True
                        True
                        True
                      
                    
                  
                
              
            
            
              
                True
                _View
                True
              
            
            
              
                True
                _Help
                True
                
                  
                    True
                    
                      
                        gtk-about
                        True
                        True
                        True
                      
                    
                  
                
              
            
          
          
            False
            0
          
        
        
          
            True
            True
          
          
            1
          
        
        
          
            True
            2
          
          
            False
            2
          
        
      
    
  

We are still wrapping up this project, but we have had a lot of fun with Ruby, Glade and GTK. Hope you can have some fun with it too.

Conversation
  • Samuel A. Falvo II says:

    As of this posting, the XML is malformed on this blog, and < appears instead of < in the blog text. Somewhere, things aren't being escaped quite right.

    • Greg Williams Greg Williams says:

      Samuel,

      It looks WordPress clobbered the XML in a previous save. I restored it, so it’s all good once again. Thanks for the heads up!

      Greg

  • Comments are closed.