首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 开源 FAQ 第二书店 博文视点 程序员
频道: 研发 数据库 中间件 信息化 视频 .NET Java 游戏 移动 服务: 人才 外包 培训
    图书品种:235680
       
热门搜索: ASP.NET Ajax Spring Hibernate Java

12.3  FXRuby(FOX)

FOX系统也是较新的技术,它关注的是速度和平台间的一致性。其高度一致性部分源于其自依赖性;它不是本征GUI的封装,这与有些系统的实现不同。

FOX的核心是基于C++的,但可以为任何语言(如Ruby)创建绑定。由于一开始固有的面向对象特征,因此能够与Ruby很好地交互,且扩展性非常高。

FOX没有Tk和GTK+那样应用广泛,但在Ruby程序员中很受欢迎,部分原因是优秀的Ruby绑定FXRuby(主页为http://fxruby.org)。FXRub是Lyle Johnson开发的,他在该库的文档编写和维护方面花了很多时间。这几年来他还提供了出色的技术支持,也为本节的编写提供了宝贵帮助。

12.3.1  概述

FXRuby是FOX C++库的Ruby绑定,提供了很多类,可用于开发功能齐备的GUI应用程序。尽管FOX表示Free Objects for X,但被移植到各种平台,包括MS Windows。Lyle Johnson开发了FOX的Ruby绑定,并完成了将FOX C++工具包移植到Windows的大部分工作。FOX是Jeroen van der Zijp在CFD Research Corporation的支持下开发的。

FOX窗口部件提供了一种现代界面风格(look and feel),可与本征GUI(包括MS Windows)媲美,其工具包提供了很多其他窗口部件库没有的特性。

FOX类库很简洁,功能很强大,熟悉其他GUI库的程序员很容易掌握。其API的平台无关性不明显。由于FOX本身是使用C++实现的,因此FXRuby API的有些方面仍受C++静态性和编程习惯的影响(例如,使用枚举和位运算)。

FOX的核心简化机制是消息/目标模式。FOX对象是FXObject或其子类的实例,用户定义的FOX对象必须继承这些类之一。每个FXObject实例都可以发送和接收消息;在运行阶段,发送的消息与特定目标相关联。

FOX中的消息在内部用消息类型、消息标识符和消息数据表示。很多FOX类都使用通用的消息定义集合,因此窗口部件之间能够互操作。

处理消息后,消息处理程序应返回1;如果消息没有得到处理,则返回0。FOX不显式地将位处理的消息转发给其他窗口部件。FOX根据返回值来判断是否需要更新GUI。FXRuby应用程序可根据返回值来转发未处理的消息,从而实现责任链(Chain of Responsibility)模式。

FOX中的另一种简化机制是自动更新模式。隐式的FOX事件循环包括一个更新阶段,在这个阶段,FOX对象能够处理更新消息。更新处理程序通常根据一些应用程序数据的新状态来修改窗口部件的外观。例如,在本章后面的程序清单12.9中,一个按钮根据应用程序的一个变量来更新其激活/非激活状态。

12.3.2  一个简单的窗口应用程序

下面是一个简单的FXRuby应用程序,它与前面的Tk和GTK+应用程序类似:

require 'fox16'      # Use the FOX 1.6 bindings

include Fox

application = FXApp.new

main = FXMainWindow.new(application, "Today's Date")

str = Time.now.strftime("&Today is %B %d, %Y")

button = FXButton.new(main, str)

button.connect(SEL_COMMAND) { application.exit }

application.create

main.show(PLACEMENT_SCREEN)

application.run

这个应用程序足以说明两个基本的FXRuby类:FXApp和FXMainWindow。应用程序必须创建并初始化一个FXApp实例,然后才能使用其他FOX类。FXMainWindow是FXTopWindow的子类,FOX中的每个窗口部件都是“窗口”。FXTopWindow是顶级窗口——直接显示在屏幕上的窗口。复杂的FXRuby应用程序通常会创建FXMainWindow的子类,并在初始化期间创建自己的窗口部件。

FXMainWindow的构造函数接受一个FXApp实例作为第一个参数,第二个参数是窗口的标题。默认情况下,将在屏幕中央放置FXMainWindow的一个实例,它包含FXTopWindow的所有标准窗口特征,因此,其大小可调整,将显示标题栏,并在标题栏中包含最小化、最大化和关闭按钮。

主窗口的decorations属性可显式地指定要包含的每个特征。例如,可以禁止调整窗口的大小:

main = FXMainWindow.new(application, "Today's Date")

main.decorations = DECOR_TITLE | DECOR_CLOSE

以C++方式对上述decoration选项执行按位OR运算,因此,窗口的标题栏将包含标题和一个关闭按钮。

这个简单应用程序的主窗口中有一个窗口部件——FXButton的实例,它显示包含当前日期的文本:

str = Time.now.strftime("&Today is %B %d, %Y")

button = FXButton.new(main, str)

FXButton的构造函数的第一个参数是将包含窗口部件的父窗口,这里为主窗口;第二个参数是按钮的文本。

接下来的一行演示了如何使用connect方法将按钮同代码块关联起来:

button.connect(SEL_COMMAND) { application.exit }

这段代码指出,该按钮发送命令消息(类型为SEL_COMMAND的消息)时,将调用应用程序的exit方法。

该应用程序的其他代码行演示了FXApp实例和FXMainWindow实例之间常见的“交配仪式”:

application.create

main.show(PLACEMENT_SCREEN)

application.run

所有FXRuby应用程序都应包含这样的代码行,以创建应用程序、显示FXMainWindow和运行处理GUI事件的FXApp事件循环。show过程的PLACEMENT_SCREEN参数指定主窗口显示在屏幕的什么位置。其他有趣的参数包括PLACEMENT_CURSOR(将主窗口放在鼠标所在的位置)、PLACEMENT_OWNER(将窗口放在其容器的中央)和PLACEMENT_MAXIMIZED(将窗口最大化,使其与屏幕一样大)。

12.3.3  使用按钮

前面介绍过FXRuby中的简单按钮处理,下面来看一种稍复杂的用法。

按钮并非只能显示简短的文本字符串,例如,按钮还可以显示由换行符分开的多行文本:

text = "&Hello, World!\n" +

        "Do you see multiple lines of text?"

FXButton.new(self, text)

注意,在“Hello, World!”中,H字母前有一个&。在按钮的文本中,该符号指定与单击按钮等效的快捷键。

按钮还能够显示多种文件格式的图像,例如:

text = "&Hello, World!\n" +

        "Do you see the icon?\n" +

        "Do you see multiple lines of text?"

icon = File.open("some_icon.gif", "rb") do |file|

            FXGIFIcon.new(app, file.read)

    end

FXButton.new(self, text, icon)

程序清单12.9演示了FOX工具包提供的用于更新GUI状态的机制。

程序清单12.9  在FOX中更新GUI状态

require 'fox16'

include Fox

class TwoButtonUpdateWindow < FXMainWindow

  def initialize(app)

    # Invoke base class initialize first

    super(app, "Update Example", nil, nil,

           DECOR_TITLE | DECOR_CLOSE)

    # First button

    @button_one = FXButton.new(self, "Enable Button 2")

    @button_one_enabled = true

    # Second button

    @button_two = FXButton.new(self, "Enable Button 1")

    @button_two.disable

    @button_two_enabled = false

# Hook up the message handlers

@button_one.connect(SEL_COMMAND, method(:onCommand))

@button_two.connect(SEL_COMMAND, method(:onCommand))

@button_one.connect(SEL_UPDATE,  method(:onUpdate))

@button_two.connect(SEL_UPDATE,  method(:onUpdate))

  end

  def onCommand(sender, sel, ptr)

    # Update the application state

    @button_one_enabled = !@button_one_enabled

    @button_two_enabled = !@button_two_enabled

  end

  def onUpdate(sender, sel, ptr)

    # Update the buttons based on the application state

    @button_one_enabled ?

      @button_one.enable : @button_one.disable

    @button_two_enabled ?

      @button_two.enable : @button_two.disable

  end

end

application = FXApp.new

main = TwoButtonUpdateWindow.new(application)

application.create

main.show(PLACEMENT_SCREEN)

application.run

在这个例子中,添加了两个按钮到主窗口中。这里还是使用connect方法将按钮的SEL_COMMAND消息与一段代码关联起来,但这次代码在方法中而不是代码块中:

@button_one.connect(SEL_COMMAND, method(:onCommand))

这个例子还使用了一种新消息。通过使用消息类型SEL_UPDATE,让GUI窗口部件互相独立,同时独立于应用程序的代码。这个例子表明,两个按钮彼此不知道对方,一个按钮通过发送消息给维护按钮状态的处理程序,改变另一个按钮的状态。

12.3.4  使用文本框

FOX提供了一些很有用的文本输入功能,下面的例子演示了如何使用FXTextField来编辑单行文本。可使用一些选项来限制文本的格式,TEXTFIELD_PASSWD用于区分密码文本,TEXTFIELD_REAL确保文本满足科学计数法的语法,TEXTFIELD_INTEGER确保文本满足整数的语法:

simple = FXTextField.new(main, 20, nil, 0,

                              JUSTIFY_RIGHT|FRAME_SUNKEN|

                              FRAME_THICK|LAYOUT_SIDE_TOP)

simple.text = "Simple Text Field"

passwd = FXTextField.new(main, 20, nil, 0,

                              JUSTIFY_RIGHT|TEXTFIELD_PASSWD|

                              FRAME_SUNKEN|FRAME_THICK|

                              LAYOUT_SIDE_TOP)

passwd.text = "Password"

real = FXTextField.new(main, 20, nil, 0,

                           TEXTFIELD_REAL|FRAME_SUNKEN|

                           FRAME_THICK|LAYOUT_SIDE_TOP|

                           LAYOUT_FIX_HEIGHT, 0, 0, 0, 30)

real.text = "1.0E+3"

int = FXTextField.new(main, 20, nil, 0, TEXTFIELD_INTEGER|

                          FRAME_SUNKEN|FRAME_THICK|

                          LAYOUT_SIDE_TOP|LAYOUT_FIX_HEIGHT,

                          0, 0, 0, 30)

int.text = "1000"

下面的例子演示了一种使用对话框输入文本的简单方式。同样,可以根据使用的方法将文本限制为整数或科学计数法:

puts FXInputDialog.getString("initial text",

                                   self, "Text Entry Dialog",

                                   "Enter some text:", nil)

puts FXInputDialog.getInteger(1200, self,

                                   "Integer Entry Dialog",

                                   "Enter an integer:", nil)

puts FXInputDialog.getReal(1.03e7, self,

                                "Scientific Entry Dialog",

                                "Enter a real number:", nil)

为节省篇幅,这里没有列出整个应用程序。当然,FOX工具包要求初始化对话框窗口才能显示它们。

12.3.5  使用其他窗口部件

下面这个例子演示了如何在FXRuby应用程序使用菜单和菜单栏。注意,FXMenuCommand实例也遵循FOX的消息/目标模式,这在前面针对按钮介绍过:

require 'fox16'

include Fox

application = FXApp.new

main = FXMainWindow.new(application, "Simple Menu")

menubar = FXMenuBar.new(main, LAYOUT_SIDE_TOP |

                            LAYOUT_FILL_X)

filemenu = FXMenuPane.new(main)

quit_cmd = FXMenuCommand.new(filemenu, "&Quit\tCtl-Q")

quit_cmd.connect(SEL_COMMAND) { application.exit }

FXMenuTitle.new(menubar, "&File", nil, filemenu)

application.create

main.show(PLACEMENT_SCREEN)

application.run

在这个例子中,FXMenuBar和FXMenuPane都直接显示在FXMainWindow对象中。选项LAYOUT_SIDE_TOP和LAYOUT_FILL_X使菜单栏显示在父窗口顶部,并将其宽度拉伸到与窗口相同。菜单命令的文本&Quit\tCtl-Q指定Alt+Q为热键,Ctrl+Q为快捷键。按Alt+F再按Alt+Q相当于单击菜单File后再单击菜单命令Quit,按Ctrl+Q等价于这些步骤。

FXTopWindow类提供了一个可使主窗口最小化的方法。下面三行代码将另一个菜单命令添加到File菜单中,该菜单命令将主窗口最小化。

FXMenuCommand.new(filemenu, "&Icon\tCtl-I") do |cmd|

  cmd.connect(SEL_COMMAND) { main.minimize }

end

这个例子演示了创建菜单命令时可使用的另一种技术。如果不需要保存对新建菜单命令的引用,可在调用FXMenuCommand.new时指定代码块,并在该代码块中完成所有的窗口部件初始化,这种技术适用于任何内置的FOX类。

程序清单12.10中的例子演示了单选按钮的用法。

程序清单12.10  FOX单选按钮

require 'fox16'

include Fox

class RadioButtonHandlerWindow < FXMainWindow

  def initialize(app)

    # Invoke base class initialize first

    super(app, "Radio Button Handler", nil, nil,

           DECOR_TITLE | DECOR_CLOSE)

    choices = [ "Good", "Better", "Best" ]

    group = FXGroupBox.new(self, "Radio Test Group",

              LAYOUT_SIDE_TOP |

              FRAME_GROOVE |

              LAYOUT_FILL_X)

    choices.each do |choice|

      FXRadioButton.new(group, choice,

          nil, 0,

          ICON_BEFORE_TEXT |

          LAYOUT_SIDE_TOP)

    end

  end

end

application = FXApp.new

main = RadioButtonHandlerWindow.new(application)

application.create

main.show(PLACEMENT_SCREEN)

application.run

单选按钮组是GUI应用程序中用于表示相关而互斥选项集的标准工具。在这个例子中,选项被定义为一个字符串数组:

choices = [ "Good", "Better", "Best" ]

添加一个FXGroupBox实例到主窗口中,用于提供可视化提示:单选按钮属于同一组,然后将单选按钮添加到该分组框中(每个按钮表示一种选择)。然而,FXGroupBox容器实际上并没有采取任何措施来确保单选按钮的“单选行为”。如果运行这个程序,将发现可同时选择多个单选按钮。

要确保这个例子中单选按钮的单选行为,有很多种方式,但在FOX应用程序中,最佳方式是使用数据目标。FXDataTarget类定义一种特殊的FOX对象,可用做数据值的占位符。与其他FOX对象一样,FXDataTarget实例也能够发送和接收消息。

程序清单12.11是程序清单12.10修改版,演示了如何使用数据目标。

程序清单12.11  FOX单选按钮和数据目标

require 'fox16'

include Fox

class RadioButtonHandlerWindow < FXMainWindow

  def initialize(app)

    # Invoke base class initialize first

    super(app, "Radio Button Handler", nil, nil,

            DECOR_TITLE | DECOR_CLOSE)

    choices = [ "Good", "Better", "Best" ]

default_choice = 0

    @choice = FXDataTarget.new(default_choice)

    group = FXGroupBox.new(self, "Radio Test Group",

                                LAYOUT_SIDE_TOP |

                                FRAME_GROOVE |

                                LAYOUT_FILL_X)

    choices.each_with_index do |choice, index|

      FXRadioButton.new(group, choice,

          @choice, FXDataTarget::ID_OPTION+index,

          ICON_BEFORE_TEXT |

          LAYOUT_SIDE_TOP)

    end

  end

end

application = FXApp.new

main = RadioButtonHandlerWindow.new(application)

application.create

main.show(PLACEMENT_SCREEN)

application.run

在这个例子中,@choice是一个FXDataTarget实例,其值为当前选定选项的整数索引。该数据目标被初始化为默认值零,对应于choices数组中的Good元素。

对于每个新建的单选按钮,其目标都被设置为该数据目标,每个单选按钮的消息标识符都为FXDataTarget::ID_OPTION加上适当的值。如果重新运行这个程序,现在应该看到单选按钮的单选行为。

列表窗口部件FXList也可添加到窗口中,并在其中填充一些行。LIST_BROWSESELECT选项确保每次只能选择一项,开始时选中的是第一项。如果用LIST_SINGLESELECT取代该选项,则允许选择零或一项。使用该选项时,开始不选中任何项:

@list = FXList.new(self, nil, 0,

                       LIST_BROWSESELECT |

                       LAYOUT_FILL_X)

@names = ["Chuck", "Sally", "Franklin", "Schroeder",

            "Woodstock", "Matz", "Lucy"]

@names.each { |name| @list.appendItem(name) }

注意,数组的插入运算符可用做appendItem方法的快捷方式,可用下面的代码行替换前一个例子中的最后一行:

@names.each { |name| @list << name }

程序清单12.12是完整的例子。消息是在主窗口中处理的:显示被单击的项。如果像前面讨论那样使用LIST_SINGLESELECT选项,则确定单击是选择还是取消选择至关重要。

程序清单12.12  FXList窗口部件

require 'fox16'

include Fox

class ListHandlerWindow < FXMainWindow

  def initialize(app)

    # Invoke base class initialize first

    super(app, "List Handler", nil, nil,

            DECOR_TITLE | DECOR_CLOSE)

    @list = FXList.new(self, nil, 0,

                           LIST_BROWSESELECT |

                           LAYOUT_FILL_X)

    @list.connect(SEL_COMMAND) do |sender, sel, pos|

  puts pos.to_s + " => " + @names[pos]

end

    @names = ["Chuck", "Sally", "Franklin",

                 "Schroeder", "Woodstock",

                 "Matz", "Lucy"]

    @names.each { |name| @list << name }

  end

end

application = FXApp.new

main = ListHandlerWindow.new(application)

application.create

main.show(PLACEMENT_SCREEN)

application.run

如果将LIST_BROWSESELECT选项改为LIST_EXTENDEDSELECT,则可以在列表中同时选择多项:

@list = FXList.new(self, nil, 0, LIST_EXTENDEDSELECT | LAYOUT_FILL_X)

可重新定义消息处理程序以显示所有选定的项,必须遍历所有的列表项才能找到选定的项:

@list.connect(SEL_COMMAND) do |sender, sel, pos|

  puts "Clicked on " + pos.to_s + " => " +

           @names[pos]

  puts "Currently selected:"

  @list.each do |item|

    if item.selected?

        puts "    " + item.text

    end

  end

end

可通过修改FXList实例的numVisible属性来改变可见的列表项数。使用另一个窗口部件FXListBox可只显示当前选定的项。FXListBox与FXList类似,但有几点不同。构造函数的参数相同,如下面的代码所示。注意,FXListBox只允许选定一项,因此将忽略LIST_EXTENDEDSELECT等选项:

@list_box = FXListBox.new(self, nil, 0, LIST_BROWSESELECT | LAYOUT_FILL_X)

@names = ["Chuck", "Sally", "Franklin", "Schroeder",

            "Woodstock", "Matz", "Lucy"]

@names.each { |name| @list_box << name }

可以FXDialogBox的子类的方式定义对话框,然后便可以使用这个类创建模态(modal)和非模态(nonmodal)对话框,但模态对话框与其容器交互的方式不同于非模态对话框。

模态指的是窗口或对话框在获得服务前,不允许用户访问应用程序的其他部分,也就是说,软件要求用户注意该对话框。另一方面,非模态实体允许将其焦点切换到其他实体。

下面的例子定义了一个模态对话框类和一个非模态对话框类,其中模态对话框类使用预定义的消息ID_CANCEL和ID_ACCEPT,而非模态对话框类使用预定义的消息ID_HIDE。

使用熟悉的FXTopWindow.show方法显示非模态对话框,而模态对话框是在其事件循环中显示的,它将抢占应用程序的事件循环。这是使用FXDialogBox.execute方法实现的。在完整的程序清单中可以看到,execute方法的返回值取决于传递给应用程序的stopModal方法(该方法用于结束模态对话框的事件循环)的值。在下面的示例程序中,返回值为1,表明用户单击了对话框中的Accept按钮:

modal_btn.connect do

  dialog = ModalDialogBox.new(self)

  if dialog.execute(PLACEMENT_OWNER) == 1

    puts dialog.text

  end

end

非模态对话框与应用程序的其他窗口同时运行,应用程序应在需要时向对话框查询感兴趣的值。宣布新值可用的一种机制是,在对话框中使用Apply按钮将与应用程序相关的消息发送给主窗口。下面的例子使用定时器,这是另一项有趣的FXRuby特性。定时器到期后,将发送一条消息给主窗口。该消息的处理程序(如下面的代码所示)向对话框查询新值,然后重新将定时器间隔设置为1秒:

def onTimer(sender, sel, ptr)

  text = @non_modal_dialog.text

  unless text == @previous

    @previous = text

    puts @previous

  end

  getApp().addTimeout(1000, method(:onTimer))

end

程序清单12.13是一个完整的模态对话框和非模态对话框示例。

程序清单12.13  模态对话框和非模态对话框

require 'fox16'

include Fox

class NonModalDialogBox < FXDialogBox

  def initialize(owner)

    # Invoke base class initialize function first

    super(owner, "Test of Dialog Box",

            DECOR_TITLE|DECOR_BORDER)

    text_options = JUSTIFY_RIGHT | FRAME_SUNKEN |

                      FRAME_THICK | LAYOUT_SIDE_TOP

    @text_field = FXTextField.new(self, 20, nil, 0,

                     text_options)

    @text_field.text = ""

    layout_options = LAYOUT_SIDE_TOP | FRAME_NONE |

                         LAYOUT_FILL_X | LAYOUT_FILL_Y |

                         PACK_UNIFORM_WIDTH

    layout = FXHorizontalFrame.new(self, layout_options)

    options = FRAME_RAISED | FRAME_THICK |

                LAYOUT_RIGHT | LAYOUT_CENTER_Y

    hide_btn = FXButton.new(layout, "&Hide", nil, nil, 0,

                         options)

    hide_btn.connect(SEL_COMMAND) { hide }

  end

  def text

    @text_field.text

  end

end

class ModalDialogBox < FXDialogBox

  def initialize(owner)

    # Invoke base class initialize function first

    super(owner, "Test of Dialog Box",

           DECOR_TITLE|DECOR_BORDER)

    text_options = JUSTIFY_RIGHT | FRAME_SUNKEN |

                      FRAME_THICK | LAYOUT_SIDE_TOP

    @text_field = FXTextField.new(self, 20, nil, 0,

                     text_options)

    @text_field.text = ""

    layout_options = LAYOUT_SIDE_TOP | FRAME_NONE |

                         LAYOUT_FILL_X | LAYOUT_FILL_Y |

                         PACK_UNIFORM_WIDTH

    layout = FXHorizontalFrame.new(self, layout_options)

    options = FRAME_RAISED | FRAME_THICK |

                LAYOUT_RIGHT | LAYOUT_CENTER_Y

    cancel_btn = FXButton.new(layout, "&Cancel", nil,

                    self, 0, options)

    cancel_btn.connect(SEL_COMMAND) do

        app.stopModal(self, 0)

        hide

    end

    accept_btn = FXButton.new(layout, "&Accept", nil,

                    self, 0, options)

    accept_btn.connect(SEL_COMMAND) do

        app.stopModal(self, 1)

        hide

    end

  end

  def text

    @text_field.text

  end

end

class DialogTestWindow < FXMainWindow

  def initialize(app)

    # Invoke base class initialize first

    super(app, "Dialog Test", nil, nil,

           DECOR_ALL, 0, 0, 400, 200)

    layout_options = LAYOUT_SIDE_TOP | FRAME_NONE |

                         LAYOUT_FILL_X | LAYOUT_FILL_Y |

                         PACK_UNIFORM_WIDTH

    layout = FXHorizontalFrame.new(self, layout_options)

    button_options = FRAME_RAISED | FRAME_THICK |

                         LAYOUT_CENTER_X | LAYOUT_CENTER_Y

  nonmodal_btn = FXButton.new(layout, "&Non-Modal Dialog...", nil,

    nil, 0, button_options)

  nonmodal_btn.connect(SEL_COMMAND) do

    @non_modal_dialog.show(PLACEMENT_OWNER)

end

    modal_btn = FXButton.new(layout, "&Modal Dialog...",     nil,

       nil, 0, button_options)

    modal_btn.connect(SEL_COMMAND) do

    dialog = ModalDialogBox.new(self)

    if dialog.execute(PLACEMENT_OWNER) == 1

      puts dialog.text

    end

end

    getApp.addTimeout(1000, method(:onTimer))

    @non_modal_dialog = NonModalDialogBox.new(self)

  end

  def onTimer(sender, sel, ptr)

    text = @non_modal_dialog.text

    unless text == @previous

      @previous = text

      puts @previous

    end

    getApp.addTimeout(1000, method(:onTimer))

  end

  def create

    super

    show(PLACEMENT_SCREEN)

  end

end

application = FXApp.new

DialogTestWindow.new(application)

application.create

application.run

在FXRuby中,当遇到较长时间的计算时,应将当前的鼠标变为等待鼠标,计算结束后再恢复到原来的形状。应用程序类FXApp有两个方便的方法,可用于修改鼠标的形状而不必记住原来的鼠标形状,这两个方法是beginWaitCursor和endWaitCursor。在代码块中调用beginWaitCursor时,它将确保代码块退出时调用endWaitCursor:

getApp.beginWaitCursor do

  # Perform time-consuming operation here...

end

12.3.6  其他说明

FOX工具包中还有很多其他的窗口部件和特性,如树窗口部件、可停靠的工具栏、工具提示、状态栏和选项卡等。较高级的GUI特性包括在应用程序和数据目标之间进行拖拽,从而将应用程序数据与窗口部件连接起来。FOX还包括支持跨平台编程的非图形特性,如FXRegistry类。

消息可用于将应用程序与其环境连接起来——使用信号和基于输入的消息。操作系统信号以及输入和输出都将导致向FOX对象发送消息。

FOX工具包包含支持最常见的图像格式和OpenGL 3D API的窗口部件,这并非支持3D功能的空头支票。FOX C++工具包被应用于很多工程应用程序。

无论从哪种角度看,FXRuby工具包都是一个功能强大而灵活的环境,近年来在Ruby社区中越来越受欢迎。这个工具包的功能在快速地变化和扩展,有关Ruby绑定的最新信息,请参阅http://fxruby.org。

查看所有评论(0)条】

最近评论



正在载入评论列表...
热点评论