欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

借助RubyGnome2库进行GTK下的Ruby GUI编程的基本方法

程序员文章站 2022-04-09 20:17:28
前言 随着rubygnome2库越来越完善,以及ruby1.9的性能提升,用ruby编写gui程序渐渐从我的业余爱好转为我工作的一个重要部分。   用rub...

前言
随着rubygnome2库越来越完善,以及ruby1.9的性能提升,用ruby编写gui程序渐渐从我的业余爱好转为我工作的一个重要部分。
 
用ruby写程序确实很有乐趣,它可以让你的想法快速地以一种优雅的方式实现。本文介绍的一个gem就是一个例子,用很少的代码,实现很有趣的功能,让编写ruby gui程序变得轻松愉快。
 
rubygnome2介绍
 
虽然我以前也曾经多次地介绍过rubygnome2,但我还是想再一次地推荐rubygnome2,它实在是使用ruby编写gui程序的首选。
 
rubygnome2是gtk+库的一个ruby扩展。它对gtk+的对象模型仔细地用ruby的方式进行封装,保留了gtk+ api命名方式和含义,因此gtk+的文档对于rubygnome2也是适用的---尽管我认为rubygnome2的文档已经做得非常不错了,需要回去借鉴gtk文档的地方实在不多。
 
虽然gtk本身是由c编写的,但它有一套完整的精心设计对象体系,使得它的gui元件可以非常灵活的*组合,以实现复杂的,功能强大的界面。
 
由于gtk非常重视它的对象体系的灵活性,因此刚开始使用gtk编程并不容易。很多时候表面上看起来很简单的一个功能,在gtk里面却要绕几个弯才能实现。例如要设置一个label的字体,要通过pango来实现,pango接管了gtk的所有字体渲染事务....这种“多绕几个弯”的情况很多,它确实使得编写gtk程序不那么直接了当。但换来的是整个gtk系统变得非常灵活,以较少的代价实现强大的功能,在跨平台,换肤,国际化上面都有很好的表现。
 
rubygnome2继承了gtk的所有特点,包括优点和缺点。
 
gui布局
gtk程序的布局很灵活,有许多种容器可选择。在布局的时候,大多数都是推荐相对位置而不是绝对位置,这样gui程序可以更好的适应不同分辨率的屏幕,也有利于特定风格对ui的fine tune。
最常见的容器就是“盒子”,包括“水平盒子”和“垂直盒子”。将可视的ui元件放入“盒子”,不同的“盒子”互相组合叠放就可以构建出目标布局。
 
理论上用盒子就可以构建任何相对位置布局,但是为了方面,gtk还提供了像table这样的更高级的容器。
 
盒子模型对于许多刚开始gtk编程的人觉得很难适应,即使用了可视化布局工具例如glade,初学者也往往被盒子模型困扰。可视化布局器最擅长的是固定位置布局。对于相对位置布局,很多时候用代码构建界面比用glade反而来的快捷方便。
 
但是用代码构建界面有一个显著的缺点,那就是“不直观”,即很难从代码中看出ui布局,这样会给后期维护以及变更带来麻烦。
 
有一些gui库如shose,用builder风格的代码来描述ui,使得ui布局可以通过代码形象地体现出来,如以下这个例子来自shooose.net:
 

shoes.app { 
 stack(:margin => 4) { 
  button "mice" 
  button "eagles" 
  button "quail" 
 } 
} 

 
 
这样,我们就可以从代码中一下子看出ui布局了。
 
builder风格的代码和html很类似,对于熟悉html的web界面设计者来说,可视化的编辑器并没有多大的必要。
 
 
rubygnome2没有为我们提供builder方式的布局,因此ui代码写起来就像:
 

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 

 
从上面的代码中很难一下子看出ui布局。
 
如果也为rubygnome2构建一个builder风格的布局器,那么代码就会变成:
 

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 

 
 
嗯,这个代码就和shose差不多了,可以从代码中一眼看出ui布局。
 
本文所介绍的gtksimplelayout其功能之一就是为rubygnome2提供builder风格的布局器。
 
gtksimplelayout布局器
这个简单的布局器原先只有200行不到的代码,我经常是直接拷贝到项目中使用。后来逐渐添了些功能,觉得它变得更有用了,于是便发布到github生成gem,方便感兴趣者使用。
 
source: git://github.com/rickyzheng/gtksimplelayout.git
or:
gem source -a http://gems.github.com && gem install rickyzheng-gtksimplelayout
 
以下是主要功能介绍以及简单例子。
 
提供builder风格布局
正如上面的例子中所介绍的,gtksimplelayout为rubygnome2带来了builder风格的布局功能,只需要为布局的类扩展gtksimplelayout::base即可,一个完整的例子:
 

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  
 
借助RubyGnome2库进行GTK下的Ruby GUI编程的基本方法 
从上面的例子中可以看出,gtksimplelayout并没有改变rubygnome2程序的主框架,它只是一个扩充。
 
 
属性设置
在放置ui元件的时候,往往需要设置初始属性,或者要指定布局参数。gtksimplelayout用hash来传递这些属性与参数,例如:
 

vbox do 
 button 'sensitive = false', :sensitive => false # 初始为disable状态 
 button 'expand space', :layout => [true, true] # 指定这个button填充剩余空间 
end 

借助RubyGnome2库进行GTK下的Ruby GUI编程的基本方法

上面这个例子中,第一个button的初始状态为disable。 ":sensitive => false"这个参数最终被转换成属性设置:gtk::button#sensitive=false,至于gtk::button有那些属性可以设置,请参阅rubygnome2 api文档或gtk文档。gtksimplelayout在这里只是作一个简单参数的转换而已。
 
第二个button的":layout => [true, true]"有点特殊。":layout" 参数是gtksimplelayout的保留参数,它会被转换成当这个ui被放入容器时候的参数。这个例子中,容器是vbox(gtk::vbox),默认的加入方法是gtk::vbox#pack_start,这个例子中的[true, true] 最终会被传递到pack_start,因此这个button在被加入vbox的时候调用的方法以及参数是:"gtk::vbox#pack_start( button, true, true)"。
 
因此,要使用gtksimplelayout,就首先要熟悉rubygnome2的各个元件,容器的用法,以及参数。当你熟悉了rubygnome2以后,用gtksimplelayout就会非常简单。
 
批量属性设置
在ui布局的时候,经常碰到要对一组ui元件设置相同的属性的情况,例如:

hbox do 
  button 'c', :layout => [false, false, 5] 
  button 'd', :layout => [false, false, 5] 
  button 'e', :layout => [false, false, 5] 
end 

借助RubyGnome2库进行GTK下的Ruby GUI编程的基本方法

这个时候,可以用"with_attr"来简化:

hbox do 
 with_attr :layout => [false, false, 5] do 
  button 'c' 
  button 'd' 
  button 'e' 
 end 
end  

 
特殊容器
有些容器的放置子元件的时候有 特殊要求,例如gtk::hpaned,左边子窗口要用gtk::hpaned#add1()来添加,右边的用gtk::hpaned#add2()。对于这种容器,gtksimplelayout要特别对待,就以hpaned为例:

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

  借助RubyGnome2库进行GTK下的Ruby GUI编程的基本方法

需要特殊对待的容器有:
hpaned/vpaned : 用area_first和area_second来添加子窗口。
table : 用grid来填充格子。
nodebook : 用page来添加子页。
 
标识ui元件
gtksimplelayout用":id => ??"这个参数为ui元件进行标识,例如:

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

 
之后,可以用component()函数取得这个ui元件:

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还提供了expose_components()用于自动将所有已标识的元件添加为实例读属性(getter):

expose_components() # 将自动添加btn_first和btn_second这两个读属性(getter)。  

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

 
 
自动事件响应映射
如果你嫌显式调用signal_connect来注册事件麻烦,那么gtksimplelayout为你提供了自动事件响应映射的功能:

require 'gtk2' 
require 'simple_layout' 
 
class mywin < gtk::window 
 include simplelayout::base 
 def initialize 
  super 
  add my_layout 
  register_auto_events() # 注册自动事件响应映射 
 end 
 
 def my_layout 
  hbox do 
   button "first', :btn_first 
   button "second", :btn_second 
  end 
 end 
 
 # 事件响应函数 
 def btn_first_on_clicked(*_) 
  puts "first button clicked" 
 end 
 
 # 事件响应函数 
 def btn_second_on_clicked(*_) 
  puts "second button clicked" 
 end 
 
 # 退出事件响应函数 
 def self_on_destroy(*_) 
  gtk.main_quit 
 end 
end 

 
最后那个'self‘是指宿主容器。
 
ui分组
有时候你希望对ui元件进行分组,这样就可以对同一组的ui元件进行控制,如使能或禁止整个组。gtksimplelayout允许你在布局的时候指定ui组。
gtksimplelayout的ui分组规则如下:
默认情况下,已命名的容器(即传入了:id参数)自动对自己所属的子元件建立一个组,组名就是容器明。
如果容器传入:gid=>??参数,则以此名称为所属子元件建立组。
允许多个容器的:gid名字相同,这种情况下所属子元件将归为同一个组。
可以用“group”来显式对ui分组,group可以看作是一个虚拟的容器。
用component_children(group_name)来获取ui组。
 
由于ui分组的例子比较长不在此列出,请参阅源码中的examples/group.rb文件
 
 
ui与逻辑代码分离
由于gtksimplelayout潜在地迫使使用者分离界面代码和逻辑处理(或事件响应)代码,使得整个程序的层次结构更加清晰。对于界面元件比较多的程序,可以很方便的分区进行layout,因为layout的结果还是容器,这个容器又可以放入其他容器组合成更复杂的界面。
 
由于gtksimplelayout并不改变rubygnome2的程序结构,你可以选择在你的程序中部分或全部使用gtksimplelayout。虽然本文所提供的例子都是静态布局,但由于gtksimplelayout是存代码构建ui,因此你完全可以在布局的时候传入变量,进行动态布局和动态生成ui,而仍然保持ui代码的“可视化”。
 
 
有兴趣者可以看看gtksimplelayout实现的代码,不过300行而已,这就是ruby的魅力。
 
最后,贴上一个计算器的界面部分的代码例子,你能从代码中看出ui布局么?

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 

借助RubyGnome2库进行GTK下的Ruby GUI编程的基本方法

enjoy it :-)