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

前言
随着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  
 
 
从上面的例子中可以看出,GtkSimpleLayout并没有改变RubyGnome2程序的主框架,它只是一个扩充。
 
 
属性设置
在放置UI元件的时候,往往需要设置初始属性,或者要指定布局参数。GtkSimpleLayout用Hash来传递这些属性与参数,例如:

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

上面这个例子中,第一个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

这个时候,可以用"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

需要特殊对待的容器有:
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

Enjoy it :-)

(0)

相关推荐

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

    前言 随着RubyGnome2库越来越完善,以及ruby1.9的性能提升,用Ruby编写GUI程序渐渐从我的业余爱好转为我工作的一个重要部分.   用Ruby写程序确实很有乐趣,它可以让你的想法快速地以一种优雅的方式实现.本文介绍的一个gem就是一个例子,用很少的代码,实现很有趣的功能,让编写Ruby GUI程序变得轻松愉快.   RubyGnome2介绍   虽然我以前也曾经多次地介绍过RubyGnome2,但我还是想再一次地推荐RubyGnome2,它实在是使用Ruby编写GUI程序的首选.

  • win7下从ruby源代码编译安装的方法

    工作中需要在c++代码中嵌入ruby c api,然而在vs工程中编译失败,所以现在通过手动从源代码编译ruby寻找原因(之前使用rubyinstaller安装). 先从官网下载ruby 2.4.1 版本,https://www.ruby-lang.org/en/downloads/ 从安装指导可以看到,官方只提供了linux平台下的编译安装步骤,https://www.ruby-lang.org/en/documentation/installation/#building-from-sour

  • CentOS 7下配置Ruby语言开发环境的方法教程

    本文跟大家分享的是在CentOS 7下配置Ruby语言开发环境的方法教程,分享出来供大家参考学习,下面来看看详细的介绍: 安装Ruby 2.2 CentOS7存储库中的Ruby版本为2.0,但如果需要,可以使用RPM软件包安装2.2 1.添加CentOS SCLo软件集合存储库 [root@linuxprobe ~]# yum -y install centos-release-scl-rh centos-release-scl # set [priority=10] [root@linuxpr

  • Ruby面向对象编程中类与方法的基础学习

    打开类和猴子补丁 在Ruby中,类定义的方法和其他的语句没有任何区别,都是一行一行的执行下去的.如下例子: class Example def method_1 puts "method 1" end end class Example def method_2 puts "method 2" end end 本例中,当第一次定义Class Example的时候,还没有一个叫做Example的Class存在,因此,Ruby开始定义这个类,当后面在定义这个类时,Rub

  • openSUSE下的Ruby安装openssl出错解决方法

    最近把玩 Ruby,不错的说,很有感觉:在一台老机器上面装了 openSUSE 12.3,然后使用 rvm 安装了 ruby,其实任何好的发行版打包的 ruby 都不如使用 rvm 安装的 ruby,亲身感受(不服来辩). 然后,使用 gem 安装一些软件的时候,会出现如下的错误: 复制代码 代码如下: `no such file to load -- openssl (LoadError)` 原因很简单就是 ruby 的 openssl 支持模块没有安装,在 openSUSE 下面安装: 复制

  • Ruby面向对象编程中类的方法与类的扩展

    类方法 类方法其实质是生活在该类的单件类中的单件方法.其定义方法有三种,分别是: # 法一 def MyClass.a_class_method; end # 法二 class MyClass def self.anther_class_method; end end # 法三* class MyClass class << self def yet_another_class_method; end end end 其中第三种方法道出了,类方法的实质,特别记忆一下! 类扩展 类扩展通过向类的

  • windows下安装ruby与rails时遇到的问题总结

    前言 最近因为工作的需要,准备安装ruby on rails,在网上搜了下,步骤都类似,但实际安装过程中却碰到很多问题. 说明下:文章是按照我尝试的过程描述的.但最终是靠 运行 railsinstaller一键式安装包才成功的(第五段),因此前面的部分大家可以看看,但不用去尝试. 下面来看看详细的介绍吧: 一.首先要安装ruby 因为在windows下安装ruby,都是推荐下载rubyinstaller安装程序. 先进入ruby官网http://www.ruby-lang.org/en/down

  • 解决Alamofire库在iOS7下设置Head无效的问题

    同样的代码在iOS8下没有问题,iOS7就取不到数据(会包Cocoa Error 3840之类的错误),跟踪发现请求Head参数设置不管用,根据文字底部的参考改了一下兼容代码: 代码 private func getRequest(method: Method, _ URLString: URLStringConvertible, parameters: [String: AnyObject]? = nil) -> NSMutableURLRequest { let request = NSMu

  • 总结升级易语言支持库保证向下兼容性

    易语言支持库升级之后,要保证向下兼容性,主要是做到以下几点: 一:保证原有的易语言源程序(.e)能正常打开(兼容点1).正常编译(兼容点2).编译结果正确(兼容点3): 二:保证原有的易语言程序(.exe)能正常运行(兼容点4).运行结果正确(兼容点5). 这里说的"原有的易语言源程序"和"原有的易语言程序"是指,替换新版支持库文件之前,使用旧版支持库编写的易语言源程序,和使用该源程序编译生成的可执行程序. 本文主要就此问题结合具体情况进行分析和总结. 一,为支持库

  • Ruby中操作文件的方法介绍

    Ruby提供了一套完整的I/O相关的内核模块中实现方法.所有I/O方法来自IO类. 类IO提供了所有的基本方法,如 read, write, gets, puts, readline, getc 和 printf. 本章将涵盖所有可供在Ruby中使用的基本I/O功能.如需使用更多的功能,请参考Ruby的IO类. puts 语句: 在前面的章节中,你指定值的变量和然后使用声明 puts 输出. puts 把语句指示程序显示存储在变量值.这将添加一个新行,每行末尾写出(输出). 例子: #!/usr

随机推荐