Basic Method of Ruby GUI Programming under GTK with Ruby Gnome 2 Library

Time:2019-6-11

Preface
As the Ruby Gnome 2 library becomes more and more perfect and the performance of ruby 1.9 improves, writing GUI programs in Ruby has gradually improved from my hobby to an important part of my work.
 
It’s really fun to write programs in Ruby, which allows your ideas to be implemented quickly in an elegant way. This article introduces a gem as an example, with very little code, to achieve very interesting functions, so that writing Ruby GUI program becomes easy and pleasant.
 
Introduction to Ruby Gnome 2
 
Although I have introduced Ruby Gnome 2 many times before, I would like to recommend Ruby Gnome 2 again, which is really the first choice for writing GUI programs using Ruby.
 
Ruby Gnome2 is a ruby extension of the GTK + library. It encapsulates GTK + object model carefully in Ruby mode, retaining the naming method and meaning of GTK + API, so GTK + documents are also applicable to Ruby Gnome2 – although I think Ruby Gnome2 documents have done very well and there are few places to go back and learn from GTK documents.
 
Although GTK itself is written by C, it has a complete and well-designed object system, which enables its GUI components to be very flexible and freely combined to achieve a complex and powerful interface.
 
Because GTK attaches great importance to the flexibility of its object system, it is not easy to start programming with GTK. Most of the time, a function that seems very simple on the surface can only be realized in GTK by several bends. For example, to set a label font, Pango takes over all the font rendering business of GTK. There are many cases of “more than a few bends”, which really makes writing GTK programs less straightforward. But in return, the whole GTK system has become very flexible, with less cost to achieve powerful functions, in cross-platform, skin change, internationalization has a good performance.
 
Ruby Gnome2 inherits all the features of GTK, including its advantages and disadvantages.
 
GUI Layout
The layout of GTK program is flexible and there are many kinds of containers to choose from. In layout, most of them recommend relative location rather than absolute location, so that GUI programs can better adapt to different resolutions of the screen, but also conducive to a specific style of fine tune for the UI.
The most common containers are “boxes”, including “horizontal boxes” and “vertical boxes”. By placing visual UI components in boxes, different boxes can be combined and overlapped to construct the target layout.
 
In theory, any relative location layout can be built with boxes, but for the sake of that, GTK also provides more advanced containers like tables.
 
The box model is difficult for many people who have just started GTK programming. Even with visual layout tools such as Glade, beginners are often troubled by the box model. Visual layouts are best at fixed-location layouts. For relative location layout, it is often faster and more convenient to build an interface with code than with Glade.
 
However, there is a significant disadvantage of building an interface with code, that is, it is “not intuitive”, that is, it is difficult to see the UI layout from the code, which will cause trouble for later maintenance and change.
 
Some GUI libraries, such as Shose, use builder-style code to describe the UI, so that the UI layout can be visualized through the code, such as the following example from shooose.net:
 


Shoes.app { 
 stack(:margin => 4) { 
  button "Mice" 
  button "Eagles" 
  button "Quail" 
 } 
} 

 
 
In this way, we can see the UI layout from the code at once.
 
Builder-style code is very similar to HTML, and visual editors are not necessary for web interface designers familiar with HTML.
 
 
Ruby Gnome2 does not provide us with a builder-style layout, so the UI code writes like:
 


class MyWin < Gtk::Window 
 def initialize 
  super 
  vbox = Gtk::VBox.new 
  btn_mice = Gtk::Button.new 'Mice' 
  vbox.pack_start btn_mice 
  btn_eagles = Gtk::Button.new 'Eagles' 
  vbox.pack_start btn_eagles 
  btn_quail = Gtk::Button.new 'Quail' 
  vbox.pack_start btn_quail 
  add vbox 
 end 
end 

 
It’s hard to see the UI layout at once from the above code.
 
If you also build a builder-style layout for Ruby Gnome2, the code becomes:
 


class MyWin < Gtk::Window 
 
 def initialize 
  super 
  add my_layout 
 end 
 
 def my_layout 
  vbox do 
   button 'Mice' 
   button 'Eagles' 
   button 'Quail' 
  end 
 end 
 
end 

 
 
Well, this code is about the same as hose. You can see the UI layout from the code at a glance.
 
One of the functions of GtkSimpleLayout introduced in this article is to provide builder-style layout for Ruby Gnome 2.
 
GtkSimpleLayout Layout Layer
This simple layout was originally only 200 lines of code, and I often copied it directly into the project. Later, it gradually added some functions and found it more useful, so it was released to GitHub to generate gem, which is convenient for interested people to use.
 
Source: git://github.com/rickyzheng/GtkSimpleLayout.git
or:
gem source -a http://gems.github.com && gem install rickyzheng-GtkSimpleLayout
 
Here are the main functions and simple examples.
 
Provide Builder Style Layout
As described in the above example, GtkSimpleLayout brings builder-style layout capabilities to Ruby Gnome 2 by extending GtkSimpleLayout:: Base to the layout class, a complete example:
 


require 'gtk2' 
require 'simple_layout' 
 
class MyWin < Gtk::Window 
 include SimpleLayout::Base 
 def initialize 
  super 
  add my_layout 
  signal_connect('destroy') do 
   Gtk.main_quit 
  end 
 end 
 
 def my_layout 
  hbox do 
    label 'Hello, ' 
    button 'World !' 
   end 
 end 
end 

 
MyWin.new.show_all 
Gtk.main  
 
20151214173039560.png (266×58) 
As you can see from the example above, GtkSimpleLayout has not changed the main framework of Ruby Gnome2, it is only an extension.
 
 
Property Settings
When placing UI components, you often need to set initial properties or specify layout parameters. GtkSimpleLayout uses Hash to pass these attributes and parameters, such as:
 

vbox do 
 Button'sensitive = false',: sensitive = > false # Initially disabled 
 Button'expand space',: layout => [true, true] # Specifies that the button fills in the remaining space 
end

20151214173108985.png (437×62)

In the example above, the initial state of the first button is disable. The parameter “sensitive => false” is eventually converted to property settings: Gtk:: Button# sensitive = false. For Gtk:: Button, see the Ruby Gnome 2 API documentation or GTK documentation for which properties can be set. GtkSimpleLayout here is just a simple parameter conversion.
 
The second button’s “: layout => [true, true]” is a bit special. The “: layout” parameter is the reserved parameter of GtkSimpleLayout, which is converted to the parameter when the UI is placed in the container. In this example, the container is a VBox (Gtk:: VBox), and the default addition method is Gtk:: VBox pack_start. In this example, [true, true] will eventually be passed to pack_start, so the method and parameter that the button calls when it is added to the VBox are: “Gtk:: VBox pack_start (“button, true, true)”.
 
Therefore, to use GtkSimpleLayout, you need to be familiar with Ruby Gnome 2 components, container usage, and parameters. When you’re familiar with Ruby Gnome 2, using GtkSimpleLayout is very simple.
 
Batch Property Settings
In UI layout, it is often encountered that the same attributes should be set to a set of UI components, such as:


hbox do 
  button 'C', :layout => [false, false, 5] 
  button 'D', :layout => [false, false, 5] 
  button 'E', :layout => [false, false, 5] 
end 

20151214173136286.png (238×58)

At this time, “with_attr” can be used to simplify:


hbox do 
 with_attr :layout => [false, false, 5] do 
  button 'C' 
  button 'D' 
  button 'E' 
 end 
end  

 
Special containers
Some containers have special requirements when placing sub-components, such as Gtk:: HPaned, Gtk:: HPaned ADD1 () on the left and Gtk:: HPaned add2 () on the right. For such containers, GtkSimpleLayout should be treated in a special way, taking hpaned as an example:


hpaned do 
 area_first do 
  frame 'first area' 
 end 
 area_second do 
  frame 'second area' 
 end 
end

  20151214173231646.png (211×143)

Containers requiring special treatment are:
Hpaned/vpaned: Add child windows with area_first and area_second.
Table: Fill in the grid.
Noebook: Use page to add subpages.
 
Identify UI components
GtkSimpleLayout identifies UI components with the parameter “: ID =>?”, for example:


hbox do 
 button 'first', :id => :btn_first 
 button 'second', :id => :btn_second 
end 

 
After that, you can use the component () function to get the UI component:


my_first_button = component(:btn_first) 
my_second_button = component(:btn_second) 
 
... 
my_first_button.signal_connect('clicked') do 
 puts "first button clicked" 
end 
 
my_second_button.signal_connect('clicked') do 
 puts "second button clicked" 
end 

 
 
GtkSimpleLayout also provides expose_components() to automatically add all identified components as instance read attributes (getters) if it is troublesome:

Expo_components()# automatically adds two read attributes (getter), btn_first and btn_second. Such


... 
btn_first.signal_connect('clicked') do 
 puts "first button clicked" 
end 
 
btn_second.signal_connect('clicked') do 
 puts "second button clicked" 
end 

 
 
Automatic Event Response Mapping
GtkSimpleLayout provides you with automatic event response mapping if you suspect that it’s troublesome to explicitly call signal_connect to register events:

require 'gtk2' 
require 'simple_layout' 
 
class MyWin < Gtk::Window 
 include SimpleLayout::Base 
 def initialize 
  super 
  add my_layout 
  Register_auto_events() Register automatic event response mapping 
 end 
 
 def my_layout 
  hbox do 
   button "First', :btn_first 
   button "Second", :btn_second 
  end 
 end 
 
 # Event response function 
 def btn_first_on_clicked(*_) 
  puts "First button clicked" 
 end 
 
 # Event response function 
 def btn_second_on_clicked(*_) 
  puts "Second button clicked" 
 end 
 
 # Exit event response function 
 def self_on_destroy(*_) 
  Gtk.main_quit 
 end 
end

 
The last’self’refers to the host container.
 
UI grouping
Sometimes you want to group UI components so that you can control the same set of UI components, such as enabling or disabling the entire group. GtkSimpleLayout allows you to specify UI groups when laying out.
GtkSimpleLayout’s UI grouping rules are as follows:
By default, named containers (i.e., incoming: ID parameters) automatically create a group of their subcomponents, the group name is the container name.
If the container passes in: gid=>?? parameter, then a group is created for the subcomponent to which it belongs.
Allow multiple containers: GID with the same name, in which case the subcomponents belong to the same group.
Groups can be used to explicitly group UIs, and groups can be seen as virtual containers.
Use component_children (group_name) to get the UI group.
 
Since examples of UI grouping are long and are not listed here, please refer to the examples/group.rb file in the source code.
 
 
Separation of UI from logical code
Because GtkSimpleLayout potentially forces users to separate the interface code from the logical processing (or event response) code, the hierarchy of the entire program becomes clearer. For programs with more interface elements, layout can be easily partitioned, because the result of layout is still a container, which can be put into other containers to form a more complex interface.
 
Since GtkSimpleLayout does not change the program structure of Ruby Gnome2, you can choose to use GtkSimpleLayout partly or completely in your program. Although the examples provided in this article are static layout, because GtkSimpleLayout is a storage code to build the UI, you can pass variables into the layout, dynamically layout and dynamically generate the UI, while still maintaining the “visualization” of the UI code.
 
 
Interested people can look at the GtkSimpleLayout implementation code, but only 300 lines, which is the charm of Ruby.
 
Finally, paste a code example of the interface part of the calculator. Can you see the UI layout from the code?


require 'gtk2' 
require 'simple_layout' 
 
class MyWin < Gtk::Window 
 include SimpleLayout::Base 
 def initialize 
  super 
  add my_layout 
  signal_connect('destroy') do 
   Gtk.main_quit 
  end 
 end 
 
 def my_layout 
  vbox do 
   with_attr :border_width => 3 do 
    hbox do 
     entry :id => :ent_input, :layout => [true, true, 5] 
    end 
    hbox do 
     frame do 
      label 'M', :set_size_request => [20, 20] 
     end 
     hbutton_box do 
      button 'Backspace' 
      button 'CE' 
      button 'C' 
     end 
    end 
    hbox do 
     vbutton_box do 
      button 'MC' 
      button 'MR' 
      button 'MS' 
      button 'M+' 
     end 
     with_attr :layout => [true, true] do 
      number_and_operators_layout 
     end 
    end 
   end 
  end 
 end 
 
 def number_and_operators_layout 
  vbox do 
   [ ['7', '8', '9', '/', 'sqt'], 
    ['4', '5', '6', '*', '%'], 
    ['1', '2', '3', '-', '1/x'], 
    ['0', '+/=', '.', '+', '=']].each do |cols| 
    hbox :layout => [true, true] do 
     cols.each do |txt| 
      button txt, :set_size_request => [20, 20], :layout => [true, true] 
     end 
    end 
   end 
  end 
 end 
 
end 

 
MyWin.new.show_all 
Gtk.main 

20151214173350854.png (307×219)

Enjoy it 🙂