Skip to content

Hello RuGUI! Developing desktop applications using Ruby/GTK

You can check out a portuguese version of this post here

RuGUI

So you want to start out playing with GTK and Ruby but don’t know how to start? Fear not, we’re here to help you. There are a lot of examples of “Hello, World!” applications with Ruby/GTK. Well, both Ruby and GTK are great choices for building desktop applications. The first one is a dynamic language with amazing features like closures, and of course, with an extensive built-in library. The second one is a widely used GUI framework, with lots of helpful widgets and also a great library for printing, displaying videos, playing sounds, etc. Combine these two elements and you have Ruby/GTK, a great framework… uhmmm… framework?

Let me ask you a few questions. Suppose you’re starting a new desktop application in Ruby/GTK, a really big one, with lots of use cases and small details. How would you layout your directory structure? What if you needed to customize the style of a button, or change the background color of a TextView? What if your application is a kind of server, and you want some message displayed every time an user connects to it? What if you wanted to change the status bar message, and also add an icon for the client in an IconView?

Of course you could do this yourself… Or you could use RuGUI which gives you:

  • A MVC approach to desktop applications
  • A ready to use directory layout similar to what you’d see on a Ruby on Rails application
  • An easier way of customizing widgets than in normal Ruby/GTK applications
  • An implementation of the Observer pattern
  • ActiveSupport—hey, you could say that this is a requirement/dependency, but I think this is more like a feature since you’ll end up having a better ruby API with ActiveSupport :)

Let’s see it in action with a simple Hello World application.

Generating your application skeleton

RuGUI already creates this Hello World application for us. In fact this is the starting point for every application you’ll ever make with RuGUI. Once it is installed as a gem go to a directory of your choice and type in a terminal:

rugui helloworld

This will generate an application in {YOUR_DIRECTORY}/helloworld. The directory structure will be like this:

pastas_en

To run the application, enter the helloworld directory and type:

./bin/helloworld

You could also have typed:

rake run

Or even, if you must:

ruby app/main.rb

The application should look like this:

Hello World Application

If you click in the button you should see the top label changing. How did this all worked?

Hello World step by step

Let’s analyse the application step by step:

When you run the application, in any way (all three ways of starting the application end up doing the same thing), the app/main.rb script gets called. Here are the content of that file:

#! /usr/bin/ruby#

# You can run this application by running this file.
# Do not modify it, unless you know what you are doing.

require File.join(File.dirname(__FILE__), '..', 'config', 'environment')

main_controller = MainController.new
main_controller.run

As you can see, the first thing we do is to load the config/environment.rb file. This will initialize the RuGUI application with the default environment—development. You could’ve specified a different environment by setting the RUGUI_ENV environment variable. This initialization process is very similar to the Rails initialization process, it was mainly copied and adapted from it.

Next we instantiate the MainController and call its method run. Simple isn’t?

Now to the MainController. What is it again? Unlike web applications, all desktop applications have a concept of a main loop. Basically, the application is set up before it is actually displayed. It then enters an infinite loop, which is responsible for displaying widgets and firing events as they happen (button clicks, keys pressed, network data, printing operations, etc). These events may alter the widgets visual, change data, or any other thing you might want them to do.

So RuGUI’s MainController is the actual programming starting point of your application. Something like the root of your application. Better yet, all other controllers are children of this MainController, or children of controllers which are themselves children of this MainController. This is getting a little complicated, but in time you will get it.

In this example we only have one controller, and it is the MainController. We didn’t create another controller for simplicity. Here is its code:

class MainController < RuGUI::BaseMainController
  # Add your stuff here.

  def setup_views
    register_view :main_view
  end

  def on_main_window_delete_event(widget, event)
    quit
  end
end

It set up the main view and add a handler for the main window delete event, which is used to quit the application. This event handler is configured in the main_view.glade file. Every controller can observe events configured for widgets in glade files of all views registered for it.

The main view uses glade file to simplify the creation an positioning of widgets. We could’ve created the widgets by hand, but we would’ve lost many features like the automatic connection of event handlers, and we would have to write a lot more code than we did:

class MainView < ApplicationView
  use_glade
  # Add your stuff here.

  def on_hello_button_clicked(widget)
    puts "Hello button clicked."
    self.message_label.text = "You clicked me!"
  end
end

Here we’ve put an event handler for the clicked event of the hello_button widget. It prints a message in the console and change the text of the message_label widget. A few notes here:

  • All named widgets created in glade files have read accessors for them.
  • Since we are using default naming and directory conventions we didn’t need to specify the builder file for the view, but we could have done it if we wanted. It was merely deducted from the class name (MainView => main_view)
  • The view itself is also an observer for event handlers of its widgets. The clicked event handler for the hello_button could have been declared in the controller, but we’ve decided to declare it here to show this.

This is basically all there is to it!

But hey, I can do this with fewer lines!

Of course you can, here it is:

#! /usr/bin/ruby

require 'libglade2'

class MainViewGlade
  include GetText
  attr :glade

  def initialize(path_or_data, root = nil, domain = nil, localedir = nil, flag = GladeXML::FILE)
    bindtextdomain(domain, localedir, nil, "UTF-8")
    @glade = GladeXML.new(path_or_data, root, domain, localedir, flag) {|handler| method(handler)}
  end

  def on_main_window_delete_event(widget, arg0)
    Gtk.main_quit
  end

  def on_hello_button_clicked(widget)
    puts "Hello button clicked."
    @glade['message_label'].text = "You clicked me!"
  end
end

# Main program
if __FILE__ == $0
  # Set values as your own application.
  PROG_PATH = "main_view.glade"
  PROG_NAME = "Hello World!"
  MainViewGlade.new(PROG_PATH, nil, PROG_NAME)
  Gtk.main
end

But then, you will surely get a big headache when you need to add more functionality to the application. RuGUI tries to help you by letting you separate your application in well defined layers, each with his own responsabilities. This way its easier to make your application evolve without having maintainence problems.

Getting excited? Try it out now!

{ 3 } Comments

  1. Dmitry Rocha | January 30, 2009 at 3:06 pm | Permalink

    I’m excited with the project…

    I’m waiting for more.

    Thank you.

  2. David | December 29, 2010 at 8:21 am | Permalink

    I just discovered RuGUI and I love the rails concept. I’m checking it out for my desktop app right now! I’m looking forward to a robust RuGUI community. Are there anymore tutorials to come?

  3. Waseem Besada | February 11, 2012 at 5:35 pm | Permalink

    Hi!

    I’m wondering why I can’t run the helloworld example. I get the following error:

    $ ./helloworld
    (eval):1: warning: redefining `object_id’ may cause serious problems
    (eval):1: warning: redefining `__send__’ may cause serious problems
    /var/lib/gems/1.9.1/gems/rugui-1.6.0/lib/rugui/property_changed_support.rb:49:in `included’: undefined method `class_inheritable_accessor’ for RuGUI::BaseController:Class (NoMethodError)
    from /var/lib/gems/1.9.1/gems/rugui-1.6.0/lib/rugui/property_observer.rb:37:in `include’
    from /var/lib/gems/1.9.1/gems/rugui-1.6.0/lib/rugui/property_observer.rb:37:in `included’
    from /var/lib/gems/1.9.1/gems/rugui-1.6.0/lib/rugui/base_controller.rb:6:in `include’
    from /var/lib/gems/1.9.1/gems/rugui-1.6.0/lib/rugui/base_controller.rb:6:in `’
    from /var/lib/gems/1.9.1/gems/rugui-1.6.0/lib/rugui/base_controller.rb:5:in `’
    from /var/lib/gems/1.9.1/gems/rugui-1.6.0/lib/rugui/base_controller.rb:1:in `’
    from :29:in `require’
    from :29:in `require’
    from /var/lib/gems/1.9.1/gems/activesupport-3.2.1/lib/active_support/dependencies.rb:251:in `block in require’
    from /var/lib/gems/1.9.1/gems/activesupport-3.2.1/lib/active_support/dependencies.rb:236:in `load_dependency’
    from /var/lib/gems/1.9.1/gems/activesupport-3.2.1/lib/active_support/dependencies.rb:251:in `require’
    from /var/lib/gems/1.9.1/gems/rugui-1.6.0/lib/rugui.rb:38:in `’
    from :33:in `require’
    from :33:in `rescue in require’
    from :29:in `require’
    from /home/ewaseem/myProjects/myRuby/helloworld/config/boot.rb:59:in `load_rugui’
    from /home/ewaseem/myProjects/myRuby/helloworld/config/boot.rb:54:in `load_initializer’
    from /home/ewaseem/myProjects/myRuby/helloworld/config/boot.rb:40:in `run’
    from /home/ewaseem/myProjects/myRuby/helloworld/config/boot.rb:13:in `boot!’
    from /home/ewaseem/myProjects/myRuby/helloworld/config/boot.rb:81:in `’
    from :29:in `require’
    from :29:in `require’
    from /home/ewaseem/myProjects/myRuby/helloworld/config/environment.rb:5:in `’
    from :29:in `require’
    from :29:in `require’
    from /home/ewaseem/myProjects/myRuby/helloworld/app/main.rb:8:in `’
    from ./helloworld:4:in `load’
    from ./helloworld:4:in `’

    I appreciate an hints to get it work.

    /Waseem

{ 3 } Trackbacks

  1. Intelitiva Blog » Olá RuGUI! | February 2, 2009 at 6:18 pm | Permalink

    [...] Este post é a tradução do post intitulado Hello RuGUI! [...]

  2. [...] you have already read this article you may wonder: “Ok, I can do a Hello World application with some new framework, so [...]

  3. [...] to improve productivity, favoring convention over configuration, DRY and MVC patterns. « Hello RuGUI! Developing desktop applications using Ruby/GTK Getting your feet wet with RuGUI [...]

Post a Comment

Your email is never published nor shared. Required fields are marked *

Powered by WP Hashcash