GUI Applications with Shoes

Share this article

shoes-icon

If you’ve been coding in Ruby for any length of time, you’ve probably at least heard of Shoes. Shoes is a DSL / GUI toolkit originally developed by the famed _why The Lucky Stiff. It’s also the basis for the Hackety Hack program that helps users learn Ruby and GUI programming.

You can find the official website here.

A simple app in Shoes looks like this:

# hello_world.rb
require 'green_shoes'

Shoes.app do
  para "Hello World"
end

Shoes make creating GUIs pretty darn easy. This tutorial covers the fundamentals and shows some examples of things that can be created with it.

The code in this tutorial is available on github.

Putting Your Shoes On

One of the confusing things about Shoes is that it comes in a few flavors.

  • Red Shoes – the original Shoes platform
  • Blue Shoes – QT implementation
  • Purple Shoes – JRuby/SWT implementation
  • Green Shoes – pure Ruby/GTK2 implementation
  • Shoes4 – the next version of Shoes

As of this writing, if you install the shoes gem and try to use it, you will get the following message:

Sorry, this gem currently does nothing. Team Shoes is working on Gemifying Shoes, and this is just a placeholder until then.

The Shoes github project has not been updated in quite a while, so it looks like Team Shoes decided gemifying Shoes wasn’t at the top of their priority list. Part of the reason is likely to be the fact that Shoes is not just a GUI library, it’s an app platform. It even comes with its own Ruby interpreter.

blue_shoes looks like it never made it out of the early stages of development. Looking through the code, a lot of the methods are unimplemented, so you probably want to stay away from this one.

purple_shoes was created by the same person as green\_shoes. It’s probably the second best alternative to Red Shoes. I tested several of the cross-platform examples, and they seemed to work. However, it has not been updated in a couple of years, so your mileage may vary. You will need JRuby if you want to try purple_shoes.

green_shoes is a GTK2 flavor of Shoes. It seems to be the most recently updated and most implemented alternative to Red Shoes. Isn’t exactly the same, but it’s close enough if you just want to get a taste without installing Red Shoes.

Green Shoes has a page on the differences between Red Shoes and Green Shoes. In addition, you will notice a lot of “Note: Green Shoes doesn’t support…” in the manual. If you are are serious about using Shoes, you will likely want to see you can get Red Shoes to work, or plan on heavily modifying Green Shoes.

Team Shoes currently appears to be working on a rewrite called Shoes4. The code is available on github. Unfortunately, It doesn’t look like it’s ready for use quite yet.

Getting Started

I had trouble getting the Red Shoes platform installed, so I will be using the Green Shoes gem in this tutorial. Since Green Shoes uses GTK2, make sure you have the GTK2 libraries installed before trying to run anything.

First, go ahead and grab the green_shoes gem:

gem install green_shoes

The structure of a Shoes app looks like this:

Shoes.app do
  element/slot(args) do
    element/slot(args) do
      ...etc
    end
  end  
end

In the example at the beginning of the article, “para” created a paragraph element with the content “Hello World.”

# hello_world.rb
require 'green_shoes'

Shoes.app do
  para "Hello World"
end

In the Shoes DSL, GUI elements are created in place using element creation methods. This is in contrast to the standard practice of instantiating windows, instantiating layouts, instantiating widgets, adding the widgets to the layouts, and adding the layouts to the windows. Shoes does this underneath the hood.

It’s possible to store elements in variables so that they can be referenced elsewhere in the app.

Shoes.app do
  @paragraph = para "This is a paragraph."
  @button = button("change it") do
    @paragraph.text = "Now it says something else."
  end
end

The variables in this tutorial are all instance variables (prefixed with ‘@’) to avoid confusing them with Shoes DSL methods.

Stacks and Flows

Shoes organizes its elements in two ways it calls slots.

  • stacks – vertical
  • flows – horizontal
# stacks_flows.rb

require 'green_shoes'

Shoes.app do

  stack do
    para "stack item 1", width: 100
    button "stack item 2"  
  end

  flow do
    para "flow item 1", width: 100
    button "flow item 2"
  end
end

Something worth pointing out is that, in Green Shoes, TextBlocks (para, caption, etc) take up the full width unless a width is specified. This is true even if a TextBlock is in a flow.

Stacks and flows can have a margin set to separate the elements.

# margins.rb
require 'green_shoes'

Shoes.app do

  caption "No margin set:"

  stack do
    para "stack item 1", width: 100
    button "stack item 2" 
  end

  flow do
    para "flow item 1", width: 100
    button "flow item 2"
  end

  caption "Margin: 10"

  stack(margin: 10) do
    para "stack item 1", width: 100
    button "stack item 2" 
  end

  flow(margin: 10) do
    para "flow item 1", width: 100
    button "flow item 2"
  end
end

Sometimes the desired layout is achieved by putting a flow inside of a stack and vice versa.

# flow_inside_stack.rb
require 'green_shoes'

Shoes.app do

  stack do
    para "stack item 1", width: 100

    flow do
      para "flow item 1", width: 100
      button "flow item 2"
    end

    button "stack item 2" 
  end
end

Responding to User Interaction

As expected in Ruby, Shoes uses blocks for handling events like button clicks or text changes.

# event_handling.rb
# Interactive Shoes elements take an event handler as a block
require 'green_shoes'

Shoes.app do
  stack(margin: 10) do
    @edit_box = edit_box do |e|
      @copy_box.text = @edit_box.text
    end  

    @copy_box = para "what you type in the edit box goes here"

    @button = button("obfuscate") do
      ob_bytes = @copy_box.text.each_byte.map { |b| b+1 }
      @copy_box.text = ob_bytes.map { |b| b.chr}.join
    end
  end
end

Making a YARV Instruction Viewer

Shoes’ basic text box is created with #edit_box. You can read or set EditBox#text. Here is an app that takes Ruby code and shows the virtual machine instructions associated with it. Note: This example will not work if you use Purple Shoes.

# yarv_viewer.rb
# Show YARV instructions for given Ruby code
require 'green_shoes'

Shoes.app(width: 900, height: 550) do
  stack(margin: 10) do
    flow(margin: 10) do
      caption "Ruby Code", width: 400
      caption "YARV Instructions", width: 400
    end
    flow(margin: 10) do
      @code = edit_box(width: 400, height: 400)
      @yarv = edit_box(width: 400, height: 400) 
    end
    flow(margin: 10) do
      @compile_button = button("compile") do
        @yarv.text = RubyVM::InstructionSequence.compile(@code.text).disasm
      end
    end
  end
end

Using Shoes’ Progress Bar

Shoes provides a barebones progress bar. It has the value #fraction which ranges from 0.0 to 1.0. One of the things that separates Progress from the other elements is that it appears to need to be placed manually, pixel-wise, rather than simply fitting in a slot.

# downloader.rb
# Basic downloader with progress-bar
require 'green_shoes'
require 'open-uri'

Shoes.app(width: 350) do
  title "Downloader"
  stack(margin: 10) do

    # user input
    flow do
      @url_line = edit_line
      @download_button = button("download") do
        url = @url_line.text 
        uri = URI.parse(url)
        filename = File.basename(uri.path)

        Thread.new do
          open(filename, 'wb') do |f|
            f.write(open(url, 
              :content_length_proc => @content_length_proc,
              :progress_proc => @progress_proc
            ).read)
          end
        end
      end
    end

    # labels
    caption "File Size:" 
    @expected_bytes = para ""
    caption "Downloaded:"
    @current_bytes = para ""
    caption "Progress:"
    @progress_bar = progress left: 10, top: 300

    # open-uri event handlers
    @content_length_proc = lambda do |content_length|
        @content_length = content_length
        @expected_bytes.text = content_length
    end

    @progress_proc = lambda do |bytes|
      @current_bytes.text = bytes
      @progress_bar.fraction = bytes / @content_length.round(1)
    end
  end
end

Ruby’s #open method accepts a hash that can specify two event handlers that can be used to determine progress.

  1. :content_length_proc – provides access to the size of the file being downloaded
  2. :progress_proc – provides access to the number of bytes downloaded so far

These event handlers (lambdas, in this case) are used to set @progress_bar.fraction.

Wrapping CLI Tools

One possible use for Shoes is creating simple wrappers for command-line tools like ImageMagick’s convert.

# image_converter.rb
# Basic image converter using ImageMagick
require 'green_shoes'
require 'gtk2'

Shoes.app do
  title "--Image Converter--"

  caption "Image Path:"
  @path_line = edit_line

  button("select image") do
    dialog = Gtk::FileChooserDialog.new(
            "Select Image",
            nil,
            Gtk::FileChooser::ACTION_OPEN,
            nil,
            [Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
            [Gtk::Stock::OPEN, Gtk::Dialog::RESPONSE_ACCEPT])

    if dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
      @path_line.text = dialog.filename
    end
    dialog.destroy
  end

  caption "Choose a new image format:"
  list_box items: ["jpg", "png", "gif"] do |list|
    @conversion_type = list.text
  end

  button("convert") do
    @file_path = @path_line.text
    @filename = File.basename(@file_path, '.*')
    @conversion_path = "#{File.dirname(@file_path)}/#{@filename}.#{@conversion_type}"
    if @file_path != @conversion_path
      Thread.new do
        @status.text = "converting..."
        system("convert #{@file_path} #{@conversion_path}")
        @status.text = ""
      end
    else
      alert("The image is already in that format")
    end
  end
  @status = para ""
end

Although Shoes doesn’t provide particular essentials, it’s sometimes possible to take them from other libraries. In this example, I have borrowed GTK’s FileChooserDialog to make it easier for the user to specify images. This isn’t very clean, but green_shoes uses gtk2 anyway, so in this case it’s not so bad. Note: This will not work with Purple Shoes.

Drawing and Animation

Shoes takes inspiration from animation toolkits like Processing in its drawing and animation facilities.

# drawing.rb
# Show how to draw basic shapes in Shoes
require 'green_shoes'

Shoes.app do
  background darkgray
  fill darkblue
  arrow 10, 10, 100
  fill red
  rect 10, 100, 100
  fill yellow
  oval 10, 200, 50
  strokewidth(2)
  line 10, 300, 100, 300
end

One of the unexpected things about Shoes is that colors can be specified without using symbols or strings. For example, you would use darkblue and not :darkblue. It’s also possible to specify colors using #rgb and hexadecimal color code strings like ‘#FF1B77’.

Shoes provides the #animate method for moving elements over time. It takes one argument which is the frames per second of the animation. You’re not going to get Processing -or certainly not Actionscript- levels of control, but it’s enough to play around.

# animation.rb
# Shows how to animate a bouncing ball
require 'green_shoes'

Shoes.app do
  fill blue
  @gravity = 0.05
  @velocity = 0.0
  @ball_radius = 60
  @ball = oval(top: 10, 
              left: width / 2 - @ball_radius, 
              radius: @ball_radius)
  button("drop ball") do
    animate(60) do
      @velocity += @gravity
      @ball.top += @velocity 
      if @ball.top > height - @ball_radius * 2
        @ball.top = height - @ball_radius * 2
        @velocity = @velocity * -0.8
      end
    end
  end
end

If you have never written any animation code before, the position set after the boundary check at the end might not be intuitive. Essentially, once the “ball” crosses the boundary (the “ground”), the condition is true. Even after the velocity is reversed, the condition is still true, so the velocity will simply be reversed again…and again, causing the ball to jitter around the boundary until it runs out of energy. The edge of the ball is set at the boundary to prevent this from happening and to maintain the illusion that the ball is bouncing against it. A seemingly obvious solution to this problem would be to use the == operator instead, but it is extremely unlikely that the edge of the ball is going to be exactly on the boundary on any given frame.

Conclusion

Shoes brings some good ideas and is perfect for small, toy projects. Unfortunately, if you want to do anything serious with it, some DIY will likely be involved for a few reasons:

  • Development efforts have been split between various flavors, and there isn’t a Red Shoes gem at this time.
  • Shoes was never intended to be a complete replacement for a standard GUI library. It was mainly created as an educational tool.
  • At least with Green Shoes, error reporting seems broken. No matter what happened, I only ever saw a generic GLib error. Debugging won’t be easy.

Shoes has a lot of features that were not covered in this tutorial. If you would like to learn more, be sure to check out the following resources:

Frequently Asked Questions (FAQs) about GUI Applications with Shoes

What is Shoes and why is it used in GUI applications?

Shoes is a GUI toolkit that is used to build desktop applications. It is written in Ruby, a high-level, interpreted programming language. Shoes is known for its simplicity and ease of use, making it a popular choice for beginners who are learning to create GUI applications. It provides a straightforward way to build windows, buttons, and other elements commonly found in desktop applications. Shoes also supports advanced features like animation and networking, making it a versatile tool for a wide range of applications.

How do I install Shoes for Ruby?

Shoes can be installed by downloading the appropriate package for your operating system from the official Shoes website. Once downloaded, you can install it like any other software. For Ruby, you need to have Ruby installed on your system. If you’re using a package manager like RubyGems, you can install Shoes by running the command ‘gem install shoes’.

How do I create a simple window using Shoes?

Creating a simple window in Shoes is straightforward. Here’s a basic example:

Shoes.app do
title "My First Shoes App"
end
This code creates a new Shoes application with a window that has the title “My First Shoes App”.

How can I add buttons and other elements to my Shoes application?

Shoes provides a simple way to add buttons, text boxes, and other elements to your application. Here’s an example of how to add a button:

Shoes.app do
button "Click me" do
alert "Button clicked!"
end
end
In this code, a button labeled “Click me” is added to the application. When the button is clicked, an alert box with the message “Button clicked!” is displayed.

Can I use Shoes to create complex GUI applications?

Yes, Shoes is capable of creating complex GUI applications. While it is known for its simplicity, Shoes also supports advanced features like animation, networking, and multi-threading. This makes it a versatile tool that can be used for a wide range of applications, from simple desktop apps to complex networked applications.

How do I handle user input in a Shoes application?

Shoes provides several ways to handle user input. For example, you can use the ‘edit_line’ method to create a text box that the user can type into. You can then use the ‘text’ method to retrieve the text entered by the user.

Can I use Shoes to create applications for different operating systems?

Yes, Shoes applications can be run on Windows, macOS, and Linux. This makes it a great choice for creating cross-platform applications.

How do I debug a Shoes application?

Debugging a Shoes application can be done using the standard Ruby debugging tools. You can also use the ‘debug’ method provided by Shoes to print debugging information to the console.

Can I use Shoes with other Ruby libraries?

Yes, Shoes can be used with other Ruby libraries. However, some libraries may not be compatible with Shoes due to differences in how they handle graphics and events.

Where can I find more resources to learn about Shoes?

The official Shoes website is a great place to start. It provides a comprehensive guide to getting started with Shoes, as well as detailed documentation on the various features and methods provided by Shoes. You can also find a number of tutorials and examples online that demonstrate how to use Shoes to create different types of applications.

Robert QuallsRobert Qualls
View Author

Robert is a voracious reader, Ruby aficionado, and other big words. He is currently looking for interesting projects to work on and can be found at his website.

shoes
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week