15、对话框的使用与自定义开发

对话框的使用与自定义开发

1. 颜色对话框选择颜色

当需要用户选择或修改颜色值时,可以使用 FXColorDialog 。由于其设置较少,很容易将颜色对话框集成到应用程序中。以下是示例代码:

dialog = FXColorDialog.new(self, "Choose Background Color")
dialog.rgba = FXRGB(255, 0, 0) # initialize color to red
if dialog.execute != 0
  self.backColor = dialog.rgba
end

如果不想使用默认的初始颜色(黑色),可以在显示对话框之前通过设置 rgba 属性来初始化颜色值。

颜色对话框包含五个选项卡,每个选项卡使用不同的颜色模型显示当前选择的颜色:
- 第一个选项卡显示一个用于调整颜色的色相、饱和度和值(HSV)组件的拨号盘。
- 第二个选项卡显示一组用于设置颜色的红、绿、蓝和透明度(RGBA)组件的滑块。
- 第三个选项卡显示一组用于设置颜色的 HSV 组件的滑块。
- 第四个选项卡显示一组用于设置颜色的青色、品红色、黄色和黑色(CMYK)组件的滑块。
- 最后一个选项卡显示一个颜色名称列表。

此外,颜色对话框左侧有一个“颜色选择器”按钮,可让你从屏幕上的任何位置选择颜色作为新颜色,底部还有一组预定义的颜色样本。

2. 字体对话框选择字体

FXFontDialog 用于让用户选择字体。其使用模式通常是初始化设置,显示对话框让用户交互,然后在用户完成选择后检索选择信息。以下是示例代码:

dialog = FXFontDialog.new(self, "Choose a Font")
dialog.fontSelection = button.font.fontDesc
if dialog.execute != 0
  new_font = FXFont.new(app, dialog.fontSelection)
  new_font.create
  button.font = new_font
end

第一步是通过现有字体的 fontDesc 属性提取初始字体设置(作为 FXFontDesc 对象),并使用该字体描述来初始化 FXFontDialog fontSelection 属性。不过,这一步并非严格必要,如果不提前初始化 fontSelection 属性,字体对话框会使用一些默认设置。

用户完成与字体对话框的交互并做出选择后,需要从字体对话框的 fontSelection 属性中检索字体描述,并使用它来构造一个新的 FXFont 对象。在将新字体分配给小部件之前,必须调用 create() 方法。

3. 消息框提醒用户

与其他标准对话框相比,消息框非常简单,仅提供与用户的最基本交互。例如,以下代码显示一个警告消息框:

FXMessageBox.warning(
  self,
  MBOX_OK,
  "Buyer Beware",
  "All Sales are Final!"
)

FXMessageBox 通常使用类方法(如 warning() )一次性构造和显示。如果消息框只包含一个终止按钮(如上述示例中的“OK”按钮),则无需检查方法的返回值。如果消息框包含多个终止按钮,则需要检查方法的返回值以确定用户点击了哪个按钮。例如:

answer = FXMessageBox.question(
  self,
  MBOX_YES_NO,
  "Just one question...",
  "Is it safe?"
)
if answer == MBOX_CLICKED_YES
  ask_again()
end

FXMessageBox 类还提供了 information() error() 类方法来显示相应类型的消息。

4. 创建自定义对话框

如果应用程序的需求可以通过标准对话框满足,最好使用标准对话框,以提供一致和熟悉的用户界面。但对于许多应用程序,可能需要开发一个或多个自定义对话框来处理特定的功能。下面以创建一个典型的首选项对话框为例进行说明。

创建自定义对话框的第一步是子类化 FXDialogBox

class PreferencesDialog < FXDialogBox
  def initialize(owner)
    super(owner, "Preferences", DECOR_TITLE|DECOR_BORDER|DECOR_RESIZE)
  end
end

接下来,需要在对话框底部添加一排终止按钮(如“OK”和“Cancel”按钮):

def add_terminating_buttons
  buttons = FXHorizontalFrame.new(self,
    :opts => LAYOUT_FILL_X|LAYOUT_SIDE_BOTTOM|PACK_UNIFORM_WIDTH)
  FXButton.new(buttons, "OK",
    :target => self, :selector => FXDialogBox::ID_ACCEPT,
    :opts => BUTTON_NORMAL|LAYOUT_RIGHT)
  FXButton.new(buttons, "Cancel",
    :target => self, :selector => FXDialogBox::ID_CANCEL,
    :opts => BUTTON_NORMAL|LAYOUT_RIGHT)
end

由于先添加了“OK”按钮并传入了 LAYOUT_RIGHT 布局提示,该按钮将靠在水平框架的右侧。随后添加的“Cancel”按钮也将靠在剩余空间的右侧,即出现在“OK”按钮的左侧。如果希望“OK”按钮在左侧,“Cancel”按钮在右侧,可以交换这两个语句的顺序。

FXDialogBox 类定义了两个消息标识符 ID_ACCEPT ID_CANCEL ,可以直接从“OK”和“Cancel”按钮发送到对话框以关闭它。如果用户点击“OK”按钮,它将向对话框对象发送一个类型为 SEL_COMMAND 、标识符为 ID_ACCEPT 的消息,对话框收到该消息后将隐藏自身,并确保最初启动对话框的 execute() 调用返回非零值。如果收到 ID_CANCEL 消息,则 execute() 将返回零。

然后,使用 FXTabBook 显示首选项设置:

tabbook = FXTabBook.new(self, :opts => LAYOUT_FILL)
basics_tab = FXTabItem.new(tabbook, " Basics ")
basics_page = FXVerticalFrame.new(tabbook,
  :opts => FRAME_RAISED|LAYOUT_FILL)
contact_tab = FXTabItem.new(tabbook, " Contact ")
contact_page = FXVerticalFrame.new(tabbook,
  :opts => FRAME_RAISED|LAYOUT_FILL)
extras_tab = FXTabItem.new(tabbook, " Extras ")
extras_page = FXVerticalFrame.new(tabbook,
  :opts => FRAME_RAISED|LAYOUT_FILL)

为了处理首选项数据,使用 FXDataTarget 类:

@prefs = {
  :first_name => FXDataTarget.new,
  :last_name => FXDataTarget.new,
  :street => FXDataTarget.new,
  :city => FXDataTarget.new,
  :state => FXDataTarget.new,
  :zip_code => FXDataTarget.new
}

构建与“Basics”选项卡关联的第一页内容:

def construct_basics_page(page)
  form = FXMatrix.new(page, 2,
    :opts => MATRIX_BY_COLUMNS|LAYOUT_FILL_X)
  FXLabel.new(form, "First:")
  FXTextField.new(form, 20,
    :target => @prefs[:first_name], :selector => FXDataTarget::ID_VALUE,
    :opts => TEXTFIELD_NORMAL|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN)
  FXLabel.new(form, "Last:")
  FXTextField.new(form, 20,
    :target => @prefs[:last_name], :selector => FXDataTarget::ID_VALUE,
    :opts => TEXTFIELD_NORMAL|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN)
  FXLabel.new(form, "Street Address:")
  FXTextField.new(form, 20,
    :target => @prefs[:street], :selector => FXDataTarget::ID_VALUE,
    :opts => TEXTFIELD_NORMAL|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN)
  FXLabel.new(form, "City:")
  FXTextField.new(form, 20,
    :target => @prefs[:city], :selector => FXDataTarget::ID_VALUE,
    :opts => TEXTFIELD_NORMAL|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN)
  FXLabel.new(form, "State:")
  states = FXListBox.new(form,
    :target => @prefs[:state], :selector => FXDataTarget::ID_VALUE,
    :opts => (LISTBOX_NORMAL|FRAME_SUNKEN|
    LAYOUT_FILL_X|LAYOUT_FILL_COLUMN))
  FXLabel.new(form, "Zip Code:")
  FXTextField.new(form, 10,
    :target => @prefs[:zip_code], :selector => FXDataTarget::ID_VALUE,
    :opts => TEXTFIELD_NORMAL|LAYOUT_FILL_COLUMN)
end

每个 FXTextField 小部件和 FXListBox 都使用 @prefs 哈希中的一个数据目标。小部件将从这些数据目标中的数据获取初始设置,并且每当用户更改小部件中的设置时,数据目标的值将自动更新。

要将此对话框集成到应用程序中,需要在应用程序的菜单中添加一个“Preferences…”菜单项。当调用该菜单项时,首先构造一个新的 PreferencesDialog 实例,然后初始化对话框的首选项数据副本,最后调用 execute() 方法显示对话框。如果 execute() 返回非零值,则从对话框的副本中提取修改后的应用程序设置并更新到模型中:

dialog = PreferencesDialog.new(self)
dialog.prefs[:first_name].value = user_name.first_name
dialog.prefs[:last_name].value = user_name.last_name
dialog.prefs[:street].value = user_address.street
dialog.prefs[:city].value = user_address.city
dialog.prefs[:state].value = user_address.state
dialog.prefs[:zip_code].value = user_address.zip_code
if dialog.execute != 0
  user_name.first_name = dialog.prefs[:first_name].value
  user_name.last_name = dialog.prefs[:last_name].value
  user_address.street = dialog.prefs[:street].value
  user_address.city = dialog.prefs[:city].value
  user_address.state = dialog.prefs[:state].value
  user_address.zip_code = dialog.prefs[:zip_code].value
end
未来展望

虽然我们已经学习了很多关于使用 FXRuby 创建用户界面的知识,但仍有许多更高级的方面等待探索。例如,FOX 支持基于 OpenGL 的 2D 和 3D 图形应用程序,你可以使用 FXGLViewer 小部件构建复杂的 3D 场景图,或者在需要更多控制场景呈现方式时使用更基本的 FXGLCanvas 小部件。

FOX 还提供了许多专用小部件,如拨号盘、微调器和滑块等。你可以查阅这些类的文档以了解其具体功能,并应用所学的技术来响应这些小部件的消息并将它们与数据目标关联起来。

开发 GUI 应用程序不仅仅是一项技术练习,设计一个直观且易于使用的 GUI 应用程序本身就是一个挑战。了解为什么做出某些选择可能比知道如何实现这些选择更重要。建议参考一些关于用户界面设计的优秀书籍,以提升设计能力。

最后,鼓励你加入 FOX 和 FXRuby 邮件列表,与其他软件开发人员交流经验、分享知识,帮助新手解决问题。

对话框的使用与自定义开发

5. 对话框使用总结与对比

为了更清晰地了解各种对话框的特点和使用场景,我们可以通过一个表格进行总结对比:
| 对话框类型 | 功能描述 | 使用示例代码 | 特点 |
| — | — | — | — |
| 颜色对话框( FXColorDialog ) | 让用户选择或修改颜色值 | ruby<br>dialog = FXColorDialog.new(self, "Choose Background Color")<br>dialog.rgba = FXRGB(255, 0, 0)<br>if dialog.execute != 0<br> self.backColor = dialog.rgba<br>end<br> | 有五个选项卡,支持多种颜色模型选择,左侧有颜色选择器,底部有预定义颜色样本 |
| 字体对话框( FXFontDialog ) | 让用户选择字体 | ruby<br>dialog = FXFontDialog.new(self, "Choose a Font")<br>dialog.fontSelection = button.font.fontDesc<br>if dialog.execute != 0<br> new_font = FXFont.new(app, dialog.fontSelection)<br> new_font.create<br> button.font = new_font<br>end<br> | 需要处理 FXFontDesc 对象,创建新字体后需调用 create() 方法 |
| 消息框( FXMessageBox ) | 向用户显示消息并获取简单反馈 | ruby<br>FXMessageBox.warning(<br> self,<br> MBOX_OK,<br> "Buyer Beware",<br> "All Sales are Final!"<br>)<br> ruby<br>answer = FXMessageBox.question(<br> self,<br> MBOX_YES_NO,<br> "Just one question...",<br> "Is it safe?"<br>)<br>if answer == MBOX_CLICKED_YES<br> ask_again()<br>end<br> | 简单,提供基本交互,使用类方法一次性构造和显示 |
| 自定义对话框( PreferencesDialog ) | 处理应用程序特定功能 | 一系列代码,包括子类化 FXDialogBox 、添加终止按钮、使用 FXTabBook 显示设置、使用 FXDataTarget 处理数据等 | 可根据需求定制,处理自定义数据类型和设置 |

6. 自定义对话框开发流程

下面通过 mermaid 格式的流程图来展示自定义对话框(如首选项对话框)的开发流程:

graph TD;
    A[子类化 FXDialogBox] --> B[添加终止按钮];
    B --> C[使用 FXTabBook 显示设置];
    C --> D[使用 FXDataTarget 处理数据];
    D --> E[集成到应用程序];
    E --> F[初始化对话框数据];
    F --> G[显示对话框并获取用户输入];
    G --> H{用户点击 OK?};
    H -- 是 --> I[提取修改后的数据更新到模型];
    H -- 否 --> J[忽略修改];
7. 高级功能与拓展

除了前面介绍的标准对话框和自定义对话框,还有一些高级功能值得探索。

7.1 OpenGL 图形支持

FOX 支持基于 OpenGL 的 2D 和 3D 图形应用程序。如果你想构建一个复杂的 3D 场景图,支持选择、旋转、缩放等功能,可以使用 FXGLViewer 小部件。以下是一个简单的使用思路示例:

# 假设 app 是应用程序实例
viewer = FXGLViewer.new(parent_widget, :opts => LAYOUT_FILL)
# 后续可以进行场景图的构建和设置

如果你需要更多地控制场景的呈现方式,可以使用更基本的 FXGLCanvas 小部件:

canvas = FXGLCanvas.new(parent_widget, :opts => LAYOUT_FILL)
# 进行 OpenGL 相关的绘制操作
7.2 专用小部件的使用

FOX 提供了许多专用小部件,如拨号盘、微调器和滑块等。这些小部件可以与其他小部件一起使用,丰富用户界面。例如,使用滑块来控制某个参数的值:

slider = FXSlider.new(parent_widget, :opts => SLIDER_HORIZONTAL|LAYOUT_FILL_X)
slider.connect(SEL_COMMAND) do |sender, sel, data|
  # 处理滑块值变化的逻辑
  value = slider.value
  # 进行相应的操作
end
8. 用户界面设计的重要性

开发一个 GUI 应用程序不仅仅是实现功能,设计一个直观且易于使用的界面至关重要。以下是一些设计界面时需要考虑的要点列表:
- 用户体验至上 :界面应该符合用户的操作习惯,让用户能够轻松找到他们需要的功能。
- 一致性 :保持界面元素的一致性,如颜色、字体、按钮样式等,让用户有熟悉的感觉。
- 反馈机制 :及时向用户反馈操作结果,让用户知道他们的操作是否成功。
- 简洁性 :避免界面过于复杂,只显示必要的信息和功能。

通过参考一些优秀的用户界面设计书籍,可以帮助我们更好地理解这些原则,并应用到实际的开发中。

9. 社区交流与学习

加入 FOX 和 FXRuby 邮件列表是一个很好的学习和交流途径。在这个社区中,你可以:
- 向其他开发者请教问题,获取帮助。
- 分享自己的开发经验和成果,帮助新手解决问题。
- 了解最新的技术动态和开发技巧。

通过与社区成员的互动,你可以不断提升自己的开发能力,为开发出更优秀的 GUI 应用程序打下坚实的基础。

总之,对话框的使用和自定义开发是 GUI 应用程序开发中的重要部分,结合高级功能和良好的用户界面设计,再加上社区的交流学习,能够让我们开发出更加出色的应用程序。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值